Skip to content

Commit

Permalink
gene apis enhanced to support sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
vikasguptaebi committed Nov 15, 2024
1 parent 30e4de1 commit 67bb7af
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 97 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import React, { useState } from 'react';
import styles from './GeneResultsTable.module.scss';
import {createViewState} from '@jbrowse/react-app';
import { createViewState } from '@jbrowse/react-app';

type ViewModel = ReturnType<typeof createViewState>;

Expand All @@ -11,7 +11,7 @@ interface LinkData {

interface GeneResultsTableProps {
results: any[];
onSortClick: (sortField: string) => void;
onSortClick: (sortField: string, sortOrder: 'asc' | 'desc') => void;
linkData: LinkData;
viewState?: ViewModel;
}
Expand All @@ -37,29 +37,59 @@ const handleNavigation = (
}
};
const GeneResultsTable: React.FC<GeneResultsTableProps> = ({
results,
onSortClick,
linkData,
viewState
}) => {
results,
onSortClick,
linkData,
viewState,
}) => {
const [sortField, setSortField] = useState<string | null>(null);
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc');

const handleSort = (field: string) => {
const newSortOrder = sortField === field && sortOrder === 'asc' ? 'desc' : 'asc';
setSortField(field);
setSortOrder(newSortOrder);
onSortClick(field, newSortOrder);
};

return (
<table className="vf-table vf-table--sortable">
<thead className="vf-table__header">
<tr className="vf-table__row">
<th className={`vf-table__heading ${styles.vfTableHeading}`} scope="col">Strain
<button
className="vf-button vf-button--sm vf-button--icon vf-table__button vf-table__button--sortable"
onClick={() => onSortClick('species')}
/>
<th onClick={() => handleSort('strain')}
className={`vf-table__heading ${styles.vfTableHeading} ${styles.clickableHeader}`}>
Strain
{sortField === 'strain' ? (
<span className={`icon icon-common ${sortOrder === 'asc' ? 'icon-sort-up' : 'icon-sort-down'}`}
style={{paddingLeft: '5px'}}></span>
) : (
<span className="icon icon-common icon-sort" style={{paddingLeft: '5px'}}></span>
)}
</th>
<th onClick={() => handleSort('gene_name')}
className={`vf-table__heading ${styles.vfTableHeading} ${styles.clickableHeader}`}>
Gene
{sortField === 'gene_name' ? (
<span className={`icon icon-common ${sortOrder === 'asc' ? 'icon-sort-up' : 'icon-sort-down'}`}
style={{paddingLeft: '5px'}}></span>
) : (
<span className="icon icon-common icon-sort" style={{paddingLeft: '5px'}}></span>
)}
</th>
<th className={`vf-table__heading ${styles.vfTableHeading}`} scope="col">Gene
<button
className="vf-button vf-button--sm vf-button--icon vf-table__button vf-table__button--sortable"
onClick={() => onSortClick('isolate_name')}
/>
<th onClick={() => handleSort('locus_tag')}
className={`vf-table__heading ${styles.vfTableHeading} ${styles.clickableHeader}`}>
Locus Tag
{sortField === 'locus_tag' ? (
<span className={`icon icon-common ${sortOrder === 'asc' ? 'icon-sort-up' : 'icon-sort-down'}`}
style={{paddingLeft: '5px'}}></span>
) : (
<span className="icon icon-common icon-sort" style={{paddingLeft: '5px'}}></span>
)}
</th>
<th onClick={() => handleSort('product')}
className={`vf-table__heading ${styles.vfTableHeading}`}>
Product
</th>
<th className={`vf-table__heading ${styles.vfTableHeading}`} scope="col">Product</th>
<th className={`vf-table__heading ${styles.vfTableHeading}`} scope="col">Locus Tag</th>
<th className={`vf-table__heading ${styles.vfTableHeading}`} scope="col">Actions</th>
</tr>
</thead>
Expand All @@ -68,8 +98,8 @@ const GeneResultsTable: React.FC<GeneResultsTableProps> = ({
<tr key={index} className="vf-table__row">
<td className={`vf-table__cell ${styles.vfTableCell}`}>{result.strain || 'Unknown Strain'}</td>
<td className={`vf-table__cell ${styles.vfTableCell}`}>{result.gene_name || 'Unknown Gene Name'}</td>
<td className={`vf-table__cell ${styles.vfTableCell}`}>{result.description || ''}</td>
<td className={`vf-table__cell ${styles.vfTableCell}`}>{result.locus_tag || 'Unknown Locus Tag'}</td>
<td className={`vf-table__cell ${styles.vfTableCell}`}>{result.description || ''}</td>
<td className={`vf-table__cell ${styles.vfTableCell}`}>
{viewState ? (
<a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ interface GeneSearchFormProps {
onSearchSubmit: () => void,
selectedSpecies?: number [],
results: any[],
onSortClick: (sortField: string) => void,
onSortClick: (sortField: string, sortOrder: 'asc' | 'desc') => void;
sortField: string,
sortOrder: 'asc' | 'desc';
totalPages: number,
currentPage: number,
handlePageClick: (page: number) => void,
Expand All @@ -31,7 +33,9 @@ const GeneSearchForm: React.FC<GeneSearchFormProps> = ({
onSortClick,
selectedGenomes,
linkData,
viewState
viewState,
sortField,
sortOrder
}) => {
const [query, setQuery] = useState<string>('');
const [suggestions, setSuggestions] = useState<{
Expand All @@ -41,8 +45,6 @@ const GeneSearchForm: React.FC<GeneSearchFormProps> = ({
}[]>([]);
const [geneName, setGeneName] = useState<string>('');
const [results, setResults] = useState<any[]>([]);
const [currentSortField, setCurrentSortField] = useState<string>('');
const [currentSortOrder, setCurrentSortOrder] = useState<string>('');
const [currentPage, setCurrentPage] = useState<number>(1);
const [totalPages, setTotalPages] = useState<number>(1);
const [hasPrevious, setHasPrevious] = useState<boolean>(false);
Expand All @@ -55,7 +57,7 @@ const GeneSearchForm: React.FC<GeneSearchFormProps> = ({
};

useEffect(() => {
fetchSearchResults(1);
fetchSearchResults(1, sortField, sortOrder);
}, [pageSize]);

// Fetch suggestions for autocomplete based on the query and selected species
Expand Down Expand Up @@ -110,7 +112,8 @@ const GeneSearchForm: React.FC<GeneSearchFormProps> = ({

// Fetch search results based on the query, selected species, page, sort field, and sort order
const fetchSearchResults = useCallback(
async (page = 1, sortField = currentSortField, sortOrder = currentSortOrder) => {
async (page = 1, sortField: string, sortOrder: string) => {
console.log('111111111111')
const params = new URLSearchParams({
'query': query.trim() || '',
'page': String(page),
Expand Down Expand Up @@ -156,13 +159,13 @@ const GeneSearchForm: React.FC<GeneSearchFormProps> = ({
setHasNext(false);
}
},
[query, selectedSpecies, selectedGenomes, currentSortField, currentSortOrder, pageSize]
[query, selectedSpecies, selectedGenomes, sortField, sortOrder, pageSize]
);


useEffect(() => {
fetchSearchResults();
}, [selectedSpecies, selectedGenomes, pageSize]);
fetchSearchResults(1, sortField, sortOrder);
}, [selectedSpecies, selectedGenomes, sortField, sortOrder, pageSize]);


const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -187,19 +190,13 @@ const GeneSearchForm: React.FC<GeneSearchFormProps> = ({
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
console.log('selectedStrainId:' + selectedGeneId)
event.preventDefault();
fetchSearchResults();
fetchSearchResults(currentPage, sortField, sortOrder);
};

const handleSortClick = (sortField: string) => {
const newSortOrder = currentSortField === sortField ? (currentSortOrder === 'asc' ? 'desc' : 'asc') : 'asc';
setCurrentSortField(sortField);
setCurrentSortOrder(newSortOrder);
fetchSearchResults(1, sortField, newSortOrder);
};

const handlePageClick = (page: number) => {
setCurrentPage(page);
fetchSearchResults(page);
fetchSearchResults(page, sortField, sortOrder);
};

return (
Expand Down
8 changes: 7 additions & 1 deletion dataportal-app/src/components/pages/GeneViewerPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ const GeneViewerPage: React.FC = () => {
const [geneCurrentPage, setGeneCurrentPage] = useState(1);

const {geneId, genomeId} = useParams<{ geneId?: string; genomeId?: string }>();

const [sortField, setSortField] = useState<string>('species');
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc');

const searchParams = new URLSearchParams(location.search);
// const genomeId = searchParams.get('genomeId');

Expand Down Expand Up @@ -233,7 +237,9 @@ const GeneViewerPage: React.FC = () => {
onSearchSubmit={handleGeneSearch}
selectedGenomes={genomeId ? [{id: parseInt(genomeId, 10), name: ''}] : []}
results={geneResults}
onSortClick={(sortField) => console.log('Sort by:', sortField)}
onSortClick={handleGeneSearch}
sortField={sortField}
sortOrder={sortOrder}
currentPage={geneCurrentPage}
totalPages={totalPages}
handlePageClick={(page) => setGeneCurrentPage(page)}
Expand Down
8 changes: 5 additions & 3 deletions dataportal-app/src/components/pages/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ const HomePage: React.FC = () => {
const newSortOrder = sortField === field && sortOrder === 'asc' ? 'desc' : 'asc';
setSortField(field);
setSortOrder(newSortOrder);
console.log('Sorting Genes by:', {field, order: newSortOrder});
console.log('HomePage Sorting Genes by:', {field, order: newSortOrder});
};

const linkData = {
Expand Down Expand Up @@ -226,13 +226,13 @@ const HomePage: React.FC = () => {
onGenomeSelect={handleGenomeSelect}
selectedSpecies={selectedSpecies}
onSortClick={handleGenomeSortClick}
sortField={sortField}
sortOrder={sortOrder}
results={genomeResults}
selectedGenomes={selectedGenomes}
onToggleGenomeSelect={handleToggleGenomeSelect}
currentPage={1}
totalPages={1}
sortField={sortField}
sortOrder={sortOrder}
handlePageClick={(page) => console.log('Page:', page)}
/>
)}
Expand All @@ -246,6 +246,8 @@ const HomePage: React.FC = () => {
selectedGenomes={selectedGenomes}
results={geneResults}
onSortClick={handleGeneSortClick}
sortField={sortField}
sortOrder={sortOrder}
currentPage={1}
totalPages={1}
handlePageClick={(page) => console.log('Page:', page)}
Expand Down
5 changes: 3 additions & 2 deletions dataportal-app/src/services/geneService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ export const fetchGeneSearchResults = async (
selectedSpecies?: number [],
) => {
try {
console.log('2222222222')
const params = new URLSearchParams({
query: gene,
page: String(page),
per_page: String(perPage),
sortField,
sortOrder,
sort_field: sortField,
sort_order: sortOrder,
});

// Add genome IDs if available
Expand Down
3 changes: 3 additions & 0 deletions dataportal_api/dataportal/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ async def search_genes_by_multiple_genomes_and_species_and_string(
sort_order: Optional[str] = "asc",
):
try:
logger.debug(
f"Request received with params: query={query}, page={page}, per_page={per_page}, sortField={sort_field}, sortOrder={sort_order}"
)
return await gene_service.get_genes_by_multiple_genomes_and_string(
genome_ids, species_id, query, page, per_page, sort_field, sort_order
)
Expand Down
92 changes: 39 additions & 53 deletions dataportal_api/dataportal/services/gene_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,33 @@ async def get_genes_by_multiple_genomes_and_string(
if query:
filters &= Q(gene_name__icontains=query.strip())

valid_sort_fields = {
"gene_name": "gene_name",
"strain": "strain__isolate_name",
"description": "description",
"locus_tag": "locus_tag",
"product": "product",
}

# Validate and map sort_field
sort_field_mapped = valid_sort_fields.get(sort_field, "gene_name")
if sort_field not in valid_sort_fields:
logger.warning(
f"Invalid sort_field '{sort_field}', defaulting to 'gene_name'"
)
sort_field_mapped = "gene_name"

logger.info(
f"Fetching genes with sort_field='{sort_field_mapped}', sort_order='{sort_order}'"
)

# Fetch paginated genes with sorting
genes, total_results = await self._fetch_paginated_genes(
filters, page, per_page, sort_field, sort_order
filters, page, per_page, sort_field_mapped, sort_order
)

serialized_genes = [self._serialize_gene(gene) for gene in genes]

return self._create_pagination_schema(
serialized_genes, page, per_page, total_results
)
Expand All @@ -183,47 +206,6 @@ async def get_genes_by_multiple_genomes_and_string(
logger.error(f"Error in search_genes_by_multiple_genomes_and_string: {e}")
raise HttpError(500, "Internal Server Error")

async def search_genes_in_strain(self, strain_id: int, query: str, limit: int = 10):
try:
gene_filter = Q(strain_id=strain_id, gene_name__icontains=query)
genes = await sync_to_async(
lambda: list(
Gene.objects.filter(gene_filter).select_related("strain")[:limit]
)
)()
return [
{"gene_name": gene.gene_name, "description": gene.description}
for gene in genes
]
except Exception as e:
logger.error(f"Error searching genes in strain: {e}")
return []

async def search_genes_globally(self, query: str, limit: int = 10):
try:
gene_filter = Q(gene_name__icontains=query) | Q(
description__icontains=query
)
genes = await sync_to_async(
lambda: list(
Gene.objects.filter(gene_filter).select_related("strain")[:limit]
)
)()
return [
{
"gene_name": gene.gene_name,
"seq_id": gene.seq_id,
"strain": gene.strain.isolate_name if gene.strain else "Unknown",
"assembly": gene.strain.assembly_name if gene.strain else None,
"description": gene.description,
}
for gene in genes
]

except Exception as e:
logger.error(f"Error searching genes globally: {e}")
return []

# helper methods

def _serialize_gene(self, gene: Gene) -> dict:
Expand Down Expand Up @@ -278,14 +260,18 @@ async def _fetch_paginated_genes(
order_prefix = "-" if sort_order == "desc" else ""
sort_by = f"{order_prefix}{sort_field}" if sort_field else "gene_name"

genes = await sync_to_async(
lambda: list(
Gene.objects.select_related("strain")
.filter(filter_criteria)
.order_by(sort_by)[start : start + per_page]
)
)()
total_results = await sync_to_async(
Gene.objects.filter(filter_criteria).count
)()
return genes, total_results
try:
genes = await sync_to_async(
lambda: list(
Gene.objects.select_related("strain")
.filter(filter_criteria)
.order_by(sort_by)[start : start + per_page]
)
)()
total_results = await sync_to_async(
Gene.objects.filter(filter_criteria).count
)()
return genes, total_results
except Exception as e:
logger.error(f"Error in _fetch_paginated_genes: {e}")
raise

0 comments on commit 67bb7af

Please sign in to comment.