1616)
1717
1818from colocus .core import models
19- from colocus .core .constants import ANALYSIS_TYPES , GWAS , EQTL , MEQTL , METABQTL , PQTL
19+ from colocus .core .constants import ANALYSIS_TYPES
2020
2121
2222def parse_region (region ):
@@ -102,6 +102,92 @@ def filter_queryset(self, queryset):
102102 Q (** {f'{ field_name } ' : float ('-inf' )}) | Q (** {f'{ field_name } __isnull' : True })
103103 )
104104
105+ # Add conditional annotations for ordering
106+ # These are necessary because on a per-row basis, signals may be swapped depending on user preference
107+ # (e.g. analysis_priority), so we need to create consistent "primary" and "secondary" signal fields
108+ queryset = queryset .annotate (
109+ primary_signal_trait = Case (
110+ When (use_signal1_as_primary = True , then = F ('signal1__analysis__trait__uuid' )),
111+ default = F ('signal2__analysis__trait__uuid' )
112+ ),
113+ secondary_signal_trait = Case (
114+ When (use_signal1_as_primary = True , then = F ('signal2__analysis__trait__uuid' )),
115+ default = F ('signal1__analysis__trait__uuid' )
116+ ),
117+ primary_signal_chrom = Case (
118+ When (use_signal1_as_primary = True , then = F ('signal1__lead_variant__chrom' )),
119+ default = F ('signal2__lead_variant__chrom' )
120+ ),
121+ secondary_signal_chrom = Case (
122+ When (use_signal1_as_primary = True , then = F ('signal2__lead_variant__chrom' )),
123+ default = F ('signal1__lead_variant__chrom' )
124+ ),
125+ primary_signal_pos = Case (
126+ When (use_signal1_as_primary = True , then = F ('signal1__lead_variant__pos' )),
127+ default = F ('signal2__lead_variant__pos' )
128+ ),
129+ secondary_signal_pos = Case (
130+ When (use_signal1_as_primary = True , then = F ('signal2__lead_variant__pos' )),
131+ default = F ('signal1__lead_variant__pos' )
132+ ),
133+ primary_signal_logp = Case (
134+ When (use_signal1_as_primary = True , then = F ('signal1__neg_log_p' )),
135+ default = F ('signal2__neg_log_p' )
136+ ),
137+ secondary_signal_logp = Case (
138+ When (use_signal1_as_primary = True , then = F ('signal2__neg_log_p' )),
139+ default = F ('signal1__neg_log_p' )
140+ ),
141+ primary_signal_tissue = Case (
142+ When (use_signal1_as_primary = True , then = F ('signal1__analysis__tissue' )),
143+ default = F ('signal2__analysis__tissue' )
144+ ),
145+ secondary_signal_tissue = Case (
146+ When (use_signal1_as_primary = True , then = F ('signal2__analysis__tissue' )),
147+ default = F ('signal1__analysis__tissue' )
148+ ),
149+ primary_signal_cell_type = Case (
150+ When (use_signal1_as_primary = True , then = F ('signal1__analysis__cell_type' )),
151+ default = F ('signal2__analysis__cell_type' )
152+ ),
153+ secondary_signal_cell_type = Case (
154+ When (use_signal1_as_primary = True , then = F ('signal2__analysis__cell_type' )),
155+ default = F ('signal1__analysis__cell_type' )
156+ ),
157+ primary_signal_study = Case (
158+ When (use_signal1_as_primary = True , then = F ('signal1__analysis__study__uuid' )),
159+ default = F ('signal2__analysis__study__uuid' )
160+ ),
161+ secondary_signal_study = Case (
162+ When (use_signal1_as_primary = True , then = F ('signal2__analysis__study__uuid' )),
163+ default = F ('signal1__analysis__study__uuid' )
164+ ),
165+ primary_signal_gene_ens_id = Case (
166+ When (use_signal1_as_primary = True , then = F ('signal1__analysis__trait__gene__ens_id' )),
167+ default = F ('signal2__analysis__trait__gene__ens_id' )
168+ ),
169+ secondary_signal_gene_ens_id = Case (
170+ When (use_signal1_as_primary = True , then = F ('signal2__analysis__trait__gene__ens_id' )),
171+ default = F ('signal1__analysis__trait__gene__ens_id' )
172+ ),
173+ primary_signal_gene_symbol = Case (
174+ When (use_signal1_as_primary = True , then = F ('signal1__analysis__trait__gene__symbol' )),
175+ default = F ('signal2__analysis__trait__gene__symbol' )
176+ ),
177+ secondary_signal_gene_symbol = Case (
178+ When (use_signal1_as_primary = True , then = F ('signal2__analysis__trait__gene__symbol' )),
179+ default = F ('signal1__analysis__trait__gene__symbol' )
180+ ),
181+ primary_signal_exon_ens_id = Case (
182+ When (use_signal1_as_primary = True , then = F ('signal1__analysis__trait__exon__ens_id' )),
183+ default = F ('signal2__analysis__trait__exon__ens_id' )
184+ ),
185+ secondary_signal_exon_ens_id = Case (
186+ When (use_signal1_as_primary = True , then = F ('signal2__analysis__trait__exon__ens_id' )),
187+ default = F ('signal1__analysis__trait__exon__ens_id' )
188+ ),
189+ )
190+
105191 return super ().filter_queryset (queryset )
106192
107193 def create_query (self , field , value ):
@@ -228,27 +314,55 @@ def filter_region(self, queryset, name, value):
228314 if match :
229315 chrom , start_pos , end_pos = match
230316
231- pos_field = None
232- if "signal1" in name :
233- pos_field = "signal1__lead_variant__pos"
234- elif "signal2" in name :
235- pos_field = "signal2__lead_variant__pos"
236-
237- chrom_field = None
238317 if "signal1" in name :
239318 chrom_field = "signal1__lead_variant__chrom"
319+ pos_field = "signal1__lead_variant__pos"
320+ return queryset .filter (
321+ Q (** {
322+ "use_signal1_as_primary" : True ,
323+ f'{ chrom_field } ' : chrom ,
324+ f'{ pos_field } __gte' : start_pos ,
325+ f'{ pos_field } __lte' : end_pos }) |
326+ Q (** {
327+ "use_signal1_as_primary" : False ,
328+ f'{ chrom_field .replace ("signal1" , "signal2" )} ' : chrom ,
329+ f'{ pos_field .replace ("signal1" , "signal2" )} __gte' : start_pos ,
330+ f'{ pos_field .replace ("signal1" , "signal2" )} __lte' : end_pos })
331+ )
240332 elif "signal2" in name :
241333 chrom_field = "signal2__lead_variant__chrom"
242-
243- if chrom_field and pos_field :
334+ pos_field = "signal2__lead_variant__pos"
244335 return queryset .filter (
245- Q (** {f'{ chrom_field } ' : chrom })
246- & Q (** {f'{ pos_field } __gte' : start_pos })
247- & Q (** {f'{ pos_field } __lte' : end_pos })
336+ Q (** {
337+ "use_signal1_as_primary" : True ,
338+ f'{ chrom_field } ' : chrom ,
339+ f'{ pos_field } __gte' : start_pos ,
340+ f'{ pos_field } __lte' : end_pos }) |
341+ Q (** {
342+ "use_signal1_as_primary" : False ,
343+ f'{ chrom_field .replace ("signal2" , "signal1" )} ' : chrom ,
344+ f'{ pos_field .replace ("signal2" , "signal1" )} __gte' : start_pos ,
345+ f'{ pos_field .replace ("signal2" , "signal1" )} __lte' : end_pos })
346+ )
347+ else :
348+ return queryset .filter (
349+ Q (** {
350+ "signal1__lead_variant__chrom" : chrom ,
351+ "signal1__lead_variant__pos__gte" : start_pos ,
352+ "signal1__lead_variant__pos__lte" : end_pos }) |
353+ Q (** {
354+ "signal2__lead_variant__chrom" : chrom ,
355+ "signal2__lead_variant__pos__gte" : start_pos ,
356+ "signal2__lead_variant__pos__lte" : end_pos })
248357 )
249358
250359 return queryset
251360
361+ region = CharFilter (
362+ method = 'filter_region' ,
363+ label = "Only retrieve results (for signal1 or signal2) within a specified region given as chr:start-end"
364+ )
365+
252366 signal1_region = CharFilter (
253367 method = 'filter_region' ,
254368 label = "Only retrieve signal 1 results within a specified region given as chr:start-end" )
@@ -262,11 +376,24 @@ def filter_region(self, queryset, name, value):
262376 signal2_analysis = CharFilter (field_name = 'signal2__analysis__uuid' , lookup_expr = 'exact' ,
263377 label = "Signal 2 analysis UUID" )
264378
265- signal1_trait = CharFilter (field_name = 'signal1__analysis__trait__uuid ' , lookup_expr = 'exact' ,
379+ signal1_trait = CharFilter (method = 'filter_signal1_trait ' , lookup_expr = 'exact' ,
266380 label = "Signal 1 trait UUID" )
267- signal2_trait = CharFilter (field_name = 'signal2__analysis__trait__uuid' , lookup_expr = 'exact' ,
381+
382+ def filter_signal1_trait (self , queryset , name , value ):
383+ return queryset .filter (
384+ Q (use_signal1_as_primary = True , signal1__analysis__trait__uuid = value ) |
385+ Q (use_signal1_as_primary = False , signal2__analysis__trait__uuid = value )
386+ )
387+
388+ signal2_trait = CharFilter (method = 'filter_signal2_trait' , lookup_expr = 'exact' ,
268389 label = "Signal 2 trait UUID" )
269390
391+ def filter_signal2_trait (self , queryset , name , value ):
392+ return queryset .filter (
393+ Q (use_signal1_as_primary = True , signal2__analysis__trait__uuid = value ) |
394+ Q (use_signal1_as_primary = False , signal1__analysis__trait__uuid = value )
395+ )
396+
270397 signal1_min_logp = NumberFilter (field_name = 'signal1__neg_log_p' , lookup_expr = 'gte' ,
271398 label = "Minimum -log10 p-value for signal 1" )
272399 signal2_min_logp = NumberFilter (field_name = 'signal2__neg_log_p' , lookup_expr = 'gte' ,
@@ -284,26 +411,26 @@ def filter_region(self, queryset, name, value):
284411 ('r2' , 'r2' ),
285412 ('n_coloc_between_traits' , 'n_coloc_between_traits' ),
286413 * (f"logp_max_over_{ e [0 ].lower ()} " for e in ANALYSIS_TYPES ),
287- ('signal1__neg_log_p ' , 'signal1_logp' ),
288- ('signal2__neg_log_p ' , 'signal2_logp' ),
289- ('signal1__lead_variant__chrom ' , 'signal1_chrom' ),
290- ('signal1__lead_variant__pos ' , 'signal1_pos' ),
291- ('signal1__analysis__trait__uuid ' , 'signal1_trait' ),
292- ('signal2__analysis__trait__uuid ' , 'signal2_trait' ),
293- ('signal2__lead_variant__chrom ' , 'signal2_chrom' ),
294- ('signal2__lead_variant__pos ' , 'signal2_pos' ),
295- ('signal1__analysis__trait__gene__ens_id ' , 'signal1_gene_ens_id' ),
296- ('signal1__analysis__trait__gene__symbol ' , 'signal1_gene_symbol' ),
297- ('signal1__analysis__tissue ' , 'signal1_tissue' ),
298- ('signal1__analysis__cell_type ' , 'signal1_cell_type' ),
299- ('signal1__analysis__trait__exon__ens_id ' , 'signal1_exon_ens_id' ),
300- ('signal2__analysis__trait__gene__ens_id ' , 'signal2_gene_ens_id' ),
301- ('signal2__analysis__trait__gene__symbol ' , 'signal2_gene_symbol' ),
302- ('signal2__analysis__tissue ' , 'signal2_tissue' ),
303- ('signal2__analysis__cell_type ' , 'signal2_cell_type' ),
304- ('signal2__analysis__trait__exon__ens_id ' , 'signal2_exon_ens_id' ),
305- ('signal1__analysis__study__uuid ' , 'signal1_study' ),
306- ('signal2__analysis__study__uuid ' , 'signal2_study' )
414+ ('primary_signal_logp ' , 'signal1_logp' ),
415+ ('secondary_signal_logp ' , 'signal2_logp' ),
416+ ('primary_signal_chrom ' , 'signal1_chrom' ),
417+ ('primary_signal_pos ' , 'signal1_pos' ),
418+ ('primary_signal_trait ' , 'signal1_trait' ),
419+ ('secondary_signal_trait ' , 'signal2_trait' ),
420+ ('secondary_signal_chrom ' , 'signal2_chrom' ),
421+ ('secondary_signal_pos ' , 'signal2_pos' ),
422+ ('primary_signal_gene_ens_id ' , 'signal1_gene_ens_id' ),
423+ ('primary_signal_gene_symbol ' , 'signal1_gene_symbol' ),
424+ ('primary_signal_tissue ' , 'signal1_tissue' ),
425+ ('primary_signal_cell_type ' , 'signal1_cell_type' ),
426+ ('primary_signal_exon_ens_id ' , 'signal1_exon_ens_id' ),
427+ ('secondary_signal_gene_ens_id ' , 'signal2_gene_ens_id' ),
428+ ('secondary_signal_gene_symbol ' , 'signal2_gene_symbol' ),
429+ ('secondary_signal_tissue ' , 'signal2_tissue' ),
430+ ('secondary_signal_cell_type ' , 'signal2_cell_type' ),
431+ ('secondary_signal_exon_ens_id ' , 'signal2_exon_ens_id' ),
432+ ('primary_signal_study ' , 'signal1_study' ),
433+ ('secondary_signal_study ' , 'signal2_study' )
307434 )
308435 )
309436
0 commit comments