Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix display and session of genomic interval and text mining input + Revise code for clearing dcc Store data #93

Merged
merged 8 commits into from
Aug 31, 2023
22 changes: 11 additions & 11 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,6 @@

dash.page_container,

# Do NOT place inside session-container.
# Otherwise, the input field for genomic interval will be cleared when submitted.
dcc.Store(
id='homepage-genomic-intervals-saved-input',
storage_type='session'
),

# Session storage
html.Div(
id='session-container',
Expand All @@ -85,6 +78,11 @@
storage_type='session'
),

dcc.Store(
id='homepage-genomic-intervals-saved-input',
storage_type='session'
),

dcc.Store(
id='homepage-genomic-intervals-submitted-input',
storage_type='session'
Expand All @@ -95,6 +93,8 @@
storage_type='session'
),



# ==========
# Lift-over
# ==========
Expand Down Expand Up @@ -208,16 +208,16 @@
id='coexpression-is-submitted',
storage_type='session'
),

# ==============================
# Regulatory Feature Enrichment
# ==============================

dcc.Store(
id='tfbs-saved-input',
storage_type='session'
),

# ==============================
# Regulatory Feature Enrichment
# ==============================

dcc.Store(
id='tfbs-submitted-input',
storage_type='session'
Expand Down
6 changes: 2 additions & 4 deletions callbacks/coexpression/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@

import pandas as pd
import networkx as nx
from scipy.stats import fisher_exact
import statsmodels.stats.multitest as sm
from scipy.stats import fisher_exact, false_discovery_control

from collections import namedtuple

Expand Down Expand Up @@ -219,8 +218,7 @@ def do_module_enrichment_analysis(implicated_gene_ids, genomic_intervals, addl_g
# Add 1 since user-facing module number is one-based
p_values_indices.append(idx + 1)

_, adj_p_values, _, _ = sm.multipletests(
p_values, method='fdr_bh')
adj_p_values = false_discovery_control(p_values, method='bh')
significant_adj_p_values = [(p_values_indices[idx], adj_p_value) for idx, adj_p_value in enumerate(
adj_p_values) if adj_p_value < const.P_VALUE_CUTOFF]
significant_adj_p_values.sort(key=lambda x: x[1])
Expand Down
38 changes: 19 additions & 19 deletions callbacks/homepage/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,25 @@ def display_specific_analysis_page(nav_className, analysis_nav_id, analysis_layo
State('homepage-genomic-intervals', 'value'),
Input('homepage-submit', 'n_clicks'),
Input('homepage-genomic-intervals', 'n_submit'),
State('session-container', 'children'),
Input('homepage-reset', 'n_clicks'),
Input('homepage-clear-cache', 'n_clicks'),
prevent_initial_call=True
)
def parse_input(nb_intervals_str, n_clicks, dccStore_children, *_):
def parse_input(nb_intervals_str, n_clicks, n_submit, dccStore_children, *_):
if 'homepage-clear-cache' == ctx.triggered_id:
clear_cache_folder()

if 'homepage-reset' == ctx.triggered_id:
# clear data for items in dcc.Store found in session-container
dccStore_children = get_cleared_dccStore_data(dccStore_children)
dccStore_children = get_cleared_dccStore_data_excluding_some_data(dccStore_children)

return dccStore_children, None, {'display': 'none'}, False, ''

if 'homepage-submit' == ctx.triggered_id and n_clicks >= 1:
if n_submit >= 1 or ('homepage-submit' == ctx.triggered_id and n_clicks >= 1):
if nb_intervals_str:
intervals = lift_over_util.get_genomic_intervals_from_input(
nb_intervals_str)
Expand All @@ -85,8 +86,8 @@ def parse_input(nb_intervals_str, n_clicks, dccStore_children, *_):
{'display': 'block'}, False, nb_intervals_str
else:
# clear data for items in dcc.Store found in session-container
dccStore_children = get_cleared_dccStore_data(
dccStore_children)
dccStore_children = get_cleared_dccStore_data_excluding_some_data(
dccStore_children, 'homepage-genomic-intervals-saved-input')

browse_loci_util.write_igv_tracks_to_file(nb_intervals_str)

Expand Down Expand Up @@ -120,27 +121,26 @@ def get_nipponbare_gene_ids(nb_intervals_str, homepage_is_submitted):
@app.callback(
Output('homepage-genomic-intervals-saved-input',
'data', allow_duplicate=True),
State('homepage-genomic-intervals', 'value'),
Input({'type': 'example-genomic-interval',
'description': ALL}, 'n_clicks'),
Input('homepage-reset', 'n_clicks'),
prevent_initial_call=True
)
def set_input_fields(genomic_intervals, example_genomic_interval_n_clicks, *_):
if all(val == 0 for val in example_genomic_interval_n_clicks):
return ''

if ctx.triggered_id:
if 'homepage-reset' == ctx.triggered_id:
return None

if 'homepage-genomic-intervals' == ctx.triggered_id:
return genomic_intervals

def set_input_fields_with_preset_input(example_genomic_interval_n_clicks):
if ctx.triggered_id and not all(val == 0 for val in example_genomic_interval_n_clicks):
return get_example_genomic_interval(ctx.triggered_id['description'])

raise PreventUpdate


@app.callback(
Output('homepage-genomic-intervals-saved-input',
'data', allow_duplicate=True),
Input('homepage-genomic-intervals', 'value'),
prevent_initial_call=True
)
def set_input_fields(genomic_intervals):
return genomic_intervals


@app.callback(
Output('homepage-results-container', 'style'),
Expand Down
8 changes: 6 additions & 2 deletions callbacks/homepage/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ def clear_cache_folder():
if os.path.exists(const.TEMP):
shutil.rmtree(const.TEMP, ignore_errors=True)

def get_cleared_dccStore_data(dccStore_children):
def get_cleared_dccStore_data_excluding_some_data(dccStore_children, *arg):
for i in range(len(dccStore_children)):
dccStore_children[i]['props']['data'] = ''
dccStore_ID = dccStore_children[i]['props']['id']

if not dccStore_ID in arg:
dccStore_children[i]['props']['data'] = ''

return dccStore_children

def get_example_genomic_interval(description):
Expand Down
24 changes: 17 additions & 7 deletions callbacks/lift_over/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,8 @@ def display_gene_tables(nb_intervals_str, active_tab, filter_rice_variants, othe
if active_tab == get_tab_id('All Genes'):
all_genes_raw = get_all_genes(other_refs, nb_intervals)

all_genes_raw['OGI'] = get_rgi_orthogroup_link(
mask = (all_genes_raw['OGI'] != NULL_PLACEHOLDER)
all_genes_raw.loc[mask, 'OGI'] = get_rgi_orthogroup_link(
all_genes_raw, 'OGI')
if 'Nipponbare' in all_genes_raw.columns:
mask = (all_genes_raw['Nipponbare'] != NULL_PLACEHOLDER)
Expand Down Expand Up @@ -336,13 +337,16 @@ def display_gene_tables(nb_intervals_str, active_tab, filter_rice_variants, othe
mask = (common_genes_raw['OGI'] != NULL_PLACEHOLDER)
common_genes_raw.loc[mask, 'OGI'] = get_rgi_orthogroup_link(
common_genes_raw, 'OGI')

if 'Nipponbare' in common_genes_raw.columns:
common_genes_raw['Nipponbare'] = get_rgi_genecard_link(
mask = (common_genes_raw['Nipponbare'] != NULL_PLACEHOLDER)
common_genes_raw.loc[mask, 'Nipponbare'] = get_rgi_genecard_link(
common_genes_raw, 'Nipponbare')

for cultivar in other_ref_genomes:
if cultivar in common_genes_raw.columns:
common_genes_raw[cultivar] = get_rgi_genecard_link(
mask = (common_genes_raw[cultivar] != NULL_PLACEHOLDER)
common_genes_raw.loc[mask, cultivar] = get_rgi_genecard_link(
common_genes_raw, cultivar)

common_genes = common_genes_raw.to_dict('records')
Expand All @@ -357,9 +361,12 @@ def display_gene_tables(nb_intervals_str, active_tab, filter_rice_variants, othe
nb_intervals)[0].drop(
['Chromosome', 'Start', 'End', 'Strand'], axis=1)

genes_from_Nb_raw['OGI'] = get_rgi_orthogroup_link(
mask = (genes_from_Nb_raw['OGI'] != NULL_PLACEHOLDER)
genes_from_Nb_raw.loc[mask, 'OGI'] = get_rgi_orthogroup_link(
genes_from_Nb_raw, 'OGI')
genes_from_Nb_raw['Name'] = get_rgi_genecard_link(

mask = (genes_from_Nb_raw['Name'] != NULL_PLACEHOLDER)
genes_from_Nb_raw.loc[mask, 'Name'] = get_rgi_genecard_link(
genes_from_Nb_raw, 'Name')

genes_from_Nb = genes_from_Nb_raw.to_dict('records')
Expand All @@ -376,9 +383,12 @@ def display_gene_tables(nb_intervals_str, active_tab, filter_rice_variants, othe
genes_from_other_ref_raw = get_unique_genes_in_other_ref(
other_ref, nb_intervals)

genes_from_other_ref_raw['OGI'] = get_rgi_orthogroup_link(
mask = (genes_from_other_ref_raw['OGI'] != NULL_PLACEHOLDER)
genes_from_other_ref_raw.loc[mask, 'OGI'] = get_rgi_orthogroup_link(
genes_from_other_ref_raw, 'OGI')
genes_from_other_ref_raw['Name'] = get_rgi_genecard_link(

mask = (genes_from_other_ref_raw['Name'] != NULL_PLACEHOLDER)
genes_from_other_ref_raw.loc[mask, 'Name'] = get_rgi_genecard_link(
genes_from_other_ref_raw, 'Name')

genes_from_other_ref = genes_from_other_ref_raw.to_dict(
Expand Down
60 changes: 31 additions & 29 deletions callbacks/text_mining/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@
from .util import *
from ..lift_over import util as lift_over_util


Text_mining_input = namedtuple(
'Text_mining_input', ['text_mining_query'])


def init_callback(app):

# to display user input interval in the top nav
Expand All @@ -30,19 +25,25 @@ def display_input(nb_intervals_str, homepage_is_submitted, *_):

@app.callback(
Output('text-mining-query-saved-input', 'data', allow_duplicate=True),
State('text-mining-query', 'value'),
Input({'type': 'example-text-mining',
'description': ALL}, 'n_clicks'),
prevent_initial_call=True
)
def set_input_fields(query_string, *_):
if ctx.triggered_id:
if 'text-mining-query' == ctx.triggered_id:
return query_string

def set_input_fields_with_preset_input(example_text_mining_n_clicks):
if ctx.triggered_id and not all(val == 0 for val in example_text_mining_n_clicks):
return ctx.triggered_id['description']

raise PreventUpdate


@app.callback(
Output('text-mining-query-saved-input', 'data', allow_duplicate=True),
Input('text-mining-query', 'value'),
prevent_initial_call=True
)
def set_input_fields(query_string):
return query_string


@app.callback(
Output('text-mining-query', 'value'),
Expand All @@ -52,40 +53,41 @@ def get_input_homepage_session_state(query):
return query

@app.callback(
Output('text-mining-input-error', 'style'),
Output('text-mining-input-error', 'children'),
Output('text-mining-is-submitted', 'data', allow_duplicate=True),
Output('text-mining-query-submitted-input',
'data', allow_duplicate=True),
Input('text-mining-submit', 'n_clicks'),
Input('text-mining-query', 'n_submit'),
State('homepage-is-submitted', 'data'),
State('text-mining-query', 'value'),
prevent_initial_call=True
)
def submit_text_mining_input(text_mining_submitted_n_clicks, homepage_is_submitted, text_mining_query):
if homepage_is_submitted and text_mining_submitted_n_clicks >= 1:
submitted_input = Text_mining_input(
text_mining_query)._asdict()

return True, submitted_input
def submit_text_mining_input(text_mining_submitted_n_clicks, text_mining_query_n_submit, homepage_is_submitted, text_mining_query):
if homepage_is_submitted and (text_mining_submitted_n_clicks >= 1 or text_mining_query_n_submit >= 1):
is_there_error, message = is_error(text_mining_query)

if not is_there_error:
return {'display': 'none'}, message, True, text_mining_query
else:
return {'display': 'block'}, message, False, None

raise PreventUpdate


@app.callback(
Output('text-mining-results-container', 'style'),
Output('text-mining-input-error', 'style'),
Output('text-mining-input-error', 'children'),
State('text-mining-query', 'value'),
Input('text-mining-is-submitted', 'data')
)
def display_text_mining_output(text_mining_query, text_mining_is_submitted):
is_there_error, message = is_error(text_mining_query)
def display_coexpression_output(text_mining_is_submitted):
if text_mining_is_submitted:
if not is_there_error:
return {'display': 'block'}, {'display': 'none'}, message
else:
return {'display': 'none'}, {'display': 'block'}, message
return {'display': 'block'}

else:
return {'display': 'none'}

return {'display': 'none'}, {'display': 'none'}, message

@app.callback(
Output('text-mining-result-table', 'data'),
Expand All @@ -98,7 +100,7 @@ def display_text_mining_output(text_mining_query, text_mining_is_submitted):
)
def display_text_mining_results(text_mining_is_submitted, homepage_submitted, text_mining_query_submitted_input):
if homepage_submitted and text_mining_is_submitted:
query_string = text_mining_query_submitted_input['text_mining_query']
query_string = text_mining_query_submitted_input

is_there_error, _ = is_error(query_string)
if not is_there_error:
Expand Down
10 changes: 7 additions & 3 deletions pages/analysis/text_mining.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,23 @@
dbc.Input(
id='text-mining-query',
type='text',
value=''
value='',
debounce=True,
n_submit=0
),

html.Div([html.Span('Examples:', className='pe-3'),
html.Span('pre-harvest sprouting',
id={'type': 'example-text-mining',
'description': 'pre-harvest sprouting'},
className='sample-genomic-interval'),
className='sample-genomic-interval',
n_clicks=0),
html.Span(',', className='sample-genomic-interval'),
html.Span('anaerobic germination',
id={'type': 'example-text-mining',
'description': 'anaerobic germination'},
className='sample-genomic-interval ms-3')],
className='sample-genomic-interval ms-3',
n_clicks=0)],
className='pt-3'),
html.Br(),

Expand Down
4 changes: 3 additions & 1 deletion pages/homepage.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@
dbc.Input(
id='homepage-genomic-intervals',
type='text',
value=''
value='',
debounce=True,
n_submit=0
),

html.Div([
Expand Down