WinslowFan Claude Opus 4.8 commited on
Commit
aee68fd
·
1 Parent(s): 182233e

Update dataset statistics to MuSProt conformational metrics

Browse files

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

frontend/src/components/datasets/protein/ProteinDashboard.tsx CHANGED
@@ -1,5 +1,5 @@
1
- import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
2
- import { FilterPanel } from './FilterPanel';
3
  import { StaticDistributionPlots } from './StaticDistributionPlots';
4
  import { DataTable } from './DataTable';
5
  import { StatsPanel } from './StatsPanel';
@@ -10,7 +10,7 @@ import BindersPanel from './BindersPanel';
10
  import FunctionPanel from './FunctionPanel';
11
  import ErrorBoundary from './ErrorBoundary';
12
  import { proteinApi } from '../../../api/protein';
13
- import type { FilterParams, FilterOptions, DataResponse, SummaryStats, ChainMetadata } from '../../../types/protein';
14
  import { PageSEO } from '../../seo/PageSEO';
15
  import { MuSProtJsonLd } from '../../seo/MuSProtJsonLd';
16
 
@@ -23,13 +23,12 @@ interface CacheEntry {
23
 
24
  const NEGATIVE_CACHE_TTL = 10 * 60 * 1000; // 10 minutes
25
 
26
- export function ProteinDashboard() {
27
  // State management
28
  const [filterOptions, setFilterOptions] = useState<FilterOptions | null>(null);
29
  const [filters, setFilters] = useState<FilterParams>({});
30
  const [dataResponse, setDataResponse] = useState<DataResponse | null>(null);
31
- const [summaryStats, setSummaryStats] = useState<SummaryStats | null>(null);
32
-
33
  // Search input state (controlled)
34
  const [searchInputValue, setSearchInputValue] = useState('');
35
  const [searchStatus, setSearchStatus] = useState<'idle' | 'typing' | 'loading' | 'success' | 'not_found' | 'error'>('idle');
@@ -45,8 +44,7 @@ export function ProteinDashboard() {
45
 
46
  const [, setLoadingFilters] = useState(true);
47
  const [loadingData, setLoadingData] = useState(false);
48
- const [loadingStats, setLoadingStats] = useState(false);
49
-
50
  const [error, setError] = useState<string | null>(null);
51
 
52
  // Load filter options on mount
@@ -57,7 +55,7 @@ export function ProteinDashboard() {
57
  const options = await proteinApi.getFilters();
58
  setFilterOptions(options);
59
  } catch (err) {
60
- setError('Failed to load filter options. The MuSProt database may still be initializing.');
61
  console.error('Error loading filters:', err);
62
  } finally {
63
  setLoadingFilters(false);
@@ -81,29 +79,16 @@ export function ProteinDashboard() {
81
  }
82
  }, [filters]);
83
 
84
- const fetchStats = useCallback(async () => {
85
- try {
86
- setLoadingStats(true);
87
- const stats = await proteinApi.getSummary(filters);
88
- setSummaryStats(stats);
89
- } catch (err) {
90
- console.error('Error fetching stats:', err);
91
- } finally {
92
- setLoadingStats(false);
93
- }
94
- }, [filters]);
95
-
96
  // Debounced data fetching — skip fetchData when there's no active chain search
97
  useEffect(() => {
98
  const timeoutId = setTimeout(() => {
99
  if (filters.pdb_id && filters.auth_asym_id) {
100
  fetchData();
101
  }
102
- fetchStats();
103
  }, 500);
104
 
105
  return () => clearTimeout(timeoutId);
106
- }, [fetchData, fetchStats, filters.pdb_id, filters.auth_asym_id]);
107
 
108
  const handleFiltersChange = (newFilters: FilterParams) => {
109
  setFilters(newFilters);
@@ -271,13 +256,13 @@ export function ProteinDashboard() {
271
  return (
272
  <div className="protein-dashboard min-h-screen bg-white">
273
  <PageSEO
274
- title="MuSProt: Multistate Protein Structure Database"
275
  description="Explore 355,731 protein chains from 121,584 PDB entries with pairwise structural comparisons, energy scoring, and CATH functional annotations. Download the full SQLite database."
276
- path="/"
277
- />
278
- <MuSProtJsonLd />
279
-
280
- {error && (
281
  <div className="max-w-7xl mx-auto px-6 mt-4">
282
  <div className="protein-error-banner">
283
  <span>⚠️ {error}</span>
@@ -366,7 +351,7 @@ export function ProteinDashboard() {
366
 
367
  {/* Default Dataset Summary View */}
368
  <section className="border border-gray-200 rounded-2xl p-6 bg-white shadow-sm hover:border-slate-900 hover:shadow-lg hover:-translate-y-0.5 transition-all duration-300">
369
- <StatsPanel stats={summaryStats} loading={loadingStats} />
370
  </section>
371
 
372
  <section>
 
1
+ import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
2
+ import { FilterPanel } from './FilterPanel';
3
  import { StaticDistributionPlots } from './StaticDistributionPlots';
4
  import { DataTable } from './DataTable';
5
  import { StatsPanel } from './StatsPanel';
 
10
  import FunctionPanel from './FunctionPanel';
11
  import ErrorBoundary from './ErrorBoundary';
12
  import { proteinApi } from '../../../api/protein';
13
+ import type { FilterParams, FilterOptions, DataResponse, ChainMetadata } from '../../../types/protein';
14
  import { PageSEO } from '../../seo/PageSEO';
15
  import { MuSProtJsonLd } from '../../seo/MuSProtJsonLd';
16
 
 
23
 
24
  const NEGATIVE_CACHE_TTL = 10 * 60 * 1000; // 10 minutes
25
 
26
+ export function ProteinDashboard() {
27
  // State management
28
  const [filterOptions, setFilterOptions] = useState<FilterOptions | null>(null);
29
  const [filters, setFilters] = useState<FilterParams>({});
30
  const [dataResponse, setDataResponse] = useState<DataResponse | null>(null);
31
+
 
32
  // Search input state (controlled)
33
  const [searchInputValue, setSearchInputValue] = useState('');
34
  const [searchStatus, setSearchStatus] = useState<'idle' | 'typing' | 'loading' | 'success' | 'not_found' | 'error'>('idle');
 
44
 
45
  const [, setLoadingFilters] = useState(true);
46
  const [loadingData, setLoadingData] = useState(false);
47
+
 
48
  const [error, setError] = useState<string | null>(null);
49
 
50
  // Load filter options on mount
 
55
  const options = await proteinApi.getFilters();
56
  setFilterOptions(options);
57
  } catch (err) {
58
+ setError('Failed to load filter options. The MuSProt database may still be initializing.');
59
  console.error('Error loading filters:', err);
60
  } finally {
61
  setLoadingFilters(false);
 
79
  }
80
  }, [filters]);
81
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  // Debounced data fetching — skip fetchData when there's no active chain search
83
  useEffect(() => {
84
  const timeoutId = setTimeout(() => {
85
  if (filters.pdb_id && filters.auth_asym_id) {
86
  fetchData();
87
  }
 
88
  }, 500);
89
 
90
  return () => clearTimeout(timeoutId);
91
+ }, [fetchData, filters.pdb_id, filters.auth_asym_id]);
92
 
93
  const handleFiltersChange = (newFilters: FilterParams) => {
94
  setFilters(newFilters);
 
256
  return (
257
  <div className="protein-dashboard min-h-screen bg-white">
258
  <PageSEO
259
+ title="MuSProt: Multistate Protein Structure Database"
260
  description="Explore 355,731 protein chains from 121,584 PDB entries with pairwise structural comparisons, energy scoring, and CATH functional annotations. Download the full SQLite database."
261
+ path="/"
262
+ />
263
+ <MuSProtJsonLd />
264
+
265
+ {error && (
266
  <div className="max-w-7xl mx-auto px-6 mt-4">
267
  <div className="protein-error-banner">
268
  <span>⚠️ {error}</span>
 
351
 
352
  {/* Default Dataset Summary View */}
353
  <section className="border border-gray-200 rounded-2xl p-6 bg-white shadow-sm hover:border-slate-900 hover:shadow-lg hover:-translate-y-0.5 transition-all duration-300">
354
+ <StatsPanel />
355
  </section>
356
 
357
  <section>
frontend/src/components/datasets/protein/StatsPanel.tsx CHANGED
@@ -1,42 +1,37 @@
1
  import React from 'react';
2
- import type { SummaryStats } from '../../../types/protein';
3
  import './StatsPanel.css';
4
 
5
- interface StatsPanelProps {
6
- stats: SummaryStats | null;
7
- loading: boolean;
8
- }
9
-
10
- export const StatsPanel: React.FC<StatsPanelProps> = ({ stats, loading }) => {
11
- if (loading || !stats) {
12
- return null;
13
- }
14
-
15
  return (
16
  <div className="stats-panel">
17
  <div className="stat-card">
18
- <div className="stat-value">{stats.total_records.toLocaleString()}</div>
19
- <div className="stat-label">Total Records</div>
20
  </div>
21
-
22
  <div className="stat-card">
23
- <div className="stat-value">{stats.unique_chains.toLocaleString()}</div>
24
- <div className="stat-label">Unique Chains</div>
25
  </div>
26
-
27
  <div className="stat-card">
28
- <div className="stat-value">{stats.avg_rmsd.toFixed(2)} Å</div>
29
- <div className="stat-label">Avg RMSD</div>
30
  </div>
31
-
32
  <div className="stat-card">
33
- <div className="stat-value">{stats.avg_tm_score.toFixed(3)}</div>
34
- <div className="stat-label">Avg TM-score</div>
35
  </div>
36
-
 
 
 
 
 
37
  <div className="stat-card">
38
- <div className="stat-value">{Math.round(stats.avg_sequence_length)}</div>
39
- <div className="stat-label">Avg Length</div>
40
  </div>
41
  </div>
42
  );
 
1
  import React from 'react';
 
2
  import './StatsPanel.css';
3
 
4
+ export const StatsPanel: React.FC = () => {
 
 
 
 
 
 
 
 
 
5
  return (
6
  <div className="stats-panel">
7
  <div className="stat-card">
8
+ <div className="stat-value">64,812</div>
9
+ <div className="stat-label">Sequences with multiple conformational observations</div>
10
  </div>
11
+
12
  <div className="stat-card">
13
+ <div className="stat-value">11,946</div>
14
+ <div className="stat-label">Sequences with multiple state clusters</div>
15
  </div>
16
+
17
  <div className="stat-card">
18
+ <div className="stat-value">355,731</div>
19
+ <div className="stat-label">Conformational observations</div>
20
  </div>
21
+
22
  <div className="stat-card">
23
+ <div className="stat-value">90,745</div>
24
+ <div className="stat-label">Unique state clusters</div>
25
  </div>
26
+
27
+ <div className="stat-card">
28
+ <div className="stat-value">27.6 million</div>
29
+ <div className="stat-label">Transition triplets between conformational observations</div>
30
+ </div>
31
+
32
  <div className="stat-card">
33
+ <div className="stat-value">5.7 million</div>
34
+ <div className="stat-label">Transition triplets between distinct states</div>
35
  </div>
36
  </div>
37
  );