Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions backend/variants/query_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@ class CaseQuery:
mitomap_count: typing.Optional[int] = None
mitomap_frequency: typing.Optional[float] = None

_quick_preset_label: typing.Optional[str] = None
_quick_preset_label_version: typing.Optional[int] = 1
_category_preset_labels: typing.Optional[typing.Dict[str, typing.Optional[str]]] = None


class QueryJsonToFormConverter:
"""Helper class"""
Expand Down Expand Up @@ -478,6 +482,14 @@ def convert(self, case: Case, query: CaseQuery) -> typing.Dict[str, typing.Any]:
None if not value else getattr(value, field, None)
)

# Preserve the quick preset label and version if present
if query._quick_preset_label is not None:
result["_quick_preset_label"] = query._quick_preset_label
if query._quick_preset_label_version is not None:
result["_quick_preset_label_version"] = query._quick_preset_label_version
if query._category_preset_labels is not None:
result["_category_preset_labels"] = query._category_preset_labels

return result, query.VERSION


Expand Down
31 changes: 31 additions & 0 deletions backend/variants/schemas/case-query-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -1501,6 +1501,37 @@
]
}
}
},
"_quick_preset_label": {
"$id": "#/properties/_quick_preset_label",
"type": ["string", "null"],
"title": "Quick preset label",
"description": "Label of the quick preset that was used for this query (for display purposes only)",
"default": null
},
"_quick_preset_label_version": {
"$id": "#/properties/_quick_preset_label_version",
"type": ["integer", "null"],
"title": "Quick preset label version",
"description": "Version number of the quick preset label to track changes over time",
"default": 1,
"minimum": 1
},
"_category_preset_labels": {
"$id": "#/properties/_category_preset_labels",
"type": ["object", "null"],
"title": "Category preset labels",
"description": "Labels of category presets (inheritance, frequency, impact, quality, chromosomes, flagsetc) for display in exports",
"default": null,
"properties": {
"inheritance": {"type": ["string", "null"]},
"frequency": {"type": ["string", "null"]},
"impact": {"type": ["string", "null"]},
"quality": {"type": ["string", "null"]},
"chromosomes": {"type": ["string", "null"]},
"flagsetc": {"type": ["string", "null"]}
},
"additionalProperties": false
}
},
"additionalProperties": false
Expand Down
147 changes: 147 additions & 0 deletions backend/variants/views/api/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,94 @@ def generate_pdf_directly(filter_settings, case_info, request, base_filename):
story.append(meta_table)
story.append(Spacer(1, 20))

# Add quick preset label and version if available
quick_preset_label = filter_settings.get("_quick_preset_label")
quick_preset_version = filter_settings.get("_quick_preset_label_version")

# Always display quick preset, showing "custom" if none was set
if quick_preset_label:
preset_value = (
f"{quick_preset_label} (v{quick_preset_version})"
if quick_preset_version
else quick_preset_label
)
else:
preset_value = "custom"

preset_data = [["Quick Preset:", preset_value]]
preset_table = Table(preset_data, colWidths=[3 * inch, 4 * inch])
preset_table.setStyle(
TableStyle(
[
("ALIGN", (0, 0), (-1, -1), "LEFT"),
("FONTNAME", (0, 0), (0, -1), "Helvetica-Bold"),
("FONTNAME", (1, 0), (1, -1), "Helvetica"),
("FONTSIZE", (0, 0), (-1, -1), 10),
("GRID", (0, 0), (-1, -1), 1, colors.black),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("RIGHTPADDING", (0, 0), (-1, -1), 8),
("TOPPADDING", (0, 0), (-1, -1), 6),
("BOTTOMPADDING", (0, 0), (-1, -1), 6),
]
)
)
story.append(preset_table)
story.append(Spacer(1, 12))

# Add category preset labels if available
category_presets = filter_settings.get("_category_preset_labels")
if category_presets:
preset_order = [
"inheritance",
"frequency",
"impact",
"quality",
"chromosomes",
"flagsetc",
]
preset_display_names = {
"inheritance": "Inheritance",
"frequency": "Frequency",
"impact": "Impact",
"quality": "Quality",
"chromosomes": "Chromosomes",
"flagsetc": "Flags etc.",
}

# Collect presets that have values
preset_rows = [["Category", "Preset"]] # Header row
for key in preset_order:
if category_presets.get(key):
display_name = preset_display_names.get(key, key.capitalize())
preset_rows.append([display_name, category_presets[key]])

if len(preset_rows) > 1: # Has data rows beyond header
# Add heading
story.append(Paragraph("Category Presets", heading3_style))
story.append(Spacer(1, 6))

# Create table
category_table = Table(preset_rows, colWidths=[3 * inch, 4 * inch])
category_table.setStyle(
TableStyle(
[
("ALIGN", (0, 0), (-1, -1), "LEFT"),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"), # Header row bold
("FONTNAME", (0, 1), (-1, -1), "Helvetica"),
("FONTSIZE", (0, 0), (-1, -1), 10),
("GRID", (0, 0), (-1, -1), 1, colors.black),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("RIGHTPADDING", (0, 0), (-1, -1), 8),
("TOPPADDING", (0, 0), (-1, -1), 6),
("BOTTOMPADDING", (0, 0), (-1, -1), 6),
]
)
)
story.append(category_table)
story.append(Spacer(1, 20))

# Add complete filter settings
if filter_settings:
# Categorize filter settings using the same logic as DOCX export
Expand Down Expand Up @@ -2336,6 +2424,65 @@ def _add_document_header(doc, case_info, request, filter_settings):
info_p.add_run("\nTranscript Database: ").bold = True
info_p.add_run(display_value)

# Add quick preset label and version if available
quick_preset_label = filter_settings.get("_quick_preset_label")
quick_preset_version = filter_settings.get("_quick_preset_label_version")

# Always display quick preset, showing "custom" if none was set
info_p.add_run("\n\nQuick Preset: ").bold = True
if quick_preset_label:
if quick_preset_version:
info_p.add_run(f"{quick_preset_label} (v{quick_preset_version})")
else:
info_p.add_run(quick_preset_label)
else:
info_p.add_run("custom")

# Add category preset labels if available
category_presets = filter_settings.get("_category_preset_labels")
if category_presets:
# Create a small table for category presets
preset_order = ["inheritance", "frequency", "impact", "quality", "chromosomes", "flagsetc"]
preset_display_names = {
"inheritance": "Inheritance",
"frequency": "Frequency",
"impact": "Impact",
"quality": "Quality",
"chromosomes": "Chromosomes",
"flagsetc": "Flags etc.",
}

# Collect presets that have values
preset_rows = []
for key in preset_order:
if category_presets.get(key):
display_name = preset_display_names.get(key, key.capitalize())
preset_rows.append((display_name, category_presets[key]))

if preset_rows:
# Add table heading
heading = doc.add_paragraph()
heading.add_run("Category Presets").bold = True

# Create table with 2 columns
table = doc.add_table(rows=len(preset_rows) + 1, cols=2)
table.style = "Table Grid"

# Add header row
header_cells = table.rows[0].cells
header_cells[0].text = "Category"
header_cells[1].text = "Preset"
for cell in header_cells:
for paragraph in cell.paragraphs:
for run in paragraph.runs:
run.font.bold = True

# Add data rows
for idx, (category, preset) in enumerate(preset_rows):
row = table.rows[idx + 1]
row.cells[0].text = category
row.cells[1].text = preset


def _categorize_filter_settings(filter_settings):
"""
Expand Down
61 changes: 60 additions & 1 deletion frontend/src/variants/components/FilterForm/QuickPresets.vue
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ const inheritanceWrapper = computed({
props.querySettings.recessive_mode = recessiveMode
inheritanceRef.value = newValue
lastExplicitInheritance.value = newValue // Track explicit selection
updateCategoryPresetLabels()
}
},
})
Expand Down Expand Up @@ -221,7 +222,7 @@ const refreshQualityRef = () => {
qualityRef.value = 'custom'
}

/** Computed property for quality. If set through here, the default is applied (except for custom). */
/** Computed propery for quality. If set through here, the default is applied (except for custom). */
const qualityWrapper = computed({
get() {
return qualityRef.value
Expand All @@ -235,6 +236,7 @@ const qualityWrapper = computed({
)
}
refreshQualityRef()
updateCategoryPresetLabels()
}
},
})
Expand Down Expand Up @@ -305,6 +307,10 @@ const makeWrapper = (name) =>
}
}
blockRefresh.value = oldBlockRefresh
// Update the valueRef to reflect the new selection
valueRefs[name].value = newValue
// Update category preset labels after setting values
updateCategoryPresetLabels()
}
},
})
Expand Down Expand Up @@ -337,11 +343,14 @@ const refreshQuickPreset = () => {
flagsWrapper.value === theQuickPresets.flagsetc
) {
quickPresetRef.value = name
// Update the store with the matched preset label
variantQueryStore.selectedQuickPresetLabel = theQuickPresets.label || name
return
}
}
// if we reach here, nothing is compatible, assign "custom"
quickPresetRef.value = 'custom'
variantQueryStore.selectedQuickPresetLabel = 'custom'
}

/** Computed propery for quick preset. If set through here, the default is applied (except for custom). */
Expand All @@ -361,6 +370,15 @@ const quickPresetWrapper = computed({
qualityWrapper.value = newQuickPresets.quality
chromosomesWrapper.value = newQuickPresets.chromosomes
flagsWrapper.value = newQuickPresets.flagsetc
// Store the preset label in the query store
variantQueryStore.selectedQuickPresetLabel =
newQuickPresets.label || newValue
// Update category preset labels
updateCategoryPresetLabels()
} else {
// Set to custom when manually selecting custom
variantQueryStore.selectedQuickPresetLabel = 'custom'
variantQueryStore.categoryPresetLabels = null
}
blockRefresh.value = oldBlockRefresh
},
Expand Down Expand Up @@ -398,6 +416,47 @@ const refreshAllRefs = () => {
refreshQuickPreset()
}

/** Update category preset labels in the query store. */
const updateCategoryPresetLabels = () => {
const labels = {}

// Get label for each category preset
if (inheritanceWrapper.value && inheritanceWrapper.value !== 'custom') {
const preset =
props.categoryPresets?.inheritance?.[inheritanceWrapper.value]
labels.inheritance = preset?.label || inheritanceWrapper.value
}

if (frequencyWrapper.value && frequencyWrapper.value !== 'custom') {
const preset = props.categoryPresets?.frequency?.[frequencyWrapper.value]
labels.frequency = preset?.label || frequencyWrapper.value
}

if (impactWrapper.value && impactWrapper.value !== 'custom') {
const preset = props.categoryPresets?.impact?.[impactWrapper.value]
labels.impact = preset?.label || impactWrapper.value
}

if (qualityWrapper.value && qualityWrapper.value !== 'custom') {
const preset = props.categoryPresets?.quality?.[qualityWrapper.value]
labels.quality = preset?.label || qualityWrapper.value
}

if (chromosomesWrapper.value && chromosomesWrapper.value !== 'custom') {
const preset =
props.categoryPresets?.chromosomes?.[chromosomesWrapper.value]
labels.chromosomes = preset?.label || chromosomesWrapper.value
}

if (flagsWrapper.value && flagsWrapper.value !== 'custom') {
const preset = props.categoryPresets?.flagsetc?.[flagsWrapper.value]
labels.flagsetc = preset?.label || flagsWrapper.value
}

variantQueryStore.categoryPresetLabels =
Object.keys(labels).length > 0 ? labels : null
}

/** React to store changes by adjusting the selection fields. */
onMounted(() => {
variantQueryStore.initializeRes.then(() => {
Expand Down
43 changes: 43 additions & 0 deletions frontend/src/variants/components/FilterResultsTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,30 @@ const extraAnnoFields = computed(
() => variantResultSetStore.extraAnnoFields ?? [],
)

const queryPresetLabel = computed(() => {
// Get the preset label from the executed query if available
const storedLabel =
variantResultSetStore.query?.query_settings?._quick_preset_label

// Return the label if present, otherwise show "custom" for queries without a preset
if (storedLabel) {
return storedLabel
}
// For queries created before this feature or custom queries
if (variantResultSetStore.query?.query_settings) {
return 'custom'
}
// No query loaded yet
return '-'
})

const queryPresetLabelVersion = computed(() => {
return (
variantResultSetStore.query?.query_settings?._quick_preset_label_version ||
null
)
})

const scrollToLastPosition = () => {
if (variantQueryStore.lastPosition) {
const elem = document.querySelector('div#app')
Expand Down Expand Up @@ -760,6 +784,25 @@ watch(
</span>
</div>
</div>
<div class="pr-3 align-self-start">
<div>
<label class="font-weight-bold small mb-0 text-nowrap">
Quick Preset
</label>
</div>
<div class="text-center">
<span
class="badge badge-secondary"
:title="
queryPresetLabelVersion
? `Query quick preset: ${queryPresetLabel} (v${queryPresetLabelVersion})`
: `Query quick preset: ${queryPresetLabel}`
"
>
{{ queryPresetLabel }}
</span>
</div>
</div>
<ColumnControl
v-model:display-details="displayDetails"
v-model:display-frequency="displayFrequency"
Expand Down
Loading
Loading