Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
3 changes: 2 additions & 1 deletion .env_template
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ DJANGO_ALLOWED_HOSTS=['localhost','127.0.0.1','0.0.0.0']
DJANGO_LOGLEVEL='WARNING'
NPD_PROJECT_NAME='[Project Name]'
NPD_READONLY_PASSWORD=''
NPD_LOAD_TESTER_PASSWORD=''
CORS_TRUSTED_ORIGINS=['http://localhost','http://127.0.0.1','http://0.0.0.0']
NPD_LOAD_TESTER_PASSWORD=''
9 changes: 5 additions & 4 deletions backend/npdfhir/filters/endpoint_filter_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ class EndpointFilterSet(filters.FilterSet):

status = filters.CharFilter(method="filter_status", help_text="Filter by status")

organization = filters.CharFilter(
method="filter_organization", help_text="Filter by organization"
)
# We don't have a concept of endpoint organization at the moment
# organization = filters.CharFilter(
# method="filter_organization", help_text="Filter by organization"
# )

class Meta:
model = EndpointInstance
fields = ["name", "connection_type", "payload_type", "status", "organization"]
fields = ["name", "connection_type", "payload_type", "status"]

def filter_status(self, queryset, name, value):
# needs to be implemented
Expand Down
62 changes: 27 additions & 35 deletions backend/npdfhir/filters/practitioner_filter_set.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.contrib.postgres.search import SearchVector
from django.contrib.postgres.search import SearchQuery
from django.db.models import Q
from django_filters import rest_framework as filters

Expand All @@ -8,24 +8,31 @@


class PractitionerFilterSet(filters.FilterSet):
practitioner_table = None

identifier = filters.CharFilter(
method="filter_identifier",
help_text="Filter by identifier (NPI or other). Format: value or system|value",
)

name = filters.CharFilter(
method="filter_name", help_text="Filter by practitioner name (first, last, or full name)"
method="filter_practitioner_name",
help_text="Filter by practitioner name (first, middle, last, or full name). Name filter accepts websearch syntax.",
)

gender = filters.ChoiceFilter(
method="filter_gender", choices=genderMapping.to_choices(), help_text="Filter by gender"
)

practitioner_type = filters.CharFilter(
method="filter_practitioner_type", help_text="Filter by practitioner type/taxonomy"
method="filter_practitioner_type",
help_text="Filter by practitioner type. Practitioner type filter accepts websearch syntax.",
)

address = filters.CharFilter(method="filter_address", help_text="Filter by any part of address")
address = filters.CharFilter(
method="filter_address",
help_text="Filter by any part of address. Address filter accepts websearch syntax.",
)

address_city = filters.CharFilter(method="filter_address_city", help_text="Filter by city name")

Expand Down Expand Up @@ -83,47 +90,32 @@ def filter_identifier(self, queryset, name, value):

return queryset.filter(queries).distinct()

def filter_name(self, queryset, name, value):
return queryset.annotate(
search=SearchVector(
"individual__individualtoname__first_name",
"individual__individualtoname__last_name",
"individual__individualtoname__middle_name",
)
).filter(search=value)
def filter_practitioner_name(self, queryset, name, value):
query = SearchQuery(f"{value.upper()}", search_type="websearch")
return queryset.filter(individual__individualtoname__search_vector=query).distinct()

def filter_practitioner_type(self, queryset, name, value):
return queryset.annotate(
search=SearchVector("providertotaxonomy__nucc_code__display_name")
).filter(search=value)
query = SearchQuery(value, search_type="websearch")
return queryset.filter(providertotaxonomy__nucc_code__search_vector=query)

def filter_address(self, queryset, name, value):
return queryset.annotate(
search=SearchVector(
"individual__individualtoaddress__address__address_us__delivery_line_1",
"individual__individualtoaddress__address__address_us__delivery_line_2",
"individual__individualtoaddress__address__address_us__city_name",
"individual__individualtoaddress__address__address_us__state_code__abbreviation",
"individual__individualtoaddress__address__address_us__zipcode",
)
).filter(search=value)
query = SearchQuery(value, search_type="websearch")
return queryset.filter(
individual__individualtoaddress__address__address_us__search_vector=query
)

def filter_address_city(self, queryset, name, value):
return queryset.annotate(
search=SearchVector("individual__individualtoaddress__address__address_us__city_name")
).filter(search=value)
return queryset.filter(
individual__individualtoaddress__address__address_us__city_name=value
)

def filter_address_state(self, queryset, name, value):
return queryset.annotate(
search=SearchVector(
"individual__individualtoaddress__address__address_us__state_code__abbreviation"
)
).filter(search=value)
return queryset.filter(
individual__individualtoaddress__address__address_us__state_code__abbreviation=value
)

def filter_address_postalcode(self, queryset, name, value):
return queryset.annotate(
search=SearchVector("individual__individualtoaddress__address__address_us__zipcode")
).filter(search=value)
return queryset.filter(individual__individualtoaddress__address__address_us__zipcode=value)

def filter_address_use(self, queryset, name, value):
if value in addressUseMapping.keys():
Expand Down
83 changes: 37 additions & 46 deletions backend/npdfhir/filters/practitioner_role_filter_set.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import re
from django.contrib.gis.geos import Point
from django.contrib.gis.measure import D
from django.contrib.postgres.search import SearchVector
from django.contrib.postgres.search import SearchVector, SearchQuery
from django.db.models import Q
from django_filters import rest_framework as filters

Expand All @@ -11,9 +11,11 @@


class PractitionerRoleFilterSet(filters.FilterSet):
practitioner_table = "provider_to_organization"
# practitioner_name = PractitionerFilterSet.name
practitioner_name = filters.CharFilter(
method="filter_practitioner_name",
help_text="Filter by practitioner name (first, last, or full name)",
help_text="Filter by practitioner name (first, middle, last, or full name). Name filter accepts websearch syntax.",
)

practitioner_gender = filters.ChoiceFilter(
Expand Down Expand Up @@ -66,30 +68,30 @@ class PractitionerRoleFilterSet(filters.FilterSet):
endpoint_status = filters.CharFilter(
method="filter_endpoint_status", help_text="Filter providers by endpoint status"
)

endpoint_organization_id = filters.UUIDFilter(
method="filter_endpoint_organization_id",
help_text="Filter by the UUID of the organization associated with endpoints",
)

endpoint_organization_name = filters.CharFilter(
method="filter_endpoint_organization_name",
help_text="Filter by the name of the organization associated with endpoints",
)
# We don't have a concept of endpoint organizations at the moment
# endpoint_organization_id = filters.UUIDFilter(
# method="filter_endpoint_organization_id",
# help_text="Filter by the UUID of the organization associated with endpoints",
# )
#
# endpoint_organization_name = filters.CharFilter(
# method="filter_endpoint_organization_name",
# help_text="Filter by the name of the organization associated with endpoints",
# )

location_address = filters.CharFilter(
method="filter_address", help_text="Filter by the location address"
)

location_city = filters.CharFilter(
location_address_city = filters.CharFilter(
method="filter_address_city", help_text="Filter by the location city"
)

location_state = filters.CharFilter(
location_address_state = filters.CharFilter(
method="filter_address_state", help_text="Filter by the location state"
)

location_zip_code = filters.CharFilter(
location_address_postalcode = filters.CharFilter(
method="filter_address_postalcode", help_text="Filter by the location postal code"
)

Expand All @@ -108,22 +110,17 @@ class Meta:
"specialty",
"endpoint_connection_type",
"endpoint_payload_type",
"endpoint_organization_id",
"endpoint_organization_name",
"location_address",
"location_city",
"location_state",
"location_zip_code",
"location_address_city",
"location_address_state",
"location_address_postalcode",
]

def filter_practitioner_name(self, queryset, name, value):
return queryset.annotate(
search=SearchVector(
"provider_to_organization__individual__individual__individualtoname__first_name",
"provider_to_organization__individual__individual__individualtoname__last_name",
"provider_to_organization__individual__individual__individualtoname__middle_name",
)
).filter(search=value)
query = SearchQuery(f"{value.upper()}", search_type="websearch")
return queryset.filter(
provider_to_organization__individual__individual__individualtoname__search_vector=query
).distinct()

def filter_practitioner_gender(self, queryset, name, value):
if value in genderMapping.keys():
Expand All @@ -132,9 +129,10 @@ def filter_practitioner_gender(self, queryset, name, value):
return queryset

def filter_practitioner_type(self, queryset, name, value):
query = SearchQuery(value, search_type="websearch")
return queryset.filter(
provider_to_organization__individual__providertotaxonomy__nucc_code__code=value
).distinct()
provider_to_organization__individual__providertotaxonomy__nucc_code__search_vector=query
)

def filter_organization_name(self, queryset, name, value):
return queryset.annotate(
Expand Down Expand Up @@ -169,7 +167,7 @@ def filter_distance(self, queryset, name, value):
def filter_organization_type(self, queryset, name, value):
return queryset.filter(
Q(
provider_to_organization__organization__clinicalorganization__organizationtotaxonomy__nucc_code__code=value
provider_to_organization__organization__clinicalorganization__organizationtotaxonomy__nucc_code__display_name=value
)
).distinct()

Expand Down Expand Up @@ -199,18 +197,18 @@ def filter_specialty(self, queryset, name, value):
return queryset.filter(Q(specialty_id__iexact=value)).distinct()

def filter_connection_type(self, queryset, name, value):
return queryset.annotate(
search=SearchVector("other_endpoint__endpoint_instance__endpoint_connection_type__id")
).filter(search=value)
return queryset.filter(
location__locationtoendpointinstance__endpoint_instance__endpoint_connection_type_id=value
)

def filter_endpoint_status(self, queryset, name, value):
return queryset.annotate(
search=SearchVector("location__locationtoendpointinstance__endpoint_instance__status")
).filter(search=value)
return queryset.filter(
location__locationtoendpointinstance__endpoint_instance__status=value
)

def filter_payload_type(self, queryset, name, value):
return queryset.filter(
location__locationtoendpointinstance__endpoint_instance__endpointinstancetopayload__payload_type__id=value
location__locationtoendpointinstance__endpoint_instance__endpointinstancetopayload__payload_type_id=value
).distinct()

def filter_endpoint_organization_id(self, queryset, name, value):
Expand All @@ -222,15 +220,8 @@ def filter_endpoint_organization_name(self, queryset, name, value):
return queryset.filter(location__organization__organizationtoname__name=value)

def filter_address(self, queryset, name, value):
return queryset.annotate(
search=SearchVector(
"location__address__address_us__delivery_line_1",
"location__address__address_us__delivery_line_2",
"location__address__address_us__city_name",
"location__address__address_us__state_code__abbreviation",
"location__address__address_us__zipcode",
)
).filter(search=value)
query = SearchQuery(value, search_type="websearch")
return queryset.filter(location__address__address_us__search_vector=query)

def filter_address_city(self, queryset, name, value):
return queryset.annotate(
Expand Down
4 changes: 2 additions & 2 deletions backend/npdfhir/management/commands/seedsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.db import IntegrityError
from faker import Faker

from npdfhir.tests.fixtures.endpoint import create_endpoint
from npdfhir.tests.fixtures.endpoint import create_endpoint_instance
from npdfhir.tests.fixtures.organization import create_organization
from npdfhir.tests.fixtures.practitioner import create_practitioner

Expand Down Expand Up @@ -124,7 +124,7 @@ def handle(self, *args, **options):
self.stdout.write("(organization with other_id 1234567893 already exists)")

if organization:
endpoint = create_endpoint(organization=organization)
endpoint = create_endpoint_instance(organization=organization)
self.stdout.write(f"created Endpoint: {self.to_json(id=endpoint.id)}")

self.generate_sample_organizations(25)
Expand Down
27 changes: 23 additions & 4 deletions backend/npdfhir/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.db import models
from django.contrib.gis.db import models as geomodels
from django.contrib.postgres.search import SearchVectorField


class Address(models.Model):
Expand Down Expand Up @@ -126,11 +127,17 @@ class AddressUs(models.Model):
suitelink_match = models.CharField(max_length=5, blank=True, null=True)
enhanced_match = models.CharField(max_length=64, blank=True, null=True)
geolocation = geomodels.PointField(srid=4326)
search_vector = SearchVectorField(blank=True, null=True)

class Meta:
managed = False
db_table = "address_us"

def _do_insert(self, manager, using, fields, update_pk, raw):
# Prevents the model from attempting to insert values into the generated search_vector field
fields = [f for f in fields if f.attname != "search_vector"]
return super()._do_insert(manager, using, fields, update_pk, raw)


class ClinicalOrganization(models.Model):
organization = models.OneToOneField("Organization", models.DO_NOTHING, blank=True, null=True)
Expand Down Expand Up @@ -388,20 +395,26 @@ class Meta:

class IndividualToName(models.Model):
pk = models.CompositePrimaryKey("individual_id", "first_name", "last_name", "name_use_id")
individual = models.ForeignKey(Individual, models.DO_NOTHING)
individual = models.ForeignKey(Individual, models.DO_NOTHING, db_index=True)
prefix = models.CharField(max_length=10, blank=True, null=True)
first_name = models.CharField(max_length=50)
first_name = models.CharField(max_length=50, db_index=True)
middle_name = models.CharField(max_length=50, blank=True, null=True)
last_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200, db_index=True)
start_date = models.DateField(blank=True, null=True)
end_date = models.DateField(blank=True, null=True)
name_use = models.ForeignKey(FhirNameUse, models.DO_NOTHING)
suffix = models.CharField(max_length=10, blank=True, null=True)
search_vector = SearchVectorField(blank=True, null=True)

class Meta:
managed = False
db_table = "individual_to_name"

def _do_insert(self, manager, using, fields, update_pk, raw):
# Prevents the model from attempting to insert values into the generated search_vector field
fields = [f for f in fields if f.attname != "search_vector"]
return super()._do_insert(manager, using, fields, update_pk, raw)


class IndividualToPhone(models.Model):
individual = models.ForeignKey(Individual, models.DO_NOTHING)
Expand Down Expand Up @@ -505,11 +518,17 @@ class Nucc(models.Model):
notes = models.TextField(blank=True, null=True)
certifying_board_name = models.TextField(blank=True, null=True)
certifying_board_url = models.TextField(blank=True, null=True)
search_vector = SearchVectorField(blank=True, null=True)

class Meta:
managed = False
db_table = "nucc"

def _do_insert(self, manager, using, fields, update_pk, raw):
# Prevents the model from attempting to insert values into the generated search_vector field
fields = [f for f in fields if f.attname != "search_vector"]
return super()._do_insert(manager, using, fields, update_pk, raw)


class NuccClassification(models.Model):
nucc_code = models.ForeignKey(
Expand Down Expand Up @@ -644,7 +663,7 @@ class Meta:


class Provider(models.Model):
npi = models.OneToOneField(Npi, models.DO_NOTHING, db_column="npi")
npi = models.OneToOneField(Npi, models.DO_NOTHING, db_column="npi", db_index=True)
individual = models.OneToOneField(Individual, models.DO_NOTHING, primary_key=True)

class Meta:
Expand Down
Loading