Coverage for rest_api/viewsets_database.py: 27%
63 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-10 03:22 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-10 03:22 +0000
1from django.apps import apps
2from django.db import connection
3from rest_framework import status
4from rest_framework import viewsets
5from rest_framework.decorators import action
6from rest_framework.response import Response
8from . import models
9from .viewsets_sample import SampleFilterMixin
10from .viewsets_statistics_and_plots import SampleViewSetPlots
11from .viewsets_statistics_and_plots import SampleViewSetStatistics
14class DatabaseInfoView(
15 viewsets.GenericViewSet,
16 SampleFilterMixin,
17):
18 @action(detail=False, methods=["get"])
19 def get_database_tables_status(self, request, *args, **kwargs):
20 """
21 Checks if all required database tables are created and connected with Django.
22 Returns True if all are ready, otherwise False.
23 """
24 # List of all tables from model
25 # this also include django tables (e.g., auth_user)
26 expected_tables = {model._meta.db_table for model in apps.get_models()}
28 # sonar_db = [
29 # "sequence", "alignment",
30 # "alignment2mutation", "annotation_type",
31 # "replicon", "gene",
32 # "gene_segment", "lineage",
33 # "reference", "property",
34 # "sample", "sample2property",
35 # "mutation", "mutation2annotation",
36 # "processing_job", "file_processing",
37 # "import_log",
38 # ]
40 # Retrieve all actual tables from the database
41 with connection.cursor() as cursor:
42 cursor.execute(
43 "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';"
44 )
45 actual_tables = {row[0] for row in cursor.fetchall()}
47 # Check for missing tables
48 missing_tables = set(expected_tables) - actual_tables
50 if missing_tables:
51 return Response(
52 data={"status": False, "missing_tables": list(missing_tables)},
53 status=status.HTTP_200_OK,
54 )
56 return Response(data={"status": True}, status=status.HTTP_200_OK)
58 @action(detail=False, methods=["get"]) # detail=False means it's a list action
59 def get_database_info(self, request, *args, **kwargs):
60 result = {}
61 queryset = self.get_filtered_queryset(request)
62 statistics = SampleViewSetStatistics().get_statistics()
63 total_samples = statistics["samples_total"]
64 metadata_coverage = SampleViewSetPlots().get_metadata_coverage(queryset)
65 # Add percentages
66 for key, count in metadata_coverage.items():
67 percentage = (count / total_samples) * 100 if total_samples > 0 else 0
68 metadata_coverage[key] = f"{count} ({percentage:.2f}%)"
70 result["metadata_coverage"] = metadata_coverage
71 result.update(
72 {
73 "samples_total": total_samples,
74 "earliest_sampling_date": statistics["first_sample_date"],
75 "latest_sampling_date": statistics["latest_sample_date"],
76 }
77 )
78 # Earliest and Latest Genome Import
79 earliest_genome_import = models.Sample.objects.order_by(
80 "init_upload_date"
81 ).first()
82 latest_genome_import = models.Sample.objects.order_by(
83 "-init_upload_date"
84 ).first()
86 result["earliest_genome_import"] = (
87 earliest_genome_import.init_upload_date if earliest_genome_import else None
88 )
89 result["latest_genome_import"] = (
90 latest_genome_import.init_upload_date if latest_genome_import else None
91 )
92 # Reference Genomes with organism-specific statistics
93 result["reference_genomes"] = {}
94 for reference in models.Reference.objects.all():
95 organism = reference.organism
96 if organism not in result["reference_genomes"]:
97 result["reference_genomes"][organism] = {}
98 replicons = models.Replicon.objects.filter(
99 description__isnull=False, reference__accession=reference.accession
100 )
101 result["reference_genomes"][organism]["replicons"] = [
102 f"{replicon.accession} {replicon.description}" for replicon in replicons
103 ]
104 result["reference_genomes"][organism]["reference_length"] = [
105 reference_replicon.length for reference_replicon in replicons
106 ]
107 # Annotated Proteins
108 annotated_proteins = models.Gene.objects.filter(
109 cds__gene__symbol__isnull=False,
110 cds__gene__replicon__reference__accession=reference.accession,
111 ).values_list("symbol", flat=True)
112 result["reference_genomes"][organism]["annotated_proteins"] = ", ".join(
113 sorted(set(annotated_proteins))
114 )
116 # Unique Sequences for this organism
117 unique_sequences = (
118 models.Sequence.objects.filter(
119 alignments__replicon__reference__accession=reference.accession
120 )
121 .distinct()
122 .count()
123 )
124 result["reference_genomes"][organism]["unique_sequences"] = unique_sequences
126 # Total Genomes (alignments) for this organism
127 total_genomes = models.Alignment.objects.filter(
128 replicon__reference__accession=reference.accession
129 ).count()
130 result["reference_genomes"][organism]["genomes"] = total_genomes
131 print(result["reference_genomes"])
132 result["database_size"] = self.get_database_size()
133 result["database_version"] = self.get_database_version()
134 return Response(data={"detail": result}, status=status.HTTP_200_OK)
136 def get_database_size(self):
138 with connection.cursor() as cursor:
139 cursor.execute(
140 """
141 SELECT pg_size_pretty(pg_database_size(current_database()));
142 """
143 )
144 return cursor.fetchone()[0]
146 def get_database_version(self):
147 with connection.cursor() as cursor:
148 cursor.execute("SELECT version();")
149 return cursor.fetchone()[0]