diff --git a/chord_metadata_service/patients/api_views.py b/chord_metadata_service/patients/api_views.py index fc11c0a7d..d45a1ad90 100644 --- a/chord_metadata_service/patients/api_views.py +++ b/chord_metadata_service/patients/api_views.py @@ -5,7 +5,7 @@ from .models import Individual from .filters import IndividualFilter from chord_metadata_service.phenopackets.api_views import BIOSAMPLE_PREFETCH, PHENOPACKET_PREFETCH -from chord_metadata_service.restapi.api_renderers import FHIRRenderer, PhenopacketsRenderer +from chord_metadata_service.restapi.api_renderers import FHIRRenderer, PhenopacketsRenderer, IndividualCSVRenderer from chord_metadata_service.restapi.pagination import LargeResultsSetPagination @@ -24,7 +24,8 @@ class IndividualViewSet(viewsets.ModelViewSet): ).order_by("id") serializer_class = IndividualSerializer pagination_class = LargeResultsSetPagination - renderer_classes = (*api_settings.DEFAULT_RENDERER_CLASSES, FHIRRenderer, PhenopacketsRenderer) + renderer_classes = (*api_settings.DEFAULT_RENDERER_CLASSES, FHIRRenderer, + PhenopacketsRenderer, IndividualCSVRenderer) filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] filter_class = IndividualFilter ordering_fields = ["id"] diff --git a/chord_metadata_service/restapi/api_renderers.py b/chord_metadata_service/restapi/api_renderers.py index 97a7efe97..80769f096 100644 --- a/chord_metadata_service/restapi/api_renderers.py +++ b/chord_metadata_service/restapi/api_renderers.py @@ -1,8 +1,11 @@ import json +import csv from djangorestframework_camel_case.render import CamelCaseJSONRenderer +from rest_framework.renderers import JSONRenderer from rdflib import Graph from rdflib.plugin import register, Serializer -from rest_framework.renderers import JSONRenderer +from django.http import HttpResponse + from uuid import UUID from .jsonld_utils import dataset_to_jsonld @@ -79,3 +82,60 @@ def render(self, data, media_type=None, renderer_context=None): g = Graph().parse(data=json.dumps(ld_context_data, cls=UUIDEncoder), format='json-ld') rdf_data = g.serialize(format='pretty-xml') return rdf_data + + +class IndividualCSVRenderer(JSONRenderer): + media_type = 'text/csv' + format = 'csv' + + def render(self, data, media_type=None, renderer_context=None): + if 'results' in data: + individuals = [] + for individual in data['results']: + ind_obj = { + 'id': individual['id'], + 'sex': individual.get('sex', None), + 'date_of_birth': individual.get('date_of_birth', None), + 'taxonomy': None, + 'karyotypic_sex': individual['karyotypic_sex'], + 'race': individual.get('race', None), + 'ethnicity': individual.get('ethnicity', None), + 'age': None, + 'diseases': None, + 'created': individual['created'], + 'updated': individual['updated'] + } + if 'taxonomy' in individual: + ind_obj['taxonomy'] = individual['taxonomy'].get('label', None) + if 'age' in individual: + if 'age' in individual['age']: + ind_obj['age'] = individual['age'].get('age', None) + elif 'start' and 'end' in individual['age']: + ind_obj['age'] = str( + individual['age']['start'].get('age', "NA") + + ' - ' + + individual['age']['end'].get('age', "NA") + ) + else: + ind_obj['age'] = None + if 'phenopackets' in individual: + all_diseases = [] + for phenopacket in individual['phenopackets']: + if 'diseases' in phenopacket: + # use ; because some disease terms might contain , in their label + single_phenopacket_diseases = '; '.join( + [d['term']['label'] for d in phenopacket['diseases']] + ) + all_diseases.append(single_phenopacket_diseases) + if all_diseases: + ind_obj['diseases'] = '; '.join(all_diseases) + individuals.append(ind_obj) + columns = individuals[0].keys() + # remove underscore and capitalize column names + headers = {key: key.replace('_', ' ').capitalize() for key in individuals[0].keys()} + response = HttpResponse(content_type='text/csv') + response['Content-Disposition'] = "attachment; filename='export.csv'" + dict_writer = csv.DictWriter(response, fieldnames=columns) + dict_writer.writerow(headers) + dict_writer.writerows(individuals) + return response