Skip to content

Commit

Permalink
Merge pull request #1894 from broadinstitute/development
Browse files Browse the repository at this point in the history
Release 1.56.0
  • Loading branch information
bistline authored Oct 4, 2023
2 parents 14b0e8b + 583d3ed commit ba5c496
Show file tree
Hide file tree
Showing 55 changed files with 1,686 additions and 527 deletions.
4 changes: 3 additions & 1 deletion app/controllers/api/v1/study_files_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,9 @@ def study_file_params
data_fragments: AnnDataFileInfo::DATA_FRAGMENT_PARAMS
],
differential_expression_file_info_attributes: [
:_id, :clustering_association, :annotation_name, :annotation_scope, :computational_method
:_id, :clustering_association, :annotation_name, :annotation_scope, :computational_method,
:gene_header, :group_header, :comparison_group_header,
:size_metric, :significance_metric
],
)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,19 +241,24 @@ def cell_values
def facets
cluster = ClusterVizService.get_cluster_group(@study, params)
if cluster.nil?
render json: { error: "Cannot find cluster: #{params[:cluster]}" }, status: :not_found and return
render json: { error: "Cannot find clustering: #{params[:cluster]}" }, status: :not_found and return
end

# need to check for presence as some clusters will not have them if cells were not found in all_cells_array
unless cluster.indexed
render json: { error: 'Cluster is not indexed' }, status: :bad_request and return
render json: { error: 'Clustering is not indexed' }, status: :bad_request and return
end

# annotation validation
if params[:annotations].include?('--numeric--')
render json: { error: 'Cannot use numeric annotations for facets' }, status: :bad_request and return
end

annotations = self.class.get_facet_annotations(@study, cluster, params[:annotations])
if annotations.empty? && params[:annotations].blank?
render json: { error: 'Must provide at least one annotation' }, status: :bad_request and return
end

missing = self.class.find_missing_annotations(annotations, params[:annotations])
if missing.any?
render json: { error: "Cannot find annotations: #{missing.join(', ')}" }, status: :not_found and return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,7 @@ def set_selected_annotation
cluster: @cluster,
annot_name: params[:annotation_name],
annot_type: params[:annotation_type],
annot_scope: params[:annotation_scope],
fallback: false)
annot_scope: params[:annotation_scope])
end

def set_subsampling_threshold
Expand Down
1 change: 0 additions & 1 deletion app/controllers/site_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ def study
# normally we would do this in React but the tab display is in the Rails HTML view
# we need to check server-side since we have to account for @study.can_visualize? as well
@explore_tab_default = @study.can_visualize?
@show_appcue_pin = FeatureFlaggable.feature_flags_for_instances(current_user).[](:show_appcue_viz_tour)
end

def record_download_acceptance
Expand Down
6 changes: 5 additions & 1 deletion app/controllers/studies_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,11 @@ def study_file_params
expression_file_info_attributes: [:id, :library_preparation_protocol, :units,
:biosample_input_type, :modality, :is_raw_counts,
raw_counts_associations: []],
differential_expression_file_info_attributes: [:_id, :clustering_association, :annotation_association, :computational_method]
differential_expression_file_info_attributes: [
:_id, :clustering_association, :annotation_association, :computational_method,
:gene_header, :group_header, :comparison_group_header,
:size_metric, :significance_metric
]
)
end

Expand Down
11 changes: 1 addition & 10 deletions app/javascript/components/HomePageContent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import ResultsPanel from '~/components/search/results/ResultsPanel'
import StudyDetails from '~/components/search/results/StudySearchResult'
import StudySearchProvider, { StudySearchContext } from '~/providers/StudySearchProvider'
import SearchFacetProvider from '~/providers/SearchFacetProvider'
import UserProvider, { getFeatureFlagsWithDefaults } from '~/providers/UserProvider'
import UserProvider from '~/providers/UserProvider'
import ErrorBoundary from '~/lib/ErrorBoundary'

/** include search controls and results */
Expand All @@ -30,8 +30,6 @@ const LinkableSearchTabs = function(props) {
const location = useLocation()
const basePath = location.pathname.includes('covid19') ? '/single_cell/covid19' : '/single_cell'
const showGenesTab = location.pathname.includes('/app/genes')
const featureFlags = getFeatureFlagsWithDefaults()
const showVizAppcue = featureFlags && featureFlags?.show_appcue_viz_tour

// the queryParams object does not support the more typical hasOwnProperty test
return (
Expand All @@ -45,13 +43,6 @@ const LinkableSearchTabs = function(props) {
className={showGenesTab ? 'active' : ''}>
<span className="fas fa-dna"></span> Search genes
</Link>
{ showVizAppcue &&
<a className='btn btn-primary appcue-pin'
href='https://singlecell.broadinstitute.org/single_cell/study/SCP2271'
data-analytics-name='appcue-viz-demo'>
Demo of SCP visualization tools
</a>
}
</nav>
<div className="tab-content top-pad">
<Router basepath={basePath}>
Expand Down
68 changes: 29 additions & 39 deletions app/javascript/components/explore/DifferentialExpressionFilters.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ function MetricDisplayValue({ metric }) {
<>
{metric === 'log2FoldChange' && <>log<sub>2</sub>(FC)</>}
{metric === 'pvalAdj' && <>Adj. p-value</>}
{metric === 'pval' && <>p-value</>}
{metric === 'qval' && <>q-value</>}
</>
)
Expand All @@ -18,28 +19,28 @@ function MetricDisplayValue({ metric }) {
/**
* Adds slider widget for a numerical metric
**/
function SliderContainer({ metric, toggleDeFacet, isActive }) {
function SliderContainer({ metricHeader, metric, toggleDeFacet, isActive }) {
return (
<div className={`de-slider-container ${isActive ? '' : 'inactive'}`}>
<div className="de-slider-checkbox-container">
<input
type="checkbox"
checked={isActive}
className={`slider-checkbox slider-checkbox-${metric}`}
onChange={() => {toggleDeFacet(metric)}}
className={`slider-checkbox slider-checkbox-${metricHeader}`}
onChange={() => {toggleDeFacet(metricHeader)}}
/>
<label htmlFor={`slider-checkbox-${metric}`} >
<label htmlFor={`slider-checkbox-${metricHeader}`} >
<MetricDisplayValue metric={metric} />
</label>
</div>
<div className={`de-slider de-slider-${metric}`} data-metric={metric}></div>
<div className={`de-slider de-slider-${metricHeader}`} data-metric-header={metricHeader}></div>
<br/>
</div>
)
}

const defaultSliderConfigProps = {
'pvalAdj': {
'significance': {
range: {
'min': [0, 0.001],
'50%': [0.05, 0.01],
Expand All @@ -52,20 +53,7 @@ const defaultSliderConfigProps = {
values: [0, 25, 50, 73.5, 100],
density: 4
},
'qval': {
range: {
'min': [0, 0.001],
'50%': [0.05, 0.01],
'max': 1
},
start: [0, 0.05],
sliderDecimals: 3,
pipDecimals: 3,
connect: true,
values: [0, 25, 50, 73.5, 100],
density: 4
},
'log2FoldChange': {
'size': {
range: {
'min': [-1.5],
'max': 1.5
Expand All @@ -83,12 +71,12 @@ const defaultSliderConfigProps = {
*
* noUiSlider docs: https://refreshless.com/nouislider/
**/
function getSliderConfig(metric) {
const defaultProps = defaultSliderConfigProps[metric]
function getSliderConfig(metricHeader) {
const defaultProps = defaultSliderConfigProps[metricHeader]

let configRange
let configStart
if (['pvalAdj', 'qval'].includes(metric)) {
if (metricHeader === 'significance') {
configRange = {
'min': [0, 0.001],
'50%': [0.05, 0.01],
Expand Down Expand Up @@ -135,19 +123,20 @@ function getSliderConfig(metric) {

/** Range filters for DE table */
export default function DifferentialExpressionFilters({
deFacets, activeFacets, updateDeFacets, toggleDeFacet, isAuthorDe
deFacets, activeFacets, updateDeFacets, toggleDeFacet,
hasPairwiseDe, sizeMetric, significanceMetric
}) {
const fdrMetric = isAuthorDe ? 'qval' : 'pvalAdj'
const metrics = ['log2FoldChange', fdrMetric]
const metricHeaders = ['size', 'significance']
const metrics = [sizeMetric, significanceMetric]

/** Update DE facets upon changing range filter selection */
function onUpdateDeFacets(range) {
// eslint-disable-next-line no-invalid-this
const slider = this
const metric = slider.target.dataset['metric']
const metricHeader = slider.target.dataset['metricHeader']

range = range.map(d => parseFloat(d))
if (metric === 'log2FoldChange') {
if (metricHeader === 'size') {
range = range.map(v => {
if (v === -1.5) {return -Infinity}
if (v === 1.5) {return Infinity}
Expand All @@ -157,20 +146,20 @@ export default function DifferentialExpressionFilters({
} else {
range = [{ min: range[0], max: range[1] }]
}
deFacets[metric] = range
deFacets[metricHeader] = range

updateDeFacets(deFacets, metric)
updateDeFacets(deFacets, metricHeader)
}

useEffect(() => {
metrics.forEach(metric => {
const sliderSelector = `.de-slider-${metric}`
metricHeaders.forEach(metricHeader => {
const sliderSelector = `.de-slider-${metricHeader}`
const slider = document.querySelector(sliderSelector)

if (!slider.noUiSlider) {
const config = getSliderConfig(metric)
const config = getSliderConfig(metricHeader)
noUiSlider.create(slider, config)
if (metric === 'log2FoldChange') {
if (metricHeader === 'size') {
const val1 = document.querySelector(`${sliderSelector} .noUi-value:nth-child(2)`)
val1.innerHTML = `≤ ${ val1.innerHTML}`
const val2 = document.querySelector(`${sliderSelector} .noUi-value:last-child`)
Expand All @@ -183,13 +172,14 @@ export default function DifferentialExpressionFilters({

return (
<div>
{!isAuthorDe && <br/>}
{metrics.map(metric =>
{!hasPairwiseDe && <br/>}
{metricHeaders.map((metricHeader, i) =>
<SliderContainer
metric={metric}
key={metric}
metricHeader={metricHeader}
metric={metrics[i]}
key={metricHeader}
toggleDeFacet={toggleDeFacet}
isActive={activeFacets[metric]}
isActive={activeFacets[metricHeader]}
/>
)}
</div>
Expand Down
Loading

0 comments on commit ba5c496

Please sign in to comment.