Spaces:
Running
Running
Commit
·
e4f1db2
0
Parent(s):
Initial commit for Hugging Face Spaces
Browse files- .dockerignore +80 -0
- .gitignore +60 -0
- Dockerfile +58 -0
- README.md +72 -0
- backend/package-lock.json +1406 -0
- backend/package.json +21 -0
- backend/routes/algorithms.js +47 -0
- backend/routes/pubmed.js +129 -0
- backend/routes/search.js +527 -0
- backend/server.js +26 -0
- data/algorithms.json +238 -0
- data/timeline-cache.json +392 -0
- frontend/index.html +13 -0
- frontend/package-lock.json +0 -0
- frontend/package.json +29 -0
- frontend/postcss.config.js +6 -0
- frontend/src/App.tsx +51 -0
- frontend/src/components/AlgorithmCard.tsx +177 -0
- frontend/src/components/Navbar.tsx +74 -0
- frontend/src/docs/cnn.md +51 -0
- frontend/src/docs/linear_regression.md +162 -0
- frontend/src/index.css +3 -0
- frontend/src/main.tsx +10 -0
- frontend/src/pages/AlgorithmDetail.tsx +262 -0
- frontend/src/pages/Dashboard.tsx +255 -0
- frontend/src/pages/Roadmap.tsx +865 -0
- frontend/src/pages/Search.tsx +168 -0
- frontend/src/pages/Timeline.tsx +427 -0
- frontend/src/services/api.ts +16 -0
- frontend/tailwind.config.js +11 -0
- frontend/vite.config.ts +15 -0
- package.json +21 -0
.dockerignore
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Node modules
|
2 |
+
node_modules
|
3 |
+
*/node_modules
|
4 |
+
npm-debug.log*
|
5 |
+
yarn-debug.log*
|
6 |
+
yarn-error.log*
|
7 |
+
|
8 |
+
# Build outputs
|
9 |
+
frontend/dist
|
10 |
+
backend/dist
|
11 |
+
*.tgz
|
12 |
+
|
13 |
+
# Development files
|
14 |
+
.env.local
|
15 |
+
.env.development.local
|
16 |
+
.env.test.local
|
17 |
+
.env.production.local
|
18 |
+
|
19 |
+
# IDE and editor files
|
20 |
+
.vscode
|
21 |
+
.idea
|
22 |
+
*.swp
|
23 |
+
*.swo
|
24 |
+
*~
|
25 |
+
|
26 |
+
# OS generated files
|
27 |
+
.DS_Store
|
28 |
+
.DS_Store?
|
29 |
+
._*
|
30 |
+
.Spotlight-V100
|
31 |
+
.Trashes
|
32 |
+
ehthumbs.db
|
33 |
+
Thumbs.db
|
34 |
+
|
35 |
+
# Git
|
36 |
+
.git
|
37 |
+
.gitignore
|
38 |
+
|
39 |
+
# Docker
|
40 |
+
Dockerfile*
|
41 |
+
docker-compose*
|
42 |
+
.dockerignore
|
43 |
+
|
44 |
+
# Logs
|
45 |
+
logs
|
46 |
+
*.log
|
47 |
+
|
48 |
+
# Runtime data
|
49 |
+
pids
|
50 |
+
*.pid
|
51 |
+
*.seed
|
52 |
+
*.pid.lock
|
53 |
+
|
54 |
+
# Coverage directory used by tools like istanbul
|
55 |
+
coverage
|
56 |
+
*.lcov
|
57 |
+
|
58 |
+
# Dependency directories
|
59 |
+
.npm
|
60 |
+
.eslintcache
|
61 |
+
|
62 |
+
# Optional npm cache directory
|
63 |
+
.npm
|
64 |
+
|
65 |
+
# Optional REPL history
|
66 |
+
.node_repl_history
|
67 |
+
|
68 |
+
# Output of 'npm pack'
|
69 |
+
*.tgz
|
70 |
+
|
71 |
+
# Yarn Integrity file
|
72 |
+
.yarn-integrity
|
73 |
+
|
74 |
+
# dotenv environment variables file
|
75 |
+
.env
|
76 |
+
.env.test
|
77 |
+
|
78 |
+
# Temporary folders
|
79 |
+
tmp/
|
80 |
+
temp/
|
.gitignore
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Claude Code specific
|
2 |
+
CLAUDE.md
|
3 |
+
|
4 |
+
# Dependencies
|
5 |
+
node_modules/
|
6 |
+
*/node_modules/
|
7 |
+
npm-debug.log*
|
8 |
+
yarn-debug.log*
|
9 |
+
yarn-error.log*
|
10 |
+
|
11 |
+
# Build outputs
|
12 |
+
frontend/dist/
|
13 |
+
backend/dist/
|
14 |
+
*.tgz
|
15 |
+
|
16 |
+
# Environment files
|
17 |
+
.env
|
18 |
+
.env.local
|
19 |
+
.env.development.local
|
20 |
+
.env.test.local
|
21 |
+
.env.production.local
|
22 |
+
|
23 |
+
# IDE files
|
24 |
+
.vscode/
|
25 |
+
.idea/
|
26 |
+
*.swp
|
27 |
+
*.swo
|
28 |
+
*~
|
29 |
+
|
30 |
+
# OS files
|
31 |
+
.DS_Store
|
32 |
+
.DS_Store?
|
33 |
+
._*
|
34 |
+
.Spotlight-V100
|
35 |
+
.Trashes
|
36 |
+
ehthumbs.db
|
37 |
+
Thumbs.db
|
38 |
+
|
39 |
+
# Logs
|
40 |
+
logs
|
41 |
+
*.log
|
42 |
+
|
43 |
+
# Runtime data
|
44 |
+
pids
|
45 |
+
*.pid
|
46 |
+
*.seed
|
47 |
+
*.pid.lock
|
48 |
+
|
49 |
+
# Coverage
|
50 |
+
coverage/
|
51 |
+
*.lcov
|
52 |
+
|
53 |
+
# Cache
|
54 |
+
.npm
|
55 |
+
.eslintcache
|
56 |
+
.node_repl_history
|
57 |
+
|
58 |
+
# Temporary
|
59 |
+
tmp/
|
60 |
+
temp/
|
Dockerfile
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Multi-stage build for React + Node.js application
|
2 |
+
FROM node:18-alpine AS base
|
3 |
+
|
4 |
+
# Install dumb-init for proper signal handling
|
5 |
+
RUN apk add --no-cache dumb-init
|
6 |
+
|
7 |
+
# Create app directory
|
8 |
+
WORKDIR /app
|
9 |
+
|
10 |
+
# Copy package files
|
11 |
+
COPY package*.json ./
|
12 |
+
COPY backend/package*.json ./backend/
|
13 |
+
COPY frontend/package*.json ./frontend/
|
14 |
+
|
15 |
+
# Install dependencies
|
16 |
+
RUN npm ci --only=production && \
|
17 |
+
cd backend && npm ci --only=production && \
|
18 |
+
cd ../frontend && npm ci
|
19 |
+
|
20 |
+
# Frontend build stage
|
21 |
+
FROM base AS frontend-build
|
22 |
+
WORKDIR /app/frontend
|
23 |
+
COPY frontend/ .
|
24 |
+
RUN npm run build
|
25 |
+
|
26 |
+
# Production stage
|
27 |
+
FROM node:18-alpine AS production
|
28 |
+
|
29 |
+
# Install dumb-init
|
30 |
+
RUN apk add --no-cache dumb-init
|
31 |
+
|
32 |
+
# Create user with UID 1000 as required by HF Spaces
|
33 |
+
RUN addgroup -g 1000 -S user && \
|
34 |
+
adduser -S user -u 1000
|
35 |
+
|
36 |
+
# Set working directory
|
37 |
+
WORKDIR /app
|
38 |
+
|
39 |
+
# Copy backend dependencies and source
|
40 |
+
COPY --from=base --chown=user /app/backend/node_modules ./backend/node_modules
|
41 |
+
COPY --chown=user backend/ ./backend/
|
42 |
+
COPY --chown=user data/ ./data/
|
43 |
+
|
44 |
+
# Copy built frontend
|
45 |
+
COPY --from=frontend-build --chown=user /app/frontend/dist ./frontend/dist
|
46 |
+
|
47 |
+
# Switch to user
|
48 |
+
USER user
|
49 |
+
|
50 |
+
# Expose port
|
51 |
+
EXPOSE 3001
|
52 |
+
|
53 |
+
# Health check
|
54 |
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
55 |
+
CMD node -e "require('http').get('http://localhost:3001/api/algorithms', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) }).on('error', () => process.exit(1))"
|
56 |
+
|
57 |
+
# Start the application
|
58 |
+
CMD ["dumb-init", "node", "backend/server.js"]
|
README.md
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: PubMed AI Explorer
|
3 |
+
emoji: 🔬
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: purple
|
6 |
+
sdk: docker
|
7 |
+
app_port: 3001
|
8 |
+
---
|
9 |
+
|
10 |
+
# PubMed AI Explorer
|
11 |
+
|
12 |
+
A web application that helps users explore AI algorithms used in medical research papers from PubMed.
|
13 |
+
|
14 |
+
## Features
|
15 |
+
|
16 |
+
- **Dashboard**: View statistics and trends of AI algorithms used in medical papers
|
17 |
+
- **Algorithm Search**: Search for specific medical problems and see which algorithms are most commonly used
|
18 |
+
- **Interactive Charts**: Visual representation of algorithm usage and distribution
|
19 |
+
- **PubMed Integration**: Direct links to relevant papers on PubMed
|
20 |
+
- **Editable Algorithm Database**: JSON-based configuration for easy algorithm management
|
21 |
+
|
22 |
+
## Tech Stack
|
23 |
+
|
24 |
+
- **Frontend**: React + TypeScript + Vite + Tailwind CSS
|
25 |
+
- **Backend**: Node.js + Express
|
26 |
+
- **Charts**: Recharts
|
27 |
+
- **API**: PubMed E-utilities
|
28 |
+
- **Icons**: Lucide React
|
29 |
+
|
30 |
+
## Installation
|
31 |
+
|
32 |
+
1. Install dependencies for all parts:
|
33 |
+
```bash
|
34 |
+
npm run install:all
|
35 |
+
```
|
36 |
+
|
37 |
+
2. Start the development server:
|
38 |
+
```bash
|
39 |
+
npm run dev
|
40 |
+
```
|
41 |
+
|
42 |
+
This will start both the backend (port 3001) and frontend (port 3000) concurrently.
|
43 |
+
|
44 |
+
## Usage
|
45 |
+
|
46 |
+
1. **Dashboard**: Visit the homepage to see overall statistics of AI algorithms in medical research
|
47 |
+
2. **Search**: Use the search page to find algorithms used for specific medical problems
|
48 |
+
3. **Algorithm Management**: Edit `/data/algorithms.json` to add, remove, or modify algorithms and their synonyms
|
49 |
+
|
50 |
+
## Algorithm Configuration
|
51 |
+
|
52 |
+
The algorithms are configured in `/data/algorithms.json`. Each algorithm has:
|
53 |
+
- `name`: Display name
|
54 |
+
- `category`: Either "classical_ml" or "deep_learning"
|
55 |
+
- `description`: Brief description of the algorithm
|
56 |
+
- `synonyms`: Array of terms used to search PubMed (includes variations, abbreviations, etc.)
|
57 |
+
|
58 |
+
## API Endpoints
|
59 |
+
|
60 |
+
- `GET /api/algorithms` - Get all algorithms
|
61 |
+
- `PUT /api/algorithms` - Update algorithms configuration
|
62 |
+
- `GET /api/search/dashboard-stats` - Get dashboard statistics
|
63 |
+
- `POST /api/search/problem` - Search algorithms for a specific problem
|
64 |
+
- `GET /api/search/pubmed-link` - Generate PubMed search URL
|
65 |
+
- `GET /api/pubmed/search` - Search PubMed papers
|
66 |
+
- `GET /api/pubmed/paper/:pmid` - Get specific paper details
|
67 |
+
|
68 |
+
## Development
|
69 |
+
|
70 |
+
- Backend runs on port 3001
|
71 |
+
- Frontend runs on port 3000 with proxy to backend
|
72 |
+
- Hot reload enabled for both frontend and backend
|
backend/package-lock.json
ADDED
@@ -0,0 +1,1406 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "pubmed-ai-explorer-backend",
|
3 |
+
"version": "1.0.0",
|
4 |
+
"lockfileVersion": 3,
|
5 |
+
"requires": true,
|
6 |
+
"packages": {
|
7 |
+
"": {
|
8 |
+
"name": "pubmed-ai-explorer-backend",
|
9 |
+
"version": "1.0.0",
|
10 |
+
"dependencies": {
|
11 |
+
"axios": "^1.6.2",
|
12 |
+
"cors": "^2.8.5",
|
13 |
+
"dotenv": "^16.3.1",
|
14 |
+
"express": "^4.18.2",
|
15 |
+
"node-cache": "^5.1.2",
|
16 |
+
"xml2js": "^0.6.2"
|
17 |
+
},
|
18 |
+
"devDependencies": {
|
19 |
+
"nodemon": "^3.0.2"
|
20 |
+
}
|
21 |
+
},
|
22 |
+
"node_modules/accepts": {
|
23 |
+
"version": "1.3.8",
|
24 |
+
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
25 |
+
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
26 |
+
"license": "MIT",
|
27 |
+
"dependencies": {
|
28 |
+
"mime-types": "~2.1.34",
|
29 |
+
"negotiator": "0.6.3"
|
30 |
+
},
|
31 |
+
"engines": {
|
32 |
+
"node": ">= 0.6"
|
33 |
+
}
|
34 |
+
},
|
35 |
+
"node_modules/anymatch": {
|
36 |
+
"version": "3.1.3",
|
37 |
+
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
38 |
+
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
39 |
+
"dev": true,
|
40 |
+
"license": "ISC",
|
41 |
+
"dependencies": {
|
42 |
+
"normalize-path": "^3.0.0",
|
43 |
+
"picomatch": "^2.0.4"
|
44 |
+
},
|
45 |
+
"engines": {
|
46 |
+
"node": ">= 8"
|
47 |
+
}
|
48 |
+
},
|
49 |
+
"node_modules/array-flatten": {
|
50 |
+
"version": "1.1.1",
|
51 |
+
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
52 |
+
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
53 |
+
"license": "MIT"
|
54 |
+
},
|
55 |
+
"node_modules/asynckit": {
|
56 |
+
"version": "0.4.0",
|
57 |
+
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
58 |
+
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
59 |
+
"license": "MIT"
|
60 |
+
},
|
61 |
+
"node_modules/axios": {
|
62 |
+
"version": "1.9.0",
|
63 |
+
"resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
|
64 |
+
"integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
|
65 |
+
"license": "MIT",
|
66 |
+
"dependencies": {
|
67 |
+
"follow-redirects": "^1.15.6",
|
68 |
+
"form-data": "^4.0.0",
|
69 |
+
"proxy-from-env": "^1.1.0"
|
70 |
+
}
|
71 |
+
},
|
72 |
+
"node_modules/balanced-match": {
|
73 |
+
"version": "1.0.2",
|
74 |
+
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
75 |
+
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
76 |
+
"dev": true,
|
77 |
+
"license": "MIT"
|
78 |
+
},
|
79 |
+
"node_modules/binary-extensions": {
|
80 |
+
"version": "2.3.0",
|
81 |
+
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
82 |
+
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
83 |
+
"dev": true,
|
84 |
+
"license": "MIT",
|
85 |
+
"engines": {
|
86 |
+
"node": ">=8"
|
87 |
+
},
|
88 |
+
"funding": {
|
89 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
90 |
+
}
|
91 |
+
},
|
92 |
+
"node_modules/body-parser": {
|
93 |
+
"version": "1.20.3",
|
94 |
+
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
95 |
+
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
|
96 |
+
"license": "MIT",
|
97 |
+
"dependencies": {
|
98 |
+
"bytes": "3.1.2",
|
99 |
+
"content-type": "~1.0.5",
|
100 |
+
"debug": "2.6.9",
|
101 |
+
"depd": "2.0.0",
|
102 |
+
"destroy": "1.2.0",
|
103 |
+
"http-errors": "2.0.0",
|
104 |
+
"iconv-lite": "0.4.24",
|
105 |
+
"on-finished": "2.4.1",
|
106 |
+
"qs": "6.13.0",
|
107 |
+
"raw-body": "2.5.2",
|
108 |
+
"type-is": "~1.6.18",
|
109 |
+
"unpipe": "1.0.0"
|
110 |
+
},
|
111 |
+
"engines": {
|
112 |
+
"node": ">= 0.8",
|
113 |
+
"npm": "1.2.8000 || >= 1.4.16"
|
114 |
+
}
|
115 |
+
},
|
116 |
+
"node_modules/brace-expansion": {
|
117 |
+
"version": "1.1.11",
|
118 |
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
119 |
+
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
120 |
+
"dev": true,
|
121 |
+
"license": "MIT",
|
122 |
+
"dependencies": {
|
123 |
+
"balanced-match": "^1.0.0",
|
124 |
+
"concat-map": "0.0.1"
|
125 |
+
}
|
126 |
+
},
|
127 |
+
"node_modules/braces": {
|
128 |
+
"version": "3.0.3",
|
129 |
+
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
130 |
+
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
131 |
+
"dev": true,
|
132 |
+
"license": "MIT",
|
133 |
+
"dependencies": {
|
134 |
+
"fill-range": "^7.1.1"
|
135 |
+
},
|
136 |
+
"engines": {
|
137 |
+
"node": ">=8"
|
138 |
+
}
|
139 |
+
},
|
140 |
+
"node_modules/bytes": {
|
141 |
+
"version": "3.1.2",
|
142 |
+
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
143 |
+
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
144 |
+
"license": "MIT",
|
145 |
+
"engines": {
|
146 |
+
"node": ">= 0.8"
|
147 |
+
}
|
148 |
+
},
|
149 |
+
"node_modules/call-bind-apply-helpers": {
|
150 |
+
"version": "1.0.2",
|
151 |
+
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
152 |
+
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
153 |
+
"license": "MIT",
|
154 |
+
"dependencies": {
|
155 |
+
"es-errors": "^1.3.0",
|
156 |
+
"function-bind": "^1.1.2"
|
157 |
+
},
|
158 |
+
"engines": {
|
159 |
+
"node": ">= 0.4"
|
160 |
+
}
|
161 |
+
},
|
162 |
+
"node_modules/call-bound": {
|
163 |
+
"version": "1.0.4",
|
164 |
+
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
165 |
+
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
166 |
+
"license": "MIT",
|
167 |
+
"dependencies": {
|
168 |
+
"call-bind-apply-helpers": "^1.0.2",
|
169 |
+
"get-intrinsic": "^1.3.0"
|
170 |
+
},
|
171 |
+
"engines": {
|
172 |
+
"node": ">= 0.4"
|
173 |
+
},
|
174 |
+
"funding": {
|
175 |
+
"url": "https://github.com/sponsors/ljharb"
|
176 |
+
}
|
177 |
+
},
|
178 |
+
"node_modules/chokidar": {
|
179 |
+
"version": "3.6.0",
|
180 |
+
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
181 |
+
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
182 |
+
"dev": true,
|
183 |
+
"license": "MIT",
|
184 |
+
"dependencies": {
|
185 |
+
"anymatch": "~3.1.2",
|
186 |
+
"braces": "~3.0.2",
|
187 |
+
"glob-parent": "~5.1.2",
|
188 |
+
"is-binary-path": "~2.1.0",
|
189 |
+
"is-glob": "~4.0.1",
|
190 |
+
"normalize-path": "~3.0.0",
|
191 |
+
"readdirp": "~3.6.0"
|
192 |
+
},
|
193 |
+
"engines": {
|
194 |
+
"node": ">= 8.10.0"
|
195 |
+
},
|
196 |
+
"funding": {
|
197 |
+
"url": "https://paulmillr.com/funding/"
|
198 |
+
},
|
199 |
+
"optionalDependencies": {
|
200 |
+
"fsevents": "~2.3.2"
|
201 |
+
}
|
202 |
+
},
|
203 |
+
"node_modules/clone": {
|
204 |
+
"version": "2.1.2",
|
205 |
+
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
206 |
+
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
|
207 |
+
"license": "MIT",
|
208 |
+
"engines": {
|
209 |
+
"node": ">=0.8"
|
210 |
+
}
|
211 |
+
},
|
212 |
+
"node_modules/combined-stream": {
|
213 |
+
"version": "1.0.8",
|
214 |
+
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
215 |
+
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
216 |
+
"license": "MIT",
|
217 |
+
"dependencies": {
|
218 |
+
"delayed-stream": "~1.0.0"
|
219 |
+
},
|
220 |
+
"engines": {
|
221 |
+
"node": ">= 0.8"
|
222 |
+
}
|
223 |
+
},
|
224 |
+
"node_modules/concat-map": {
|
225 |
+
"version": "0.0.1",
|
226 |
+
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
227 |
+
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
228 |
+
"dev": true,
|
229 |
+
"license": "MIT"
|
230 |
+
},
|
231 |
+
"node_modules/content-disposition": {
|
232 |
+
"version": "0.5.4",
|
233 |
+
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
234 |
+
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
235 |
+
"license": "MIT",
|
236 |
+
"dependencies": {
|
237 |
+
"safe-buffer": "5.2.1"
|
238 |
+
},
|
239 |
+
"engines": {
|
240 |
+
"node": ">= 0.6"
|
241 |
+
}
|
242 |
+
},
|
243 |
+
"node_modules/content-type": {
|
244 |
+
"version": "1.0.5",
|
245 |
+
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
246 |
+
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
247 |
+
"license": "MIT",
|
248 |
+
"engines": {
|
249 |
+
"node": ">= 0.6"
|
250 |
+
}
|
251 |
+
},
|
252 |
+
"node_modules/cookie": {
|
253 |
+
"version": "0.7.1",
|
254 |
+
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
255 |
+
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
256 |
+
"license": "MIT",
|
257 |
+
"engines": {
|
258 |
+
"node": ">= 0.6"
|
259 |
+
}
|
260 |
+
},
|
261 |
+
"node_modules/cookie-signature": {
|
262 |
+
"version": "1.0.6",
|
263 |
+
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
264 |
+
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
265 |
+
"license": "MIT"
|
266 |
+
},
|
267 |
+
"node_modules/cors": {
|
268 |
+
"version": "2.8.5",
|
269 |
+
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
270 |
+
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
271 |
+
"license": "MIT",
|
272 |
+
"dependencies": {
|
273 |
+
"object-assign": "^4",
|
274 |
+
"vary": "^1"
|
275 |
+
},
|
276 |
+
"engines": {
|
277 |
+
"node": ">= 0.10"
|
278 |
+
}
|
279 |
+
},
|
280 |
+
"node_modules/debug": {
|
281 |
+
"version": "2.6.9",
|
282 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
283 |
+
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
284 |
+
"license": "MIT",
|
285 |
+
"dependencies": {
|
286 |
+
"ms": "2.0.0"
|
287 |
+
}
|
288 |
+
},
|
289 |
+
"node_modules/delayed-stream": {
|
290 |
+
"version": "1.0.0",
|
291 |
+
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
292 |
+
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
293 |
+
"license": "MIT",
|
294 |
+
"engines": {
|
295 |
+
"node": ">=0.4.0"
|
296 |
+
}
|
297 |
+
},
|
298 |
+
"node_modules/depd": {
|
299 |
+
"version": "2.0.0",
|
300 |
+
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
301 |
+
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
302 |
+
"license": "MIT",
|
303 |
+
"engines": {
|
304 |
+
"node": ">= 0.8"
|
305 |
+
}
|
306 |
+
},
|
307 |
+
"node_modules/destroy": {
|
308 |
+
"version": "1.2.0",
|
309 |
+
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
310 |
+
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
311 |
+
"license": "MIT",
|
312 |
+
"engines": {
|
313 |
+
"node": ">= 0.8",
|
314 |
+
"npm": "1.2.8000 || >= 1.4.16"
|
315 |
+
}
|
316 |
+
},
|
317 |
+
"node_modules/dotenv": {
|
318 |
+
"version": "16.5.0",
|
319 |
+
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
320 |
+
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
321 |
+
"license": "BSD-2-Clause",
|
322 |
+
"engines": {
|
323 |
+
"node": ">=12"
|
324 |
+
},
|
325 |
+
"funding": {
|
326 |
+
"url": "https://dotenvx.com"
|
327 |
+
}
|
328 |
+
},
|
329 |
+
"node_modules/dunder-proto": {
|
330 |
+
"version": "1.0.1",
|
331 |
+
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
332 |
+
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
333 |
+
"license": "MIT",
|
334 |
+
"dependencies": {
|
335 |
+
"call-bind-apply-helpers": "^1.0.1",
|
336 |
+
"es-errors": "^1.3.0",
|
337 |
+
"gopd": "^1.2.0"
|
338 |
+
},
|
339 |
+
"engines": {
|
340 |
+
"node": ">= 0.4"
|
341 |
+
}
|
342 |
+
},
|
343 |
+
"node_modules/ee-first": {
|
344 |
+
"version": "1.1.1",
|
345 |
+
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
346 |
+
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
347 |
+
"license": "MIT"
|
348 |
+
},
|
349 |
+
"node_modules/encodeurl": {
|
350 |
+
"version": "2.0.0",
|
351 |
+
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
352 |
+
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
353 |
+
"license": "MIT",
|
354 |
+
"engines": {
|
355 |
+
"node": ">= 0.8"
|
356 |
+
}
|
357 |
+
},
|
358 |
+
"node_modules/es-define-property": {
|
359 |
+
"version": "1.0.1",
|
360 |
+
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
361 |
+
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
362 |
+
"license": "MIT",
|
363 |
+
"engines": {
|
364 |
+
"node": ">= 0.4"
|
365 |
+
}
|
366 |
+
},
|
367 |
+
"node_modules/es-errors": {
|
368 |
+
"version": "1.3.0",
|
369 |
+
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
370 |
+
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
371 |
+
"license": "MIT",
|
372 |
+
"engines": {
|
373 |
+
"node": ">= 0.4"
|
374 |
+
}
|
375 |
+
},
|
376 |
+
"node_modules/es-object-atoms": {
|
377 |
+
"version": "1.1.1",
|
378 |
+
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
379 |
+
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
380 |
+
"license": "MIT",
|
381 |
+
"dependencies": {
|
382 |
+
"es-errors": "^1.3.0"
|
383 |
+
},
|
384 |
+
"engines": {
|
385 |
+
"node": ">= 0.4"
|
386 |
+
}
|
387 |
+
},
|
388 |
+
"node_modules/es-set-tostringtag": {
|
389 |
+
"version": "2.1.0",
|
390 |
+
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
391 |
+
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
392 |
+
"license": "MIT",
|
393 |
+
"dependencies": {
|
394 |
+
"es-errors": "^1.3.0",
|
395 |
+
"get-intrinsic": "^1.2.6",
|
396 |
+
"has-tostringtag": "^1.0.2",
|
397 |
+
"hasown": "^2.0.2"
|
398 |
+
},
|
399 |
+
"engines": {
|
400 |
+
"node": ">= 0.4"
|
401 |
+
}
|
402 |
+
},
|
403 |
+
"node_modules/escape-html": {
|
404 |
+
"version": "1.0.3",
|
405 |
+
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
406 |
+
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
407 |
+
"license": "MIT"
|
408 |
+
},
|
409 |
+
"node_modules/etag": {
|
410 |
+
"version": "1.8.1",
|
411 |
+
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
412 |
+
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
413 |
+
"license": "MIT",
|
414 |
+
"engines": {
|
415 |
+
"node": ">= 0.6"
|
416 |
+
}
|
417 |
+
},
|
418 |
+
"node_modules/express": {
|
419 |
+
"version": "4.21.2",
|
420 |
+
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
421 |
+
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
422 |
+
"license": "MIT",
|
423 |
+
"dependencies": {
|
424 |
+
"accepts": "~1.3.8",
|
425 |
+
"array-flatten": "1.1.1",
|
426 |
+
"body-parser": "1.20.3",
|
427 |
+
"content-disposition": "0.5.4",
|
428 |
+
"content-type": "~1.0.4",
|
429 |
+
"cookie": "0.7.1",
|
430 |
+
"cookie-signature": "1.0.6",
|
431 |
+
"debug": "2.6.9",
|
432 |
+
"depd": "2.0.0",
|
433 |
+
"encodeurl": "~2.0.0",
|
434 |
+
"escape-html": "~1.0.3",
|
435 |
+
"etag": "~1.8.1",
|
436 |
+
"finalhandler": "1.3.1",
|
437 |
+
"fresh": "0.5.2",
|
438 |
+
"http-errors": "2.0.0",
|
439 |
+
"merge-descriptors": "1.0.3",
|
440 |
+
"methods": "~1.1.2",
|
441 |
+
"on-finished": "2.4.1",
|
442 |
+
"parseurl": "~1.3.3",
|
443 |
+
"path-to-regexp": "0.1.12",
|
444 |
+
"proxy-addr": "~2.0.7",
|
445 |
+
"qs": "6.13.0",
|
446 |
+
"range-parser": "~1.2.1",
|
447 |
+
"safe-buffer": "5.2.1",
|
448 |
+
"send": "0.19.0",
|
449 |
+
"serve-static": "1.16.2",
|
450 |
+
"setprototypeof": "1.2.0",
|
451 |
+
"statuses": "2.0.1",
|
452 |
+
"type-is": "~1.6.18",
|
453 |
+
"utils-merge": "1.0.1",
|
454 |
+
"vary": "~1.1.2"
|
455 |
+
},
|
456 |
+
"engines": {
|
457 |
+
"node": ">= 0.10.0"
|
458 |
+
},
|
459 |
+
"funding": {
|
460 |
+
"type": "opencollective",
|
461 |
+
"url": "https://opencollective.com/express"
|
462 |
+
}
|
463 |
+
},
|
464 |
+
"node_modules/fill-range": {
|
465 |
+
"version": "7.1.1",
|
466 |
+
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
467 |
+
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
468 |
+
"dev": true,
|
469 |
+
"license": "MIT",
|
470 |
+
"dependencies": {
|
471 |
+
"to-regex-range": "^5.0.1"
|
472 |
+
},
|
473 |
+
"engines": {
|
474 |
+
"node": ">=8"
|
475 |
+
}
|
476 |
+
},
|
477 |
+
"node_modules/finalhandler": {
|
478 |
+
"version": "1.3.1",
|
479 |
+
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
|
480 |
+
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
|
481 |
+
"license": "MIT",
|
482 |
+
"dependencies": {
|
483 |
+
"debug": "2.6.9",
|
484 |
+
"encodeurl": "~2.0.0",
|
485 |
+
"escape-html": "~1.0.3",
|
486 |
+
"on-finished": "2.4.1",
|
487 |
+
"parseurl": "~1.3.3",
|
488 |
+
"statuses": "2.0.1",
|
489 |
+
"unpipe": "~1.0.0"
|
490 |
+
},
|
491 |
+
"engines": {
|
492 |
+
"node": ">= 0.8"
|
493 |
+
}
|
494 |
+
},
|
495 |
+
"node_modules/follow-redirects": {
|
496 |
+
"version": "1.15.9",
|
497 |
+
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
498 |
+
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
499 |
+
"funding": [
|
500 |
+
{
|
501 |
+
"type": "individual",
|
502 |
+
"url": "https://github.com/sponsors/RubenVerborgh"
|
503 |
+
}
|
504 |
+
],
|
505 |
+
"license": "MIT",
|
506 |
+
"engines": {
|
507 |
+
"node": ">=4.0"
|
508 |
+
},
|
509 |
+
"peerDependenciesMeta": {
|
510 |
+
"debug": {
|
511 |
+
"optional": true
|
512 |
+
}
|
513 |
+
}
|
514 |
+
},
|
515 |
+
"node_modules/form-data": {
|
516 |
+
"version": "4.0.3",
|
517 |
+
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz",
|
518 |
+
"integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==",
|
519 |
+
"license": "MIT",
|
520 |
+
"dependencies": {
|
521 |
+
"asynckit": "^0.4.0",
|
522 |
+
"combined-stream": "^1.0.8",
|
523 |
+
"es-set-tostringtag": "^2.1.0",
|
524 |
+
"hasown": "^2.0.2",
|
525 |
+
"mime-types": "^2.1.12"
|
526 |
+
},
|
527 |
+
"engines": {
|
528 |
+
"node": ">= 6"
|
529 |
+
}
|
530 |
+
},
|
531 |
+
"node_modules/forwarded": {
|
532 |
+
"version": "0.2.0",
|
533 |
+
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
534 |
+
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
535 |
+
"license": "MIT",
|
536 |
+
"engines": {
|
537 |
+
"node": ">= 0.6"
|
538 |
+
}
|
539 |
+
},
|
540 |
+
"node_modules/fresh": {
|
541 |
+
"version": "0.5.2",
|
542 |
+
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
543 |
+
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
544 |
+
"license": "MIT",
|
545 |
+
"engines": {
|
546 |
+
"node": ">= 0.6"
|
547 |
+
}
|
548 |
+
},
|
549 |
+
"node_modules/fsevents": {
|
550 |
+
"version": "2.3.3",
|
551 |
+
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
552 |
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
553 |
+
"dev": true,
|
554 |
+
"hasInstallScript": true,
|
555 |
+
"license": "MIT",
|
556 |
+
"optional": true,
|
557 |
+
"os": [
|
558 |
+
"darwin"
|
559 |
+
],
|
560 |
+
"engines": {
|
561 |
+
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
562 |
+
}
|
563 |
+
},
|
564 |
+
"node_modules/function-bind": {
|
565 |
+
"version": "1.1.2",
|
566 |
+
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
567 |
+
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
568 |
+
"license": "MIT",
|
569 |
+
"funding": {
|
570 |
+
"url": "https://github.com/sponsors/ljharb"
|
571 |
+
}
|
572 |
+
},
|
573 |
+
"node_modules/get-intrinsic": {
|
574 |
+
"version": "1.3.0",
|
575 |
+
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
576 |
+
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
577 |
+
"license": "MIT",
|
578 |
+
"dependencies": {
|
579 |
+
"call-bind-apply-helpers": "^1.0.2",
|
580 |
+
"es-define-property": "^1.0.1",
|
581 |
+
"es-errors": "^1.3.0",
|
582 |
+
"es-object-atoms": "^1.1.1",
|
583 |
+
"function-bind": "^1.1.2",
|
584 |
+
"get-proto": "^1.0.1",
|
585 |
+
"gopd": "^1.2.0",
|
586 |
+
"has-symbols": "^1.1.0",
|
587 |
+
"hasown": "^2.0.2",
|
588 |
+
"math-intrinsics": "^1.1.0"
|
589 |
+
},
|
590 |
+
"engines": {
|
591 |
+
"node": ">= 0.4"
|
592 |
+
},
|
593 |
+
"funding": {
|
594 |
+
"url": "https://github.com/sponsors/ljharb"
|
595 |
+
}
|
596 |
+
},
|
597 |
+
"node_modules/get-proto": {
|
598 |
+
"version": "1.0.1",
|
599 |
+
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
600 |
+
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
601 |
+
"license": "MIT",
|
602 |
+
"dependencies": {
|
603 |
+
"dunder-proto": "^1.0.1",
|
604 |
+
"es-object-atoms": "^1.0.0"
|
605 |
+
},
|
606 |
+
"engines": {
|
607 |
+
"node": ">= 0.4"
|
608 |
+
}
|
609 |
+
},
|
610 |
+
"node_modules/glob-parent": {
|
611 |
+
"version": "5.1.2",
|
612 |
+
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
613 |
+
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
614 |
+
"dev": true,
|
615 |
+
"license": "ISC",
|
616 |
+
"dependencies": {
|
617 |
+
"is-glob": "^4.0.1"
|
618 |
+
},
|
619 |
+
"engines": {
|
620 |
+
"node": ">= 6"
|
621 |
+
}
|
622 |
+
},
|
623 |
+
"node_modules/gopd": {
|
624 |
+
"version": "1.2.0",
|
625 |
+
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
626 |
+
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
627 |
+
"license": "MIT",
|
628 |
+
"engines": {
|
629 |
+
"node": ">= 0.4"
|
630 |
+
},
|
631 |
+
"funding": {
|
632 |
+
"url": "https://github.com/sponsors/ljharb"
|
633 |
+
}
|
634 |
+
},
|
635 |
+
"node_modules/has-flag": {
|
636 |
+
"version": "3.0.0",
|
637 |
+
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
638 |
+
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
639 |
+
"dev": true,
|
640 |
+
"license": "MIT",
|
641 |
+
"engines": {
|
642 |
+
"node": ">=4"
|
643 |
+
}
|
644 |
+
},
|
645 |
+
"node_modules/has-symbols": {
|
646 |
+
"version": "1.1.0",
|
647 |
+
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
648 |
+
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
649 |
+
"license": "MIT",
|
650 |
+
"engines": {
|
651 |
+
"node": ">= 0.4"
|
652 |
+
},
|
653 |
+
"funding": {
|
654 |
+
"url": "https://github.com/sponsors/ljharb"
|
655 |
+
}
|
656 |
+
},
|
657 |
+
"node_modules/has-tostringtag": {
|
658 |
+
"version": "1.0.2",
|
659 |
+
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
660 |
+
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
661 |
+
"license": "MIT",
|
662 |
+
"dependencies": {
|
663 |
+
"has-symbols": "^1.0.3"
|
664 |
+
},
|
665 |
+
"engines": {
|
666 |
+
"node": ">= 0.4"
|
667 |
+
},
|
668 |
+
"funding": {
|
669 |
+
"url": "https://github.com/sponsors/ljharb"
|
670 |
+
}
|
671 |
+
},
|
672 |
+
"node_modules/hasown": {
|
673 |
+
"version": "2.0.2",
|
674 |
+
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
675 |
+
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
676 |
+
"license": "MIT",
|
677 |
+
"dependencies": {
|
678 |
+
"function-bind": "^1.1.2"
|
679 |
+
},
|
680 |
+
"engines": {
|
681 |
+
"node": ">= 0.4"
|
682 |
+
}
|
683 |
+
},
|
684 |
+
"node_modules/http-errors": {
|
685 |
+
"version": "2.0.0",
|
686 |
+
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
687 |
+
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
688 |
+
"license": "MIT",
|
689 |
+
"dependencies": {
|
690 |
+
"depd": "2.0.0",
|
691 |
+
"inherits": "2.0.4",
|
692 |
+
"setprototypeof": "1.2.0",
|
693 |
+
"statuses": "2.0.1",
|
694 |
+
"toidentifier": "1.0.1"
|
695 |
+
},
|
696 |
+
"engines": {
|
697 |
+
"node": ">= 0.8"
|
698 |
+
}
|
699 |
+
},
|
700 |
+
"node_modules/iconv-lite": {
|
701 |
+
"version": "0.4.24",
|
702 |
+
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
703 |
+
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
704 |
+
"license": "MIT",
|
705 |
+
"dependencies": {
|
706 |
+
"safer-buffer": ">= 2.1.2 < 3"
|
707 |
+
},
|
708 |
+
"engines": {
|
709 |
+
"node": ">=0.10.0"
|
710 |
+
}
|
711 |
+
},
|
712 |
+
"node_modules/ignore-by-default": {
|
713 |
+
"version": "1.0.1",
|
714 |
+
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
715 |
+
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
|
716 |
+
"dev": true,
|
717 |
+
"license": "ISC"
|
718 |
+
},
|
719 |
+
"node_modules/inherits": {
|
720 |
+
"version": "2.0.4",
|
721 |
+
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
722 |
+
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
723 |
+
"license": "ISC"
|
724 |
+
},
|
725 |
+
"node_modules/ipaddr.js": {
|
726 |
+
"version": "1.9.1",
|
727 |
+
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
728 |
+
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
729 |
+
"license": "MIT",
|
730 |
+
"engines": {
|
731 |
+
"node": ">= 0.10"
|
732 |
+
}
|
733 |
+
},
|
734 |
+
"node_modules/is-binary-path": {
|
735 |
+
"version": "2.1.0",
|
736 |
+
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
737 |
+
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
738 |
+
"dev": true,
|
739 |
+
"license": "MIT",
|
740 |
+
"dependencies": {
|
741 |
+
"binary-extensions": "^2.0.0"
|
742 |
+
},
|
743 |
+
"engines": {
|
744 |
+
"node": ">=8"
|
745 |
+
}
|
746 |
+
},
|
747 |
+
"node_modules/is-extglob": {
|
748 |
+
"version": "2.1.1",
|
749 |
+
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
750 |
+
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
751 |
+
"dev": true,
|
752 |
+
"license": "MIT",
|
753 |
+
"engines": {
|
754 |
+
"node": ">=0.10.0"
|
755 |
+
}
|
756 |
+
},
|
757 |
+
"node_modules/is-glob": {
|
758 |
+
"version": "4.0.3",
|
759 |
+
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
760 |
+
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
761 |
+
"dev": true,
|
762 |
+
"license": "MIT",
|
763 |
+
"dependencies": {
|
764 |
+
"is-extglob": "^2.1.1"
|
765 |
+
},
|
766 |
+
"engines": {
|
767 |
+
"node": ">=0.10.0"
|
768 |
+
}
|
769 |
+
},
|
770 |
+
"node_modules/is-number": {
|
771 |
+
"version": "7.0.0",
|
772 |
+
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
773 |
+
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
774 |
+
"dev": true,
|
775 |
+
"license": "MIT",
|
776 |
+
"engines": {
|
777 |
+
"node": ">=0.12.0"
|
778 |
+
}
|
779 |
+
},
|
780 |
+
"node_modules/math-intrinsics": {
|
781 |
+
"version": "1.1.0",
|
782 |
+
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
783 |
+
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
784 |
+
"license": "MIT",
|
785 |
+
"engines": {
|
786 |
+
"node": ">= 0.4"
|
787 |
+
}
|
788 |
+
},
|
789 |
+
"node_modules/media-typer": {
|
790 |
+
"version": "0.3.0",
|
791 |
+
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
792 |
+
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
793 |
+
"license": "MIT",
|
794 |
+
"engines": {
|
795 |
+
"node": ">= 0.6"
|
796 |
+
}
|
797 |
+
},
|
798 |
+
"node_modules/merge-descriptors": {
|
799 |
+
"version": "1.0.3",
|
800 |
+
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
801 |
+
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
802 |
+
"license": "MIT",
|
803 |
+
"funding": {
|
804 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
805 |
+
}
|
806 |
+
},
|
807 |
+
"node_modules/methods": {
|
808 |
+
"version": "1.1.2",
|
809 |
+
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
810 |
+
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
811 |
+
"license": "MIT",
|
812 |
+
"engines": {
|
813 |
+
"node": ">= 0.6"
|
814 |
+
}
|
815 |
+
},
|
816 |
+
"node_modules/mime": {
|
817 |
+
"version": "1.6.0",
|
818 |
+
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
819 |
+
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
820 |
+
"license": "MIT",
|
821 |
+
"bin": {
|
822 |
+
"mime": "cli.js"
|
823 |
+
},
|
824 |
+
"engines": {
|
825 |
+
"node": ">=4"
|
826 |
+
}
|
827 |
+
},
|
828 |
+
"node_modules/mime-db": {
|
829 |
+
"version": "1.52.0",
|
830 |
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
831 |
+
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
832 |
+
"license": "MIT",
|
833 |
+
"engines": {
|
834 |
+
"node": ">= 0.6"
|
835 |
+
}
|
836 |
+
},
|
837 |
+
"node_modules/mime-types": {
|
838 |
+
"version": "2.1.35",
|
839 |
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
840 |
+
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
841 |
+
"license": "MIT",
|
842 |
+
"dependencies": {
|
843 |
+
"mime-db": "1.52.0"
|
844 |
+
},
|
845 |
+
"engines": {
|
846 |
+
"node": ">= 0.6"
|
847 |
+
}
|
848 |
+
},
|
849 |
+
"node_modules/minimatch": {
|
850 |
+
"version": "3.1.2",
|
851 |
+
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
852 |
+
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
853 |
+
"dev": true,
|
854 |
+
"license": "ISC",
|
855 |
+
"dependencies": {
|
856 |
+
"brace-expansion": "^1.1.7"
|
857 |
+
},
|
858 |
+
"engines": {
|
859 |
+
"node": "*"
|
860 |
+
}
|
861 |
+
},
|
862 |
+
"node_modules/ms": {
|
863 |
+
"version": "2.0.0",
|
864 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
865 |
+
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
866 |
+
"license": "MIT"
|
867 |
+
},
|
868 |
+
"node_modules/negotiator": {
|
869 |
+
"version": "0.6.3",
|
870 |
+
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
871 |
+
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
872 |
+
"license": "MIT",
|
873 |
+
"engines": {
|
874 |
+
"node": ">= 0.6"
|
875 |
+
}
|
876 |
+
},
|
877 |
+
"node_modules/node-cache": {
|
878 |
+
"version": "5.1.2",
|
879 |
+
"resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz",
|
880 |
+
"integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==",
|
881 |
+
"license": "MIT",
|
882 |
+
"dependencies": {
|
883 |
+
"clone": "2.x"
|
884 |
+
},
|
885 |
+
"engines": {
|
886 |
+
"node": ">= 8.0.0"
|
887 |
+
}
|
888 |
+
},
|
889 |
+
"node_modules/nodemon": {
|
890 |
+
"version": "3.1.10",
|
891 |
+
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
|
892 |
+
"integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==",
|
893 |
+
"dev": true,
|
894 |
+
"license": "MIT",
|
895 |
+
"dependencies": {
|
896 |
+
"chokidar": "^3.5.2",
|
897 |
+
"debug": "^4",
|
898 |
+
"ignore-by-default": "^1.0.1",
|
899 |
+
"minimatch": "^3.1.2",
|
900 |
+
"pstree.remy": "^1.1.8",
|
901 |
+
"semver": "^7.5.3",
|
902 |
+
"simple-update-notifier": "^2.0.0",
|
903 |
+
"supports-color": "^5.5.0",
|
904 |
+
"touch": "^3.1.0",
|
905 |
+
"undefsafe": "^2.0.5"
|
906 |
+
},
|
907 |
+
"bin": {
|
908 |
+
"nodemon": "bin/nodemon.js"
|
909 |
+
},
|
910 |
+
"engines": {
|
911 |
+
"node": ">=10"
|
912 |
+
},
|
913 |
+
"funding": {
|
914 |
+
"type": "opencollective",
|
915 |
+
"url": "https://opencollective.com/nodemon"
|
916 |
+
}
|
917 |
+
},
|
918 |
+
"node_modules/nodemon/node_modules/debug": {
|
919 |
+
"version": "4.4.1",
|
920 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
921 |
+
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
922 |
+
"dev": true,
|
923 |
+
"license": "MIT",
|
924 |
+
"dependencies": {
|
925 |
+
"ms": "^2.1.3"
|
926 |
+
},
|
927 |
+
"engines": {
|
928 |
+
"node": ">=6.0"
|
929 |
+
},
|
930 |
+
"peerDependenciesMeta": {
|
931 |
+
"supports-color": {
|
932 |
+
"optional": true
|
933 |
+
}
|
934 |
+
}
|
935 |
+
},
|
936 |
+
"node_modules/nodemon/node_modules/ms": {
|
937 |
+
"version": "2.1.3",
|
938 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
939 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
940 |
+
"dev": true,
|
941 |
+
"license": "MIT"
|
942 |
+
},
|
943 |
+
"node_modules/normalize-path": {
|
944 |
+
"version": "3.0.0",
|
945 |
+
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
946 |
+
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
947 |
+
"dev": true,
|
948 |
+
"license": "MIT",
|
949 |
+
"engines": {
|
950 |
+
"node": ">=0.10.0"
|
951 |
+
}
|
952 |
+
},
|
953 |
+
"node_modules/object-assign": {
|
954 |
+
"version": "4.1.1",
|
955 |
+
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
956 |
+
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
957 |
+
"license": "MIT",
|
958 |
+
"engines": {
|
959 |
+
"node": ">=0.10.0"
|
960 |
+
}
|
961 |
+
},
|
962 |
+
"node_modules/object-inspect": {
|
963 |
+
"version": "1.13.4",
|
964 |
+
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
965 |
+
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
966 |
+
"license": "MIT",
|
967 |
+
"engines": {
|
968 |
+
"node": ">= 0.4"
|
969 |
+
},
|
970 |
+
"funding": {
|
971 |
+
"url": "https://github.com/sponsors/ljharb"
|
972 |
+
}
|
973 |
+
},
|
974 |
+
"node_modules/on-finished": {
|
975 |
+
"version": "2.4.1",
|
976 |
+
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
977 |
+
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
978 |
+
"license": "MIT",
|
979 |
+
"dependencies": {
|
980 |
+
"ee-first": "1.1.1"
|
981 |
+
},
|
982 |
+
"engines": {
|
983 |
+
"node": ">= 0.8"
|
984 |
+
}
|
985 |
+
},
|
986 |
+
"node_modules/parseurl": {
|
987 |
+
"version": "1.3.3",
|
988 |
+
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
989 |
+
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
990 |
+
"license": "MIT",
|
991 |
+
"engines": {
|
992 |
+
"node": ">= 0.8"
|
993 |
+
}
|
994 |
+
},
|
995 |
+
"node_modules/path-to-regexp": {
|
996 |
+
"version": "0.1.12",
|
997 |
+
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
998 |
+
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
999 |
+
"license": "MIT"
|
1000 |
+
},
|
1001 |
+
"node_modules/picomatch": {
|
1002 |
+
"version": "2.3.1",
|
1003 |
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
1004 |
+
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
1005 |
+
"dev": true,
|
1006 |
+
"license": "MIT",
|
1007 |
+
"engines": {
|
1008 |
+
"node": ">=8.6"
|
1009 |
+
},
|
1010 |
+
"funding": {
|
1011 |
+
"url": "https://github.com/sponsors/jonschlinkert"
|
1012 |
+
}
|
1013 |
+
},
|
1014 |
+
"node_modules/proxy-addr": {
|
1015 |
+
"version": "2.0.7",
|
1016 |
+
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
1017 |
+
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
1018 |
+
"license": "MIT",
|
1019 |
+
"dependencies": {
|
1020 |
+
"forwarded": "0.2.0",
|
1021 |
+
"ipaddr.js": "1.9.1"
|
1022 |
+
},
|
1023 |
+
"engines": {
|
1024 |
+
"node": ">= 0.10"
|
1025 |
+
}
|
1026 |
+
},
|
1027 |
+
"node_modules/proxy-from-env": {
|
1028 |
+
"version": "1.1.0",
|
1029 |
+
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
1030 |
+
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
1031 |
+
"license": "MIT"
|
1032 |
+
},
|
1033 |
+
"node_modules/pstree.remy": {
|
1034 |
+
"version": "1.1.8",
|
1035 |
+
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
|
1036 |
+
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
|
1037 |
+
"dev": true,
|
1038 |
+
"license": "MIT"
|
1039 |
+
},
|
1040 |
+
"node_modules/qs": {
|
1041 |
+
"version": "6.13.0",
|
1042 |
+
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
1043 |
+
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
1044 |
+
"license": "BSD-3-Clause",
|
1045 |
+
"dependencies": {
|
1046 |
+
"side-channel": "^1.0.6"
|
1047 |
+
},
|
1048 |
+
"engines": {
|
1049 |
+
"node": ">=0.6"
|
1050 |
+
},
|
1051 |
+
"funding": {
|
1052 |
+
"url": "https://github.com/sponsors/ljharb"
|
1053 |
+
}
|
1054 |
+
},
|
1055 |
+
"node_modules/range-parser": {
|
1056 |
+
"version": "1.2.1",
|
1057 |
+
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
1058 |
+
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
1059 |
+
"license": "MIT",
|
1060 |
+
"engines": {
|
1061 |
+
"node": ">= 0.6"
|
1062 |
+
}
|
1063 |
+
},
|
1064 |
+
"node_modules/raw-body": {
|
1065 |
+
"version": "2.5.2",
|
1066 |
+
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
1067 |
+
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
1068 |
+
"license": "MIT",
|
1069 |
+
"dependencies": {
|
1070 |
+
"bytes": "3.1.2",
|
1071 |
+
"http-errors": "2.0.0",
|
1072 |
+
"iconv-lite": "0.4.24",
|
1073 |
+
"unpipe": "1.0.0"
|
1074 |
+
},
|
1075 |
+
"engines": {
|
1076 |
+
"node": ">= 0.8"
|
1077 |
+
}
|
1078 |
+
},
|
1079 |
+
"node_modules/readdirp": {
|
1080 |
+
"version": "3.6.0",
|
1081 |
+
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
1082 |
+
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
1083 |
+
"dev": true,
|
1084 |
+
"license": "MIT",
|
1085 |
+
"dependencies": {
|
1086 |
+
"picomatch": "^2.2.1"
|
1087 |
+
},
|
1088 |
+
"engines": {
|
1089 |
+
"node": ">=8.10.0"
|
1090 |
+
}
|
1091 |
+
},
|
1092 |
+
"node_modules/safe-buffer": {
|
1093 |
+
"version": "5.2.1",
|
1094 |
+
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
1095 |
+
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
1096 |
+
"funding": [
|
1097 |
+
{
|
1098 |
+
"type": "github",
|
1099 |
+
"url": "https://github.com/sponsors/feross"
|
1100 |
+
},
|
1101 |
+
{
|
1102 |
+
"type": "patreon",
|
1103 |
+
"url": "https://www.patreon.com/feross"
|
1104 |
+
},
|
1105 |
+
{
|
1106 |
+
"type": "consulting",
|
1107 |
+
"url": "https://feross.org/support"
|
1108 |
+
}
|
1109 |
+
],
|
1110 |
+
"license": "MIT"
|
1111 |
+
},
|
1112 |
+
"node_modules/safer-buffer": {
|
1113 |
+
"version": "2.1.2",
|
1114 |
+
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
1115 |
+
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
1116 |
+
"license": "MIT"
|
1117 |
+
},
|
1118 |
+
"node_modules/sax": {
|
1119 |
+
"version": "1.4.1",
|
1120 |
+
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
|
1121 |
+
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
|
1122 |
+
"license": "ISC"
|
1123 |
+
},
|
1124 |
+
"node_modules/semver": {
|
1125 |
+
"version": "7.7.2",
|
1126 |
+
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
1127 |
+
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
1128 |
+
"dev": true,
|
1129 |
+
"license": "ISC",
|
1130 |
+
"bin": {
|
1131 |
+
"semver": "bin/semver.js"
|
1132 |
+
},
|
1133 |
+
"engines": {
|
1134 |
+
"node": ">=10"
|
1135 |
+
}
|
1136 |
+
},
|
1137 |
+
"node_modules/send": {
|
1138 |
+
"version": "0.19.0",
|
1139 |
+
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
|
1140 |
+
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
|
1141 |
+
"license": "MIT",
|
1142 |
+
"dependencies": {
|
1143 |
+
"debug": "2.6.9",
|
1144 |
+
"depd": "2.0.0",
|
1145 |
+
"destroy": "1.2.0",
|
1146 |
+
"encodeurl": "~1.0.2",
|
1147 |
+
"escape-html": "~1.0.3",
|
1148 |
+
"etag": "~1.8.1",
|
1149 |
+
"fresh": "0.5.2",
|
1150 |
+
"http-errors": "2.0.0",
|
1151 |
+
"mime": "1.6.0",
|
1152 |
+
"ms": "2.1.3",
|
1153 |
+
"on-finished": "2.4.1",
|
1154 |
+
"range-parser": "~1.2.1",
|
1155 |
+
"statuses": "2.0.1"
|
1156 |
+
},
|
1157 |
+
"engines": {
|
1158 |
+
"node": ">= 0.8.0"
|
1159 |
+
}
|
1160 |
+
},
|
1161 |
+
"node_modules/send/node_modules/encodeurl": {
|
1162 |
+
"version": "1.0.2",
|
1163 |
+
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
1164 |
+
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
1165 |
+
"license": "MIT",
|
1166 |
+
"engines": {
|
1167 |
+
"node": ">= 0.8"
|
1168 |
+
}
|
1169 |
+
},
|
1170 |
+
"node_modules/send/node_modules/ms": {
|
1171 |
+
"version": "2.1.3",
|
1172 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
1173 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
1174 |
+
"license": "MIT"
|
1175 |
+
},
|
1176 |
+
"node_modules/serve-static": {
|
1177 |
+
"version": "1.16.2",
|
1178 |
+
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
1179 |
+
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
|
1180 |
+
"license": "MIT",
|
1181 |
+
"dependencies": {
|
1182 |
+
"encodeurl": "~2.0.0",
|
1183 |
+
"escape-html": "~1.0.3",
|
1184 |
+
"parseurl": "~1.3.3",
|
1185 |
+
"send": "0.19.0"
|
1186 |
+
},
|
1187 |
+
"engines": {
|
1188 |
+
"node": ">= 0.8.0"
|
1189 |
+
}
|
1190 |
+
},
|
1191 |
+
"node_modules/setprototypeof": {
|
1192 |
+
"version": "1.2.0",
|
1193 |
+
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
1194 |
+
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
1195 |
+
"license": "ISC"
|
1196 |
+
},
|
1197 |
+
"node_modules/side-channel": {
|
1198 |
+
"version": "1.1.0",
|
1199 |
+
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
1200 |
+
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
1201 |
+
"license": "MIT",
|
1202 |
+
"dependencies": {
|
1203 |
+
"es-errors": "^1.3.0",
|
1204 |
+
"object-inspect": "^1.13.3",
|
1205 |
+
"side-channel-list": "^1.0.0",
|
1206 |
+
"side-channel-map": "^1.0.1",
|
1207 |
+
"side-channel-weakmap": "^1.0.2"
|
1208 |
+
},
|
1209 |
+
"engines": {
|
1210 |
+
"node": ">= 0.4"
|
1211 |
+
},
|
1212 |
+
"funding": {
|
1213 |
+
"url": "https://github.com/sponsors/ljharb"
|
1214 |
+
}
|
1215 |
+
},
|
1216 |
+
"node_modules/side-channel-list": {
|
1217 |
+
"version": "1.0.0",
|
1218 |
+
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
1219 |
+
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
1220 |
+
"license": "MIT",
|
1221 |
+
"dependencies": {
|
1222 |
+
"es-errors": "^1.3.0",
|
1223 |
+
"object-inspect": "^1.13.3"
|
1224 |
+
},
|
1225 |
+
"engines": {
|
1226 |
+
"node": ">= 0.4"
|
1227 |
+
},
|
1228 |
+
"funding": {
|
1229 |
+
"url": "https://github.com/sponsors/ljharb"
|
1230 |
+
}
|
1231 |
+
},
|
1232 |
+
"node_modules/side-channel-map": {
|
1233 |
+
"version": "1.0.1",
|
1234 |
+
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
1235 |
+
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
1236 |
+
"license": "MIT",
|
1237 |
+
"dependencies": {
|
1238 |
+
"call-bound": "^1.0.2",
|
1239 |
+
"es-errors": "^1.3.0",
|
1240 |
+
"get-intrinsic": "^1.2.5",
|
1241 |
+
"object-inspect": "^1.13.3"
|
1242 |
+
},
|
1243 |
+
"engines": {
|
1244 |
+
"node": ">= 0.4"
|
1245 |
+
},
|
1246 |
+
"funding": {
|
1247 |
+
"url": "https://github.com/sponsors/ljharb"
|
1248 |
+
}
|
1249 |
+
},
|
1250 |
+
"node_modules/side-channel-weakmap": {
|
1251 |
+
"version": "1.0.2",
|
1252 |
+
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
1253 |
+
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
1254 |
+
"license": "MIT",
|
1255 |
+
"dependencies": {
|
1256 |
+
"call-bound": "^1.0.2",
|
1257 |
+
"es-errors": "^1.3.0",
|
1258 |
+
"get-intrinsic": "^1.2.5",
|
1259 |
+
"object-inspect": "^1.13.3",
|
1260 |
+
"side-channel-map": "^1.0.1"
|
1261 |
+
},
|
1262 |
+
"engines": {
|
1263 |
+
"node": ">= 0.4"
|
1264 |
+
},
|
1265 |
+
"funding": {
|
1266 |
+
"url": "https://github.com/sponsors/ljharb"
|
1267 |
+
}
|
1268 |
+
},
|
1269 |
+
"node_modules/simple-update-notifier": {
|
1270 |
+
"version": "2.0.0",
|
1271 |
+
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
1272 |
+
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
|
1273 |
+
"dev": true,
|
1274 |
+
"license": "MIT",
|
1275 |
+
"dependencies": {
|
1276 |
+
"semver": "^7.5.3"
|
1277 |
+
},
|
1278 |
+
"engines": {
|
1279 |
+
"node": ">=10"
|
1280 |
+
}
|
1281 |
+
},
|
1282 |
+
"node_modules/statuses": {
|
1283 |
+
"version": "2.0.1",
|
1284 |
+
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
1285 |
+
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
1286 |
+
"license": "MIT",
|
1287 |
+
"engines": {
|
1288 |
+
"node": ">= 0.8"
|
1289 |
+
}
|
1290 |
+
},
|
1291 |
+
"node_modules/supports-color": {
|
1292 |
+
"version": "5.5.0",
|
1293 |
+
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
1294 |
+
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
1295 |
+
"dev": true,
|
1296 |
+
"license": "MIT",
|
1297 |
+
"dependencies": {
|
1298 |
+
"has-flag": "^3.0.0"
|
1299 |
+
},
|
1300 |
+
"engines": {
|
1301 |
+
"node": ">=4"
|
1302 |
+
}
|
1303 |
+
},
|
1304 |
+
"node_modules/to-regex-range": {
|
1305 |
+
"version": "5.0.1",
|
1306 |
+
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
1307 |
+
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
1308 |
+
"dev": true,
|
1309 |
+
"license": "MIT",
|
1310 |
+
"dependencies": {
|
1311 |
+
"is-number": "^7.0.0"
|
1312 |
+
},
|
1313 |
+
"engines": {
|
1314 |
+
"node": ">=8.0"
|
1315 |
+
}
|
1316 |
+
},
|
1317 |
+
"node_modules/toidentifier": {
|
1318 |
+
"version": "1.0.1",
|
1319 |
+
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
1320 |
+
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
1321 |
+
"license": "MIT",
|
1322 |
+
"engines": {
|
1323 |
+
"node": ">=0.6"
|
1324 |
+
}
|
1325 |
+
},
|
1326 |
+
"node_modules/touch": {
|
1327 |
+
"version": "3.1.1",
|
1328 |
+
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
|
1329 |
+
"integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
|
1330 |
+
"dev": true,
|
1331 |
+
"license": "ISC",
|
1332 |
+
"bin": {
|
1333 |
+
"nodetouch": "bin/nodetouch.js"
|
1334 |
+
}
|
1335 |
+
},
|
1336 |
+
"node_modules/type-is": {
|
1337 |
+
"version": "1.6.18",
|
1338 |
+
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
1339 |
+
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
1340 |
+
"license": "MIT",
|
1341 |
+
"dependencies": {
|
1342 |
+
"media-typer": "0.3.0",
|
1343 |
+
"mime-types": "~2.1.24"
|
1344 |
+
},
|
1345 |
+
"engines": {
|
1346 |
+
"node": ">= 0.6"
|
1347 |
+
}
|
1348 |
+
},
|
1349 |
+
"node_modules/undefsafe": {
|
1350 |
+
"version": "2.0.5",
|
1351 |
+
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
1352 |
+
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
|
1353 |
+
"dev": true,
|
1354 |
+
"license": "MIT"
|
1355 |
+
},
|
1356 |
+
"node_modules/unpipe": {
|
1357 |
+
"version": "1.0.0",
|
1358 |
+
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
1359 |
+
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
1360 |
+
"license": "MIT",
|
1361 |
+
"engines": {
|
1362 |
+
"node": ">= 0.8"
|
1363 |
+
}
|
1364 |
+
},
|
1365 |
+
"node_modules/utils-merge": {
|
1366 |
+
"version": "1.0.1",
|
1367 |
+
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
1368 |
+
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
1369 |
+
"license": "MIT",
|
1370 |
+
"engines": {
|
1371 |
+
"node": ">= 0.4.0"
|
1372 |
+
}
|
1373 |
+
},
|
1374 |
+
"node_modules/vary": {
|
1375 |
+
"version": "1.1.2",
|
1376 |
+
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
1377 |
+
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
1378 |
+
"license": "MIT",
|
1379 |
+
"engines": {
|
1380 |
+
"node": ">= 0.8"
|
1381 |
+
}
|
1382 |
+
},
|
1383 |
+
"node_modules/xml2js": {
|
1384 |
+
"version": "0.6.2",
|
1385 |
+
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
|
1386 |
+
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
|
1387 |
+
"license": "MIT",
|
1388 |
+
"dependencies": {
|
1389 |
+
"sax": ">=0.6.0",
|
1390 |
+
"xmlbuilder": "~11.0.0"
|
1391 |
+
},
|
1392 |
+
"engines": {
|
1393 |
+
"node": ">=4.0.0"
|
1394 |
+
}
|
1395 |
+
},
|
1396 |
+
"node_modules/xmlbuilder": {
|
1397 |
+
"version": "11.0.1",
|
1398 |
+
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
1399 |
+
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
1400 |
+
"license": "MIT",
|
1401 |
+
"engines": {
|
1402 |
+
"node": ">=4.0"
|
1403 |
+
}
|
1404 |
+
}
|
1405 |
+
}
|
1406 |
+
}
|
backend/package.json
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "pubmed-ai-explorer-backend",
|
3 |
+
"version": "1.0.0",
|
4 |
+
"description": "Backend API for PubMed AI Explorer",
|
5 |
+
"main": "server.js",
|
6 |
+
"scripts": {
|
7 |
+
"dev": "nodemon server.js",
|
8 |
+
"start": "node server.js"
|
9 |
+
},
|
10 |
+
"dependencies": {
|
11 |
+
"express": "^4.18.2",
|
12 |
+
"cors": "^2.8.5",
|
13 |
+
"axios": "^1.6.2",
|
14 |
+
"node-cache": "^5.1.2",
|
15 |
+
"xml2js": "^0.6.2",
|
16 |
+
"dotenv": "^16.3.1"
|
17 |
+
},
|
18 |
+
"devDependencies": {
|
19 |
+
"nodemon": "^3.0.2"
|
20 |
+
}
|
21 |
+
}
|
backend/routes/algorithms.js
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const express = require('express');
|
2 |
+
const fs = require('fs');
|
3 |
+
const path = require('path');
|
4 |
+
const router = express.Router();
|
5 |
+
|
6 |
+
const ALGORITHMS_PATH = path.join(__dirname, '../../data/algorithms.json');
|
7 |
+
|
8 |
+
router.get('/', (req, res) => {
|
9 |
+
try {
|
10 |
+
const data = fs.readFileSync(ALGORITHMS_PATH, 'utf8');
|
11 |
+
const algorithms = JSON.parse(data);
|
12 |
+
res.json(algorithms);
|
13 |
+
} catch (error) {
|
14 |
+
res.status(500).json({ error: 'Failed to load algorithms' });
|
15 |
+
}
|
16 |
+
});
|
17 |
+
|
18 |
+
router.put('/', (req, res) => {
|
19 |
+
try {
|
20 |
+
const updatedAlgorithms = req.body;
|
21 |
+
fs.writeFileSync(ALGORITHMS_PATH, JSON.stringify(updatedAlgorithms, null, 2));
|
22 |
+
res.json({ message: 'Algorithms updated successfully' });
|
23 |
+
} catch (error) {
|
24 |
+
res.status(500).json({ error: 'Failed to update algorithms' });
|
25 |
+
}
|
26 |
+
});
|
27 |
+
|
28 |
+
router.get('/categories', (req, res) => {
|
29 |
+
try {
|
30 |
+
const data = fs.readFileSync(ALGORITHMS_PATH, 'utf8');
|
31 |
+
const algorithms = JSON.parse(data);
|
32 |
+
|
33 |
+
const categories = {};
|
34 |
+
Object.values(algorithms.algorithms).forEach(algo => {
|
35 |
+
if (!categories[algo.category]) {
|
36 |
+
categories[algo.category] = [];
|
37 |
+
}
|
38 |
+
categories[algo.category].push(algo.name);
|
39 |
+
});
|
40 |
+
|
41 |
+
res.json(categories);
|
42 |
+
} catch (error) {
|
43 |
+
res.status(500).json({ error: 'Failed to get categories' });
|
44 |
+
}
|
45 |
+
});
|
46 |
+
|
47 |
+
module.exports = router;
|
backend/routes/pubmed.js
ADDED
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const express = require('express');
|
2 |
+
const axios = require('axios');
|
3 |
+
const xml2js = require('xml2js');
|
4 |
+
const NodeCache = require('node-cache');
|
5 |
+
const router = express.Router();
|
6 |
+
|
7 |
+
const cache = new NodeCache({ stdTTL: 3600 });
|
8 |
+
|
9 |
+
const PUBMED_BASE_URL = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils';
|
10 |
+
|
11 |
+
async function searchPubMed(query, maxResults = 100) {
|
12 |
+
try {
|
13 |
+
const cacheKey = `search_${query}_${maxResults}`;
|
14 |
+
const cached = cache.get(cacheKey);
|
15 |
+
if (cached) return cached;
|
16 |
+
|
17 |
+
// Add filters to exclude review papers, meta-analyses, and systematic reviews
|
18 |
+
const filteredQuery = `(${query}) NOT Review[Publication Type] NOT Meta-Analysis[Publication Type] NOT Systematic Review[Publication Type]`;
|
19 |
+
|
20 |
+
const searchUrl = `${PUBMED_BASE_URL}/esearch.fcgi?db=pubmed&term=${encodeURIComponent(filteredQuery)}&retmax=${maxResults}&retmode=json`;
|
21 |
+
const response = await axios.get(searchUrl);
|
22 |
+
|
23 |
+
const result = {
|
24 |
+
count: parseInt(response.data.esearchresult.count),
|
25 |
+
ids: response.data.esearchresult.idlist || []
|
26 |
+
};
|
27 |
+
|
28 |
+
cache.set(cacheKey, result);
|
29 |
+
return result;
|
30 |
+
} catch (error) {
|
31 |
+
throw new Error(`PubMed search failed: ${error.message}`);
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
async function fetchPaperDetails(ids) {
|
36 |
+
try {
|
37 |
+
if (!ids.length) return [];
|
38 |
+
|
39 |
+
const cacheKey = `details_${ids.join(',')}`;
|
40 |
+
const cached = cache.get(cacheKey);
|
41 |
+
if (cached) return cached;
|
42 |
+
|
43 |
+
const fetchUrl = `${PUBMED_BASE_URL}/efetch.fcgi?db=pubmed&id=${ids.join(',')}&retmode=xml`;
|
44 |
+
|
45 |
+
// Add timeout and retry logic for PubMed API
|
46 |
+
const response = await axios.get(fetchUrl, {
|
47 |
+
timeout: 10000,
|
48 |
+
headers: {
|
49 |
+
'User-Agent': 'PubMedAIExplorer/1.0'
|
50 |
+
}
|
51 |
+
});
|
52 |
+
|
53 |
+
const parser = new xml2js.Parser();
|
54 |
+
const result = await parser.parseStringPromise(response.data);
|
55 |
+
|
56 |
+
const papers = [];
|
57 |
+
const articles = result.PubmedArticleSet?.PubmedArticle || [];
|
58 |
+
|
59 |
+
articles.forEach(article => {
|
60 |
+
const medlineCitation = article.MedlineCitation?.[0];
|
61 |
+
if (medlineCitation) {
|
62 |
+
const articleData = medlineCitation.Article?.[0];
|
63 |
+
if (articleData) {
|
64 |
+
papers.push({
|
65 |
+
pmid: medlineCitation.PMID?.[0]?._,
|
66 |
+
title: articleData.ArticleTitle?.[0] || 'No title',
|
67 |
+
abstract: articleData.Abstract?.AbstractText?.[0]?._ || articleData.Abstract?.AbstractText?.[0] || '',
|
68 |
+
authors: articleData.AuthorList?.Author?.map(author => {
|
69 |
+
const lastName = author.LastName?.[0] || '';
|
70 |
+
const foreName = author.ForeName?.[0] || '';
|
71 |
+
return `${foreName} ${lastName}`.trim();
|
72 |
+
}) || [],
|
73 |
+
journal: articleData.Journal?.Title?.[0] || '',
|
74 |
+
pubDate: medlineCitation.DateCompleted?.[0]?.Year?.[0] ||
|
75 |
+
medlineCitation.DateRevised?.[0]?.Year?.[0] || ''
|
76 |
+
});
|
77 |
+
}
|
78 |
+
}
|
79 |
+
});
|
80 |
+
|
81 |
+
cache.set(cacheKey, papers);
|
82 |
+
return papers;
|
83 |
+
} catch (error) {
|
84 |
+
throw new Error(`Failed to fetch paper details: ${error.message}`);
|
85 |
+
}
|
86 |
+
}
|
87 |
+
|
88 |
+
router.get('/search', async (req, res) => {
|
89 |
+
try {
|
90 |
+
const { query, maxResults = 20 } = req.query;
|
91 |
+
if (!query) {
|
92 |
+
return res.status(400).json({ error: 'Query parameter is required' });
|
93 |
+
}
|
94 |
+
|
95 |
+
const searchResult = await searchPubMed(query, maxResults);
|
96 |
+
const papers = await fetchPaperDetails(searchResult.ids.slice(0, 10));
|
97 |
+
|
98 |
+
res.json({
|
99 |
+
totalCount: searchResult.count,
|
100 |
+
papers: papers
|
101 |
+
});
|
102 |
+
} catch (error) {
|
103 |
+
res.status(500).json({ error: error.message });
|
104 |
+
}
|
105 |
+
});
|
106 |
+
|
107 |
+
router.get('/paper/:pmid', async (req, res) => {
|
108 |
+
try {
|
109 |
+
const { pmid } = req.params;
|
110 |
+
console.log(`Fetching paper details for PMID: ${pmid}`);
|
111 |
+
|
112 |
+
const papers = await fetchPaperDetails([pmid]);
|
113 |
+
console.log(`Found ${papers.length} papers for PMID: ${pmid}`);
|
114 |
+
|
115 |
+
if (papers.length === 0) {
|
116 |
+
console.log(`No paper found for PMID: ${pmid}`);
|
117 |
+
return res.status(404).json({ error: 'Paper not found' });
|
118 |
+
}
|
119 |
+
|
120 |
+
console.log(`Returning paper:`, papers[0].title);
|
121 |
+
res.json(papers[0]);
|
122 |
+
} catch (error) {
|
123 |
+
const { pmid } = req.params;
|
124 |
+
console.error(`Error fetching paper ${pmid}:`, error.message);
|
125 |
+
res.status(500).json({ error: error.message });
|
126 |
+
}
|
127 |
+
});
|
128 |
+
|
129 |
+
module.exports = router;
|
backend/routes/search.js
ADDED
@@ -0,0 +1,527 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const express = require('express');
|
2 |
+
const fs = require('fs');
|
3 |
+
const path = require('path');
|
4 |
+
const axios = require('axios');
|
5 |
+
const NodeCache = require('node-cache');
|
6 |
+
const router = express.Router();
|
7 |
+
|
8 |
+
const cache = new NodeCache({ stdTTL: 3600 });
|
9 |
+
|
10 |
+
const ALGORITHMS_PATH = path.join(__dirname, '../../data/algorithms.json');
|
11 |
+
const TIMELINE_CACHE_PATH = path.join(__dirname, '../../data/timeline-cache.json');
|
12 |
+
const PUBMED_BASE_URL = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils';
|
13 |
+
|
14 |
+
function loadAlgorithms() {
|
15 |
+
const data = fs.readFileSync(ALGORITHMS_PATH, 'utf8');
|
16 |
+
return JSON.parse(data);
|
17 |
+
}
|
18 |
+
|
19 |
+
function loadTimelineCache() {
|
20 |
+
try {
|
21 |
+
if (fs.existsSync(TIMELINE_CACHE_PATH)) {
|
22 |
+
const data = fs.readFileSync(TIMELINE_CACHE_PATH, 'utf8');
|
23 |
+
return JSON.parse(data);
|
24 |
+
}
|
25 |
+
} catch (error) {
|
26 |
+
console.warn('Error loading timeline cache:', error.message);
|
27 |
+
}
|
28 |
+
return {};
|
29 |
+
}
|
30 |
+
|
31 |
+
function saveTimelineCache(cache) {
|
32 |
+
try {
|
33 |
+
fs.writeFileSync(TIMELINE_CACHE_PATH, JSON.stringify(cache, null, 2));
|
34 |
+
} catch (error) {
|
35 |
+
console.error('Error saving timeline cache:', error.message);
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
function getCacheKey(algorithmKey, year) {
|
40 |
+
return `${algorithmKey}-${year}`;
|
41 |
+
}
|
42 |
+
|
43 |
+
function isCurrentYear(year) {
|
44 |
+
return year === new Date().getFullYear();
|
45 |
+
}
|
46 |
+
|
47 |
+
async function searchAlgorithmUsage(problem, algorithmKey, algorithmData) {
|
48 |
+
try {
|
49 |
+
const synonymQueries = algorithmData.synonyms.map(synonym =>
|
50 |
+
`("${problem}" AND "${synonym}")`
|
51 |
+
).join(' OR ');
|
52 |
+
|
53 |
+
// Add filters to exclude review papers, meta-analyses, and systematic reviews
|
54 |
+
const filteredQuery = `(${synonymQueries}) NOT Review[Publication Type] NOT Meta-Analysis[Publication Type] NOT Systematic Review[Publication Type]`;
|
55 |
+
|
56 |
+
const searchUrl = `${PUBMED_BASE_URL}/esearch.fcgi?db=pubmed&term=${encodeURIComponent(filteredQuery)}&retmode=json`;
|
57 |
+
|
58 |
+
// Debug logging for CNN specifically
|
59 |
+
if (algorithmKey === 'cnn') {
|
60 |
+
console.log(`CNN Search for "${problem}":`);
|
61 |
+
console.log(`Query: ${filteredQuery}`);
|
62 |
+
console.log(`URL: ${searchUrl}`);
|
63 |
+
}
|
64 |
+
|
65 |
+
const response = await axios.get(searchUrl);
|
66 |
+
const count = parseInt(response.data.esearchresult.count) || 0;
|
67 |
+
|
68 |
+
// Debug logging for CNN results
|
69 |
+
if (algorithmKey === 'cnn') {
|
70 |
+
console.log(`CNN Results: ${count} papers found`);
|
71 |
+
console.log(`Sample IDs:`, response.data.esearchresult.idlist?.slice(0, 3));
|
72 |
+
}
|
73 |
+
|
74 |
+
return {
|
75 |
+
algorithm: algorithmKey,
|
76 |
+
name: algorithmData.name,
|
77 |
+
category: algorithmData.category,
|
78 |
+
description: algorithmData.description,
|
79 |
+
count: count,
|
80 |
+
sampleIds: response.data.esearchresult.idlist?.slice(0, 3) || []
|
81 |
+
};
|
82 |
+
} catch (error) {
|
83 |
+
if (algorithmKey === 'cnn') {
|
84 |
+
console.error(`CNN Search Error:`, error.message);
|
85 |
+
}
|
86 |
+
return {
|
87 |
+
algorithm: algorithmKey,
|
88 |
+
name: algorithmData.name,
|
89 |
+
category: algorithmData.category,
|
90 |
+
description: algorithmData.description,
|
91 |
+
count: 0,
|
92 |
+
sampleIds: []
|
93 |
+
};
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
router.post('/problem', async (req, res) => {
|
98 |
+
try {
|
99 |
+
const { problem } = req.body;
|
100 |
+
if (!problem) {
|
101 |
+
return res.status(400).json({ error: 'Problem parameter is required' });
|
102 |
+
}
|
103 |
+
|
104 |
+
const algorithms = loadAlgorithms();
|
105 |
+
const results = [];
|
106 |
+
|
107 |
+
for (const [key, algo] of Object.entries(algorithms.algorithms)) {
|
108 |
+
const result = await searchAlgorithmUsage(problem, key, algo);
|
109 |
+
results.push(result);
|
110 |
+
}
|
111 |
+
|
112 |
+
results.sort((a, b) => b.count - a.count);
|
113 |
+
|
114 |
+
res.json({
|
115 |
+
problem,
|
116 |
+
totalAlgorithms: results.length,
|
117 |
+
results: results.filter(r => r.count > 0),
|
118 |
+
allResults: results
|
119 |
+
});
|
120 |
+
} catch (error) {
|
121 |
+
res.status(500).json({ error: error.message });
|
122 |
+
}
|
123 |
+
});
|
124 |
+
|
125 |
+
async function delay(ms) {
|
126 |
+
return new Promise(resolve => setTimeout(resolve, ms));
|
127 |
+
}
|
128 |
+
|
129 |
+
async function fetchAlgorithmCount(key, algo) {
|
130 |
+
const cacheKey = `dashboard-${key}`;
|
131 |
+
const cached = cache.get(cacheKey);
|
132 |
+
if (cached !== undefined) {
|
133 |
+
console.log(`${algo.name}: ${cached} results (cached)`);
|
134 |
+
return cached;
|
135 |
+
}
|
136 |
+
|
137 |
+
const generalQuery = algo.synonyms.map(s => `"${s}"`).join(' OR ');
|
138 |
+
|
139 |
+
// Add filters to exclude review papers, meta-analyses, and systematic reviews
|
140 |
+
const filteredQuery = `(${generalQuery}) NOT Review[Publication Type] NOT Meta-Analysis[Publication Type] NOT Systematic Review[Publication Type]`;
|
141 |
+
|
142 |
+
try {
|
143 |
+
const searchUrl = `${PUBMED_BASE_URL}/esearch.fcgi?db=pubmed&term=${encodeURIComponent(filteredQuery)}&retmode=json`;
|
144 |
+
const response = await axios.get(searchUrl, { timeout: 15000 });
|
145 |
+
const count = parseInt(response.data.esearchresult.count) || 0;
|
146 |
+
|
147 |
+
console.log(`${algo.name}: ${count} results for query: ${filteredQuery}`);
|
148 |
+
cache.set(cacheKey, count);
|
149 |
+
return count;
|
150 |
+
} catch (error) {
|
151 |
+
console.error(`Error fetching data for ${algo.name}:`, error.message);
|
152 |
+
return 0;
|
153 |
+
}
|
154 |
+
}
|
155 |
+
|
156 |
+
router.get('/dashboard-stats', async (req, res) => {
|
157 |
+
try {
|
158 |
+
const algorithms = loadAlgorithms();
|
159 |
+
const stats = {
|
160 |
+
classical_ml: [],
|
161 |
+
deep_learning: [],
|
162 |
+
llms: []
|
163 |
+
};
|
164 |
+
|
165 |
+
// Process algorithms sequentially to avoid rate limiting
|
166 |
+
for (const [key, algo] of Object.entries(algorithms.algorithms)) {
|
167 |
+
const count = await fetchAlgorithmCount(key, algo);
|
168 |
+
|
169 |
+
stats[algo.category].push({
|
170 |
+
algorithm: key,
|
171 |
+
name: algo.name,
|
172 |
+
count: count
|
173 |
+
});
|
174 |
+
|
175 |
+
// Add delay between requests to respect rate limits
|
176 |
+
await delay(200);
|
177 |
+
}
|
178 |
+
|
179 |
+
stats.classical_ml.sort((a, b) => b.count - a.count);
|
180 |
+
stats.deep_learning.sort((a, b) => b.count - a.count);
|
181 |
+
stats.llms.sort((a, b) => b.count - a.count);
|
182 |
+
|
183 |
+
res.json(stats);
|
184 |
+
} catch (error) {
|
185 |
+
res.status(500).json({ error: error.message });
|
186 |
+
}
|
187 |
+
});
|
188 |
+
|
189 |
+
router.get('/pubmed-link', (req, res) => {
|
190 |
+
const { problem, algorithm } = req.query;
|
191 |
+
if (!problem || !algorithm) {
|
192 |
+
return res.status(400).json({ error: 'Both problem and algorithm parameters are required' });
|
193 |
+
}
|
194 |
+
|
195 |
+
const algorithms = loadAlgorithms();
|
196 |
+
const algoData = algorithms.algorithms[algorithm];
|
197 |
+
|
198 |
+
if (!algoData) {
|
199 |
+
return res.status(404).json({ error: 'Algorithm not found' });
|
200 |
+
}
|
201 |
+
|
202 |
+
const synonymQueries = algoData.synonyms.map(synonym =>
|
203 |
+
`("${problem}" AND "${synonym}")`
|
204 |
+
).join(' OR ');
|
205 |
+
|
206 |
+
// Add filters to exclude review papers for PubMed links too
|
207 |
+
const filteredQuery = `(${synonymQueries}) NOT Review[Publication Type] NOT Meta-Analysis[Publication Type] NOT Systematic Review[Publication Type]`;
|
208 |
+
|
209 |
+
const pubmedUrl = `https://pubmed.ncbi.nlm.nih.gov/?term=${encodeURIComponent(filteredQuery)}`;
|
210 |
+
|
211 |
+
res.json({ url: pubmedUrl });
|
212 |
+
});
|
213 |
+
|
214 |
+
async function fetchAlgorithmCountByYear(key, algo, year, diskCache, retryCount = 0) {
|
215 |
+
const diskCacheKey = getCacheKey(key, year);
|
216 |
+
|
217 |
+
// Check disk cache first for past years (which never change)
|
218 |
+
if (!isCurrentYear(year) && diskCache[diskCacheKey] !== undefined) {
|
219 |
+
console.log(`Using disk cache for ${algo.name} (${year}): ${diskCache[diskCacheKey]}`);
|
220 |
+
return diskCache[diskCacheKey];
|
221 |
+
}
|
222 |
+
|
223 |
+
// Check memory cache for current year
|
224 |
+
const memoryCacheKey = `timeline-${key}-${year}`;
|
225 |
+
const memCached = cache.get(memoryCacheKey);
|
226 |
+
if (isCurrentYear(year) && memCached !== undefined) {
|
227 |
+
return memCached;
|
228 |
+
}
|
229 |
+
|
230 |
+
const generalQuery = algo.synonyms.map(s => `"${s}"`).join(' OR ');
|
231 |
+
const yearFilter = `"${year}"[Date - Publication]`;
|
232 |
+
const filteredQuery = `(${generalQuery}) AND ${yearFilter} NOT Review[Publication Type] NOT Meta-Analysis[Publication Type] NOT Systematic Review[Publication Type]`;
|
233 |
+
|
234 |
+
try {
|
235 |
+
const searchUrl = `${PUBMED_BASE_URL}/esearch.fcgi?db=pubmed&term=${encodeURIComponent(filteredQuery)}&retmode=json`;
|
236 |
+
const response = await axios.get(searchUrl, { timeout: 15000 });
|
237 |
+
const count = parseInt(response.data.esearchresult.count) || 0;
|
238 |
+
|
239 |
+
// Save to appropriate cache
|
240 |
+
if (isCurrentYear(year)) {
|
241 |
+
// Current year: save to memory cache (expires)
|
242 |
+
cache.set(memoryCacheKey, count);
|
243 |
+
} else {
|
244 |
+
// Past years: save to disk cache (permanent)
|
245 |
+
diskCache[diskCacheKey] = count;
|
246 |
+
}
|
247 |
+
|
248 |
+
console.log(`Fetched ${algo.name} (${year}): ${count} papers`);
|
249 |
+
return count;
|
250 |
+
} catch (error) {
|
251 |
+
if (error.response?.status === 429 && retryCount < 3) {
|
252 |
+
const backoffTime = Math.pow(2, retryCount) * 1000; // 1s, 2s, 4s
|
253 |
+
console.log(`Rate limited for ${algo.name} (${year}), retrying in ${backoffTime}ms (attempt ${retryCount + 1})`);
|
254 |
+
await delay(backoffTime);
|
255 |
+
return fetchAlgorithmCountByYear(key, algo, year, diskCache, retryCount + 1);
|
256 |
+
}
|
257 |
+
|
258 |
+
console.error(`Error fetching timeline data for ${algo.name} (${year}):`, error.message);
|
259 |
+
return 0;
|
260 |
+
}
|
261 |
+
}
|
262 |
+
|
263 |
+
router.get('/timeline-stream', async (req, res) => {
|
264 |
+
const { startYear = 2015, endYear = 2024 } = req.query;
|
265 |
+
const start = parseInt(startYear);
|
266 |
+
const end = parseInt(endYear);
|
267 |
+
|
268 |
+
if (start > end || start < 2010 || end > 2024) {
|
269 |
+
return res.status(400).json({ error: 'Invalid year range' });
|
270 |
+
}
|
271 |
+
|
272 |
+
// Set up Server-Sent Events
|
273 |
+
res.writeHead(200, {
|
274 |
+
'Content-Type': 'text/event-stream',
|
275 |
+
'Cache-Control': 'no-cache',
|
276 |
+
'Connection': 'keep-alive',
|
277 |
+
'Access-Control-Allow-Origin': '*',
|
278 |
+
'Access-Control-Allow-Headers': 'Cache-Control'
|
279 |
+
});
|
280 |
+
|
281 |
+
const sendProgress = (data) => {
|
282 |
+
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
283 |
+
};
|
284 |
+
|
285 |
+
try {
|
286 |
+
const algorithms = loadAlgorithms();
|
287 |
+
const diskCache = loadTimelineCache();
|
288 |
+
let cacheUpdated = false;
|
289 |
+
|
290 |
+
const years = [];
|
291 |
+
for (let year = start; year <= end; year++) {
|
292 |
+
years.push(year);
|
293 |
+
}
|
294 |
+
|
295 |
+
const timelineData = [];
|
296 |
+
const algorithmsData = [];
|
297 |
+
|
298 |
+
// Initialize timeline structure
|
299 |
+
for (const year of years) {
|
300 |
+
timelineData.push({ year });
|
301 |
+
}
|
302 |
+
|
303 |
+
// Count total operations
|
304 |
+
const totalAlgorithms = Object.keys(algorithms.algorithms).length;
|
305 |
+
const totalYears = years.length;
|
306 |
+
const totalOperations = totalAlgorithms * totalYears;
|
307 |
+
|
308 |
+
let completedOperations = 0;
|
309 |
+
let cachedResults = 0;
|
310 |
+
let fetchedResults = 0;
|
311 |
+
|
312 |
+
// Count cached vs fetched upfront
|
313 |
+
for (const [key, algo] of Object.entries(algorithms.algorithms)) {
|
314 |
+
for (const year of years) {
|
315 |
+
const diskCacheKey = getCacheKey(key, year);
|
316 |
+
if (!isCurrentYear(year) && diskCache[diskCacheKey] !== undefined) {
|
317 |
+
cachedResults++;
|
318 |
+
} else {
|
319 |
+
fetchedResults++;
|
320 |
+
}
|
321 |
+
}
|
322 |
+
}
|
323 |
+
|
324 |
+
sendProgress({
|
325 |
+
type: 'init',
|
326 |
+
totalOperations,
|
327 |
+
cachedResults,
|
328 |
+
fetchedResults,
|
329 |
+
message: 'Starting timeline data collection...'
|
330 |
+
});
|
331 |
+
|
332 |
+
// Process each algorithm
|
333 |
+
for (const [key, algo] of Object.entries(algorithms.algorithms)) {
|
334 |
+
const algorithmTimeline = {
|
335 |
+
algorithm: key,
|
336 |
+
name: algo.name,
|
337 |
+
category: algo.category,
|
338 |
+
data: []
|
339 |
+
};
|
340 |
+
|
341 |
+
sendProgress({
|
342 |
+
type: 'algorithm_start',
|
343 |
+
algorithm: algo.name,
|
344 |
+
progress: Math.round((completedOperations / totalOperations) * 100),
|
345 |
+
completed: completedOperations,
|
346 |
+
total: totalOperations
|
347 |
+
});
|
348 |
+
|
349 |
+
// Get data for each year
|
350 |
+
for (const year of years) {
|
351 |
+
const count = await fetchAlgorithmCountByYear(key, algo, year, diskCache);
|
352 |
+
algorithmTimeline.data.push({ year, count });
|
353 |
+
|
354 |
+
// Add to timeline data structure
|
355 |
+
const yearIndex = timelineData.findIndex(item => item.year === year);
|
356 |
+
if (yearIndex !== -1) {
|
357 |
+
timelineData[yearIndex][key] = count;
|
358 |
+
}
|
359 |
+
|
360 |
+
completedOperations++;
|
361 |
+
|
362 |
+
sendProgress({
|
363 |
+
type: 'year_complete',
|
364 |
+
algorithm: algo.name,
|
365 |
+
year,
|
366 |
+
count,
|
367 |
+
progress: Math.round((completedOperations / totalOperations) * 100),
|
368 |
+
completed: completedOperations,
|
369 |
+
total: totalOperations
|
370 |
+
});
|
371 |
+
|
372 |
+
// Check if we made an API call and need to save cache
|
373 |
+
const diskCacheKey = getCacheKey(key, year);
|
374 |
+
if (!isCurrentYear(year) && diskCache[diskCacheKey] === count) {
|
375 |
+
cacheUpdated = true;
|
376 |
+
}
|
377 |
+
|
378 |
+
// Add delay only if we made an actual API call
|
379 |
+
if (isCurrentYear(year) || diskCache[diskCacheKey] === undefined) {
|
380 |
+
await delay(500);
|
381 |
+
}
|
382 |
+
}
|
383 |
+
|
384 |
+
algorithmsData.push(algorithmTimeline);
|
385 |
+
|
386 |
+
sendProgress({
|
387 |
+
type: 'algorithm_complete',
|
388 |
+
algorithm: algo.name,
|
389 |
+
progress: Math.round((completedOperations / totalOperations) * 100),
|
390 |
+
completed: completedOperations,
|
391 |
+
total: totalOperations
|
392 |
+
});
|
393 |
+
}
|
394 |
+
|
395 |
+
// Save cache if updated
|
396 |
+
if (cacheUpdated) {
|
397 |
+
saveTimelineCache(diskCache);
|
398 |
+
sendProgress({
|
399 |
+
type: 'cache_saved',
|
400 |
+
message: 'Timeline cache updated and saved to disk'
|
401 |
+
});
|
402 |
+
}
|
403 |
+
|
404 |
+
// Send final results
|
405 |
+
sendProgress({
|
406 |
+
type: 'complete',
|
407 |
+
timelineData,
|
408 |
+
algorithms: algorithmsData,
|
409 |
+
yearRange: { start, end },
|
410 |
+
cacheStats: {
|
411 |
+
cached: cachedResults,
|
412 |
+
fetched: fetchedResults
|
413 |
+
}
|
414 |
+
});
|
415 |
+
|
416 |
+
res.end();
|
417 |
+
} catch (error) {
|
418 |
+
sendProgress({
|
419 |
+
type: 'error',
|
420 |
+
error: error.message
|
421 |
+
});
|
422 |
+
res.end();
|
423 |
+
}
|
424 |
+
});
|
425 |
+
|
426 |
+
router.get('/timeline', async (req, res) => {
|
427 |
+
try {
|
428 |
+
const { startYear = 2015, endYear = 2024 } = req.query;
|
429 |
+
const start = parseInt(startYear);
|
430 |
+
const end = parseInt(endYear);
|
431 |
+
|
432 |
+
if (start > end || start < 2010 || end > 2024) {
|
433 |
+
return res.status(400).json({ error: 'Invalid year range' });
|
434 |
+
}
|
435 |
+
|
436 |
+
const algorithms = loadAlgorithms();
|
437 |
+
const diskCache = loadTimelineCache();
|
438 |
+
let cacheUpdated = false;
|
439 |
+
|
440 |
+
const years = [];
|
441 |
+
for (let year = start; year <= end; year++) {
|
442 |
+
years.push(year);
|
443 |
+
}
|
444 |
+
|
445 |
+
const timelineData = [];
|
446 |
+
const algorithmsData = [];
|
447 |
+
|
448 |
+
// Initialize timeline structure
|
449 |
+
for (const year of years) {
|
450 |
+
timelineData.push({ year });
|
451 |
+
}
|
452 |
+
|
453 |
+
// Count how many API calls we'll need to make
|
454 |
+
let totalApiCalls = 0;
|
455 |
+
let cachedResults = 0;
|
456 |
+
|
457 |
+
for (const [key, algo] of Object.entries(algorithms.algorithms)) {
|
458 |
+
for (const year of years) {
|
459 |
+
const diskCacheKey = getCacheKey(key, year);
|
460 |
+
if (!isCurrentYear(year) && diskCache[diskCacheKey] !== undefined) {
|
461 |
+
cachedResults++;
|
462 |
+
} else {
|
463 |
+
totalApiCalls++;
|
464 |
+
}
|
465 |
+
}
|
466 |
+
}
|
467 |
+
|
468 |
+
console.log(`Timeline request: ${cachedResults} cached results, ${totalApiCalls} API calls needed`);
|
469 |
+
|
470 |
+
// Process each algorithm
|
471 |
+
for (const [key, algo] of Object.entries(algorithms.algorithms)) {
|
472 |
+
const algorithmTimeline = {
|
473 |
+
algorithm: key,
|
474 |
+
name: algo.name,
|
475 |
+
category: algo.category,
|
476 |
+
data: []
|
477 |
+
};
|
478 |
+
|
479 |
+
// Get data for each year
|
480 |
+
for (const year of years) {
|
481 |
+
const count = await fetchAlgorithmCountByYear(key, algo, year, diskCache);
|
482 |
+
algorithmTimeline.data.push({ year, count });
|
483 |
+
|
484 |
+
// Add to timeline data structure
|
485 |
+
const yearIndex = timelineData.findIndex(item => item.year === year);
|
486 |
+
if (yearIndex !== -1) {
|
487 |
+
timelineData[yearIndex][key] = count;
|
488 |
+
}
|
489 |
+
|
490 |
+
// Check if we made an API call (not cached) and need to save cache
|
491 |
+
const diskCacheKey = getCacheKey(key, year);
|
492 |
+
if (!isCurrentYear(year) && diskCache[diskCacheKey] === count) {
|
493 |
+
cacheUpdated = true;
|
494 |
+
}
|
495 |
+
|
496 |
+
// Add delay only if we made an actual API call
|
497 |
+
if (isCurrentYear(year) || diskCache[diskCacheKey] === undefined) {
|
498 |
+
await delay(500);
|
499 |
+
}
|
500 |
+
}
|
501 |
+
|
502 |
+
algorithmsData.push(algorithmTimeline);
|
503 |
+
console.log(`Completed timeline data for ${algo.name}`);
|
504 |
+
}
|
505 |
+
|
506 |
+
// Save updated cache to disk if needed
|
507 |
+
if (cacheUpdated) {
|
508 |
+
saveTimelineCache(diskCache);
|
509 |
+
console.log('Timeline cache updated and saved to disk');
|
510 |
+
}
|
511 |
+
|
512 |
+
res.json({
|
513 |
+
timelineData,
|
514 |
+
algorithms: algorithmsData,
|
515 |
+
yearRange: { start, end },
|
516 |
+
cacheStats: {
|
517 |
+
cached: cachedResults,
|
518 |
+
fetched: totalApiCalls
|
519 |
+
}
|
520 |
+
});
|
521 |
+
} catch (error) {
|
522 |
+
console.error('Timeline API error:', error);
|
523 |
+
res.status(500).json({ error: error.message });
|
524 |
+
}
|
525 |
+
});
|
526 |
+
|
527 |
+
module.exports = router;
|
backend/server.js
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const express = require('express');
|
2 |
+
const cors = require('cors');
|
3 |
+
const path = require('path');
|
4 |
+
require('dotenv').config();
|
5 |
+
|
6 |
+
const app = express();
|
7 |
+
const PORT = process.env.PORT || 3001;
|
8 |
+
|
9 |
+
app.use(cors());
|
10 |
+
app.use(express.json());
|
11 |
+
|
12 |
+
const algorithmsRoute = require('./routes/algorithms');
|
13 |
+
const pubmedRoute = require('./routes/pubmed');
|
14 |
+
const searchRoute = require('./routes/search');
|
15 |
+
|
16 |
+
app.use('/api/algorithms', algorithmsRoute);
|
17 |
+
app.use('/api/pubmed', pubmedRoute);
|
18 |
+
app.use('/api/search', searchRoute);
|
19 |
+
|
20 |
+
app.get('/api/health', (req, res) => {
|
21 |
+
res.json({ status: 'OK', message: 'PubMed AI Explorer API is running' });
|
22 |
+
});
|
23 |
+
|
24 |
+
app.listen(PORT, () => {
|
25 |
+
console.log(`Server running on port ${PORT}`);
|
26 |
+
});
|
data/algorithms.json
ADDED
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"algorithms": {
|
3 |
+
"svm": {
|
4 |
+
"name": "Support Vector Machine",
|
5 |
+
"category": "classical_ml",
|
6 |
+
"description": "A supervised learning algorithm that finds optimal hyperplanes for classification and regression tasks",
|
7 |
+
"synonyms": ["support vector machine", "SVM", "support vector classifier", "support vector regression", "SVR"]
|
8 |
+
},
|
9 |
+
"decision_tree": {
|
10 |
+
"name": "Decision Tree",
|
11 |
+
"category": "classical_ml",
|
12 |
+
"description": "A tree-like model that makes decisions by splitting data based on feature values",
|
13 |
+
"synonyms": ["decision tree", "decision trees", "DT", "CART", "classification tree", "regression tree"]
|
14 |
+
},
|
15 |
+
"random_forest": {
|
16 |
+
"name": "Random Forest",
|
17 |
+
"category": "classical_ml",
|
18 |
+
"description": "An ensemble method that combines multiple decision trees for improved accuracy",
|
19 |
+
"synonyms": ["random forest", "RF", "random forests", "forest classifier"]
|
20 |
+
},
|
21 |
+
"xgboost": {
|
22 |
+
"name": "XGBoost",
|
23 |
+
"category": "classical_ml",
|
24 |
+
"description": "Extreme Gradient Boosting - an optimized gradient boosting framework",
|
25 |
+
"synonyms": ["xgboost", "XGBoost", "extreme gradient boosting", "XGB"]
|
26 |
+
},
|
27 |
+
"logistic_regression": {
|
28 |
+
"name": "Logistic Regression",
|
29 |
+
"category": "classical_ml",
|
30 |
+
"description": "A linear model for binary and multiclass classification problems",
|
31 |
+
"synonyms": ["logistic regression", "logit", "logistic model", "LR"]
|
32 |
+
},
|
33 |
+
"naive_bayes": {
|
34 |
+
"name": "Naive Bayes",
|
35 |
+
"category": "classical_ml",
|
36 |
+
"description": "A probabilistic classifier based on Bayes' theorem with independence assumptions",
|
37 |
+
"synonyms": ["naive bayes", "Naive Bayes", "NB", "Bayes classifier"]
|
38 |
+
},
|
39 |
+
"knn": {
|
40 |
+
"name": "K-Nearest Neighbors",
|
41 |
+
"category": "classical_ml",
|
42 |
+
"description": "A non-parametric method that classifies data points based on the class of their nearest neighbors",
|
43 |
+
"synonyms": ["k-nearest neighbors", "KNN", "k-NN", "nearest neighbor", "k nearest neighbour"]
|
44 |
+
},
|
45 |
+
"kmeans": {
|
46 |
+
"name": "K-Means Clustering",
|
47 |
+
"category": "classical_ml",
|
48 |
+
"description": "An unsupervised clustering algorithm that partitions data into k clusters",
|
49 |
+
"synonyms": ["k-means", "K-means", "kmeans", "k-means clustering", "k means"]
|
50 |
+
},
|
51 |
+
"gradient_boosting": {
|
52 |
+
"name": "Gradient Boosting",
|
53 |
+
"category": "classical_ml",
|
54 |
+
"description": "An ensemble method that builds models sequentially to correct errors of previous models",
|
55 |
+
"synonyms": ["gradient boosting", "GB", "GBM", "gradient boosted trees", "gradient boosting machine"]
|
56 |
+
},
|
57 |
+
"ada_boost": {
|
58 |
+
"name": "AdaBoost",
|
59 |
+
"category": "classical_ml",
|
60 |
+
"description": "Adaptive Boosting algorithm that combines weak learners into a strong classifier",
|
61 |
+
"synonyms": ["AdaBoost", "ada boost", "adaptive boosting", "adaboost"]
|
62 |
+
},
|
63 |
+
"pca": {
|
64 |
+
"name": "Principal Component Analysis",
|
65 |
+
"category": "classical_ml",
|
66 |
+
"description": "A dimensionality reduction technique that finds principal components of data variance",
|
67 |
+
"synonyms": ["PCA", "principal component analysis", "principal components"]
|
68 |
+
},
|
69 |
+
"linear_regression": {
|
70 |
+
"name": "Linear Regression",
|
71 |
+
"category": "classical_ml",
|
72 |
+
"description": "A linear approach to modeling the relationship between variables",
|
73 |
+
"synonyms": ["linear regression", "ordinary least squares", "OLS", "linear model"]
|
74 |
+
},
|
75 |
+
"cnn": {
|
76 |
+
"name": "Convolutional Neural Network",
|
77 |
+
"category": "deep_learning",
|
78 |
+
"description": "Deep learning architecture specialized for processing grid-like data such as images",
|
79 |
+
"synonyms": ["convolutional neural network", "CNN", "ConvNet", "convolutional network", "deep convolutional", "conv neural network", "convolution neural network"]
|
80 |
+
},
|
81 |
+
"lstm": {
|
82 |
+
"name": "Long Short-Term Memory",
|
83 |
+
"category": "deep_learning",
|
84 |
+
"description": "A type of recurrent neural network capable of learning long-term dependencies",
|
85 |
+
"synonyms": ["LSTM", "long short-term memory", "long short term memory", "LSTM network"]
|
86 |
+
},
|
87 |
+
"transformer": {
|
88 |
+
"name": "Transformer",
|
89 |
+
"category": "deep_learning",
|
90 |
+
"description": "Attention-based neural network architecture for sequence-to-sequence tasks",
|
91 |
+
"synonyms": ["transformer", "transformers", "transformer model", "transformer architecture", "self-attention"]
|
92 |
+
},
|
93 |
+
"resnet": {
|
94 |
+
"name": "ResNet",
|
95 |
+
"category": "deep_learning",
|
96 |
+
"description": "Residual Neural Network - a deep CNN architecture with skip connections",
|
97 |
+
"synonyms": ["ResNet", "resnet", "residual network", "residual neural network"]
|
98 |
+
},
|
99 |
+
"unet": {
|
100 |
+
"name": "U-Net",
|
101 |
+
"category": "deep_learning",
|
102 |
+
"description": "A CNN architecture for biomedical image segmentation with encoder-decoder structure",
|
103 |
+
"synonyms": ["U-Net", "UNet", "u-net", "unet"]
|
104 |
+
},
|
105 |
+
"gan": {
|
106 |
+
"name": "Generative Adversarial Network",
|
107 |
+
"category": "deep_learning",
|
108 |
+
"description": "A framework where two neural networks compete to generate realistic data",
|
109 |
+
"synonyms": ["GAN", "generative adversarial network", "generative adversarial networks", "GANs"]
|
110 |
+
},
|
111 |
+
"autoencoder": {
|
112 |
+
"name": "Autoencoder",
|
113 |
+
"category": "deep_learning",
|
114 |
+
"description": "Neural networks that learn efficient representations by encoding and decoding data",
|
115 |
+
"synonyms": ["autoencoder", "auto-encoder", "autoencoders", "variational autoencoder", "VAE"]
|
116 |
+
},
|
117 |
+
"vgg": {
|
118 |
+
"name": "VGG",
|
119 |
+
"category": "deep_learning",
|
120 |
+
"description": "Very Deep Convolutional Networks - a CNN architecture with small convolution filters",
|
121 |
+
"synonyms": ["VGG", "VGG-16", "VGG-19", "VGGNet"]
|
122 |
+
},
|
123 |
+
"inception": {
|
124 |
+
"name": "Inception",
|
125 |
+
"category": "deep_learning",
|
126 |
+
"description": "CNN architecture that uses inception modules for efficient computation",
|
127 |
+
"synonyms": ["Inception", "GoogLeNet", "Inception-v3", "Inception-v4", "inception network"]
|
128 |
+
},
|
129 |
+
"rnn": {
|
130 |
+
"name": "Recurrent Neural Network",
|
131 |
+
"category": "deep_learning",
|
132 |
+
"description": "Neural networks with memory that can process sequences of data",
|
133 |
+
"synonyms": ["RNN", "recurrent neural network", "recurrent network", "RNNs"]
|
134 |
+
},
|
135 |
+
"gru": {
|
136 |
+
"name": "Gated Recurrent Unit",
|
137 |
+
"category": "deep_learning",
|
138 |
+
"description": "Simplified variant of LSTM with fewer parameters and faster training",
|
139 |
+
"synonyms": ["GRU", "gated recurrent unit", "gated recurrent units", "GRUs"]
|
140 |
+
},
|
141 |
+
"yolo": {
|
142 |
+
"name": "YOLO",
|
143 |
+
"category": "deep_learning",
|
144 |
+
"description": "You Only Look Once - real-time object detection algorithm",
|
145 |
+
"synonyms": ["YOLO", "you only look once", "YOLOv3", "YOLOv4", "YOLOv5"]
|
146 |
+
},
|
147 |
+
"capsnet": {
|
148 |
+
"name": "Capsule Network",
|
149 |
+
"category": "deep_learning",
|
150 |
+
"description": "Neural network architecture that uses capsules to better model hierarchical relationships",
|
151 |
+
"synonyms": ["CapsNet", "capsule network", "capsule networks", "dynamic routing"]
|
152 |
+
},
|
153 |
+
"attention": {
|
154 |
+
"name": "Attention Mechanism",
|
155 |
+
"category": "deep_learning",
|
156 |
+
"description": "Mechanism that allows models to focus on relevant parts of input sequences",
|
157 |
+
"synonyms": ["attention mechanism", "attention", "multi-head attention", "scaled dot-product attention"]
|
158 |
+
},
|
159 |
+
"gpt": {
|
160 |
+
"name": "GPT",
|
161 |
+
"category": "llms",
|
162 |
+
"description": "Generative Pre-trained Transformer - OpenAI's large language model family",
|
163 |
+
"synonyms": ["GPT", "gpt", "generative pre-trained transformer", "ChatGPT", "GPT-3", "GPT-4", "GPT-4o", "OpenAI GPT"]
|
164 |
+
},
|
165 |
+
"claude": {
|
166 |
+
"name": "Claude",
|
167 |
+
"category": "llms",
|
168 |
+
"description": "Anthropic's AI assistant and large language model family",
|
169 |
+
"synonyms": ["Claude", "claude", "Anthropic Claude", "Claude-3", "Claude Sonnet", "Claude Haiku", "Claude Opus"]
|
170 |
+
},
|
171 |
+
"bert": {
|
172 |
+
"name": "BERT",
|
173 |
+
"category": "llms",
|
174 |
+
"description": "Bidirectional Encoder Representations from Transformers - Google's pre-trained language model",
|
175 |
+
"synonyms": ["BERT", "bert", "bidirectional encoder representations", "BERT model", "Google BERT"]
|
176 |
+
},
|
177 |
+
"gemini": {
|
178 |
+
"name": "Gemini",
|
179 |
+
"category": "llms",
|
180 |
+
"description": "Google's multimodal large language model family",
|
181 |
+
"synonyms": ["Gemini", "gemini", "Google Gemini", "Gemini Pro", "Gemini Ultra", "Gemini Nano"]
|
182 |
+
},
|
183 |
+
"llama": {
|
184 |
+
"name": "LLaMA",
|
185 |
+
"category": "llms",
|
186 |
+
"description": "Large Language Model Meta AI - Meta's open-source language model family",
|
187 |
+
"synonyms": ["LLaMA", "llama", "Llama", "Meta LLaMA", "Llama-2", "Llama 2", "Llama 3", "Code Llama"]
|
188 |
+
},
|
189 |
+
"qwen": {
|
190 |
+
"name": "Qwen",
|
191 |
+
"category": "llms",
|
192 |
+
"description": "Alibaba's large language model series with multilingual capabilities",
|
193 |
+
"synonyms": ["Qwen", "qwen", "Alibaba Qwen", "Qwen-7B", "Qwen-14B", "Qwen-72B", "Tongyi Qianwen"]
|
194 |
+
},
|
195 |
+
"deepseek": {
|
196 |
+
"name": "DeepSeek",
|
197 |
+
"category": "llms",
|
198 |
+
"description": "DeepSeek's large language model optimized for code and reasoning",
|
199 |
+
"synonyms": ["DeepSeek", "deepseek", "DeepSeek Coder", "DeepSeek LLM", "DeepSeek-V2"]
|
200 |
+
},
|
201 |
+
"mistral": {
|
202 |
+
"name": "Mistral",
|
203 |
+
"category": "llms",
|
204 |
+
"description": "Mistral AI's efficient and powerful open-source language models",
|
205 |
+
"synonyms": ["Mistral", "mistral", "Mistral 7B", "Mixtral", "Mistral AI", "Mixtral 8x7B"]
|
206 |
+
},
|
207 |
+
"palm": {
|
208 |
+
"name": "PaLM",
|
209 |
+
"category": "llms",
|
210 |
+
"description": "Pathways Language Model - Google's large-scale language model",
|
211 |
+
"synonyms": ["PaLM", "palm", "Pathways Language Model", "PaLM-2", "Google PaLM"]
|
212 |
+
},
|
213 |
+
"t5": {
|
214 |
+
"name": "T5",
|
215 |
+
"category": "llms",
|
216 |
+
"description": "Text-to-Text Transfer Transformer - Google's unified text processing model",
|
217 |
+
"synonyms": ["T5", "t5", "text-to-text transfer transformer", "Google T5"]
|
218 |
+
},
|
219 |
+
"roberta": {
|
220 |
+
"name": "RoBERTa",
|
221 |
+
"category": "llms",
|
222 |
+
"description": "Robustly Optimized BERT Pretraining Approach - Meta's improved BERT variant",
|
223 |
+
"synonyms": ["RoBERTa", "roberta", "robustly optimized BERT", "Meta RoBERTa"]
|
224 |
+
},
|
225 |
+
"phi": {
|
226 |
+
"name": "Phi",
|
227 |
+
"category": "llms",
|
228 |
+
"description": "Microsoft's small language model series optimized for efficiency",
|
229 |
+
"synonyms": ["Phi", "phi", "Microsoft Phi", "Phi-3", "Phi-2", "Phi-1"]
|
230 |
+
},
|
231 |
+
"falcon": {
|
232 |
+
"name": "Falcon",
|
233 |
+
"category": "llms",
|
234 |
+
"description": "Technology Innovation Institute's open-source large language model",
|
235 |
+
"synonyms": ["Falcon", "falcon", "Falcon-7B", "Falcon-40B", "Falcon-180B", "TII Falcon"]
|
236 |
+
}
|
237 |
+
}
|
238 |
+
}
|
data/timeline-cache.json
ADDED
@@ -0,0 +1,392 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"svm-2015": 2191,
|
3 |
+
"svm-2016": 2220,
|
4 |
+
"svm-2017": 2496,
|
5 |
+
"svm-2018": 2800,
|
6 |
+
"svm-2019": 3138,
|
7 |
+
"svm-2020": 3552,
|
8 |
+
"svm-2021": 4402,
|
9 |
+
"svm-2022": 5312,
|
10 |
+
"svm-2023": 5015,
|
11 |
+
"svm-2024": 5569,
|
12 |
+
"decision_tree-2015": 2690,
|
13 |
+
"decision_tree-2016": 2822,
|
14 |
+
"decision_tree-2017": 3089,
|
15 |
+
"decision_tree-2018": 3521,
|
16 |
+
"decision_tree-2019": 3884,
|
17 |
+
"decision_tree-2020": 4730,
|
18 |
+
"decision_tree-2021": 5816,
|
19 |
+
"decision_tree-2022": 6297,
|
20 |
+
"decision_tree-2023": 6106,
|
21 |
+
"decision_tree-2024": 6727,
|
22 |
+
"random_forest-2015": 2927,
|
23 |
+
"random_forest-2016": 3092,
|
24 |
+
"random_forest-2017": 3413,
|
25 |
+
"random_forest-2018": 4008,
|
26 |
+
"random_forest-2019": 4797,
|
27 |
+
"random_forest-2020": 6107,
|
28 |
+
"random_forest-2021": 8016,
|
29 |
+
"random_forest-2022": 9246,
|
30 |
+
"random_forest-2023": 9684,
|
31 |
+
"random_forest-2024": 11618,
|
32 |
+
"xgboost-2015": 1,
|
33 |
+
"xgboost-2016": 3,
|
34 |
+
"xgboost-2017": 16,
|
35 |
+
"xgboost-2018": 66,
|
36 |
+
"xgboost-2019": 180,
|
37 |
+
"xgboost-2020": 445,
|
38 |
+
"xgboost-2021": 1000,
|
39 |
+
"xgboost-2022": 1660,
|
40 |
+
"xgboost-2023": 2104,
|
41 |
+
"xgboost-2024": 3269,
|
42 |
+
"logistic_regression-2015": 24898,
|
43 |
+
"logistic_regression-2016": 26330,
|
44 |
+
"logistic_regression-2017": 28299,
|
45 |
+
"logistic_regression-2018": 30524,
|
46 |
+
"logistic_regression-2019": 34055,
|
47 |
+
"logistic_regression-2020": 41392,
|
48 |
+
"logistic_regression-2021": 50426,
|
49 |
+
"logistic_regression-2022": 53795,
|
50 |
+
"logistic_regression-2023": 50528,
|
51 |
+
"logistic_regression-2024": 54933,
|
52 |
+
"naive_bayes-2015": 1473,
|
53 |
+
"naive_bayes-2016": 1641,
|
54 |
+
"naive_bayes-2017": 1790,
|
55 |
+
"naive_bayes-2018": 2062,
|
56 |
+
"naive_bayes-2019": 2255,
|
57 |
+
"naive_bayes-2020": 2857,
|
58 |
+
"naive_bayes-2021": 3505,
|
59 |
+
"naive_bayes-2022": 3738,
|
60 |
+
"naive_bayes-2023": 4030,
|
61 |
+
"naive_bayes-2024": 4923,
|
62 |
+
"knn-2015": 574,
|
63 |
+
"knn-2016": 604,
|
64 |
+
"knn-2017": 595,
|
65 |
+
"knn-2018": 809,
|
66 |
+
"knn-2019": 901,
|
67 |
+
"knn-2020": 1027,
|
68 |
+
"knn-2021": 1507,
|
69 |
+
"knn-2022": 1829,
|
70 |
+
"knn-2023": 1875,
|
71 |
+
"knn-2024": 2144,
|
72 |
+
"kmeans-2015": 345,
|
73 |
+
"kmeans-2016": 353,
|
74 |
+
"kmeans-2017": 424,
|
75 |
+
"kmeans-2018": 490,
|
76 |
+
"kmeans-2019": 584,
|
77 |
+
"kmeans-2020": 730,
|
78 |
+
"kmeans-2021": 1016,
|
79 |
+
"kmeans-2022": 1255,
|
80 |
+
"kmeans-2023": 1272,
|
81 |
+
"kmeans-2024": 1373,
|
82 |
+
"gradient_boosting-2015": 2774,
|
83 |
+
"gradient_boosting-2016": 2787,
|
84 |
+
"gradient_boosting-2017": 3104,
|
85 |
+
"gradient_boosting-2018": 3688,
|
86 |
+
"gradient_boosting-2019": 4101,
|
87 |
+
"gradient_boosting-2020": 4975,
|
88 |
+
"gradient_boosting-2021": 6060,
|
89 |
+
"gradient_boosting-2022": 6674,
|
90 |
+
"gradient_boosting-2023": 6827,
|
91 |
+
"gradient_boosting-2024": 7834,
|
92 |
+
"ada_boost-2015": 67,
|
93 |
+
"ada_boost-2016": 76,
|
94 |
+
"ada_boost-2017": 74,
|
95 |
+
"ada_boost-2018": 82,
|
96 |
+
"ada_boost-2019": 117,
|
97 |
+
"ada_boost-2020": 161,
|
98 |
+
"ada_boost-2021": 271,
|
99 |
+
"ada_boost-2022": 363,
|
100 |
+
"ada_boost-2023": 370,
|
101 |
+
"ada_boost-2024": 499,
|
102 |
+
"pca-2015": 6571,
|
103 |
+
"pca-2016": 6845,
|
104 |
+
"pca-2017": 7096,
|
105 |
+
"pca-2018": 7622,
|
106 |
+
"pca-2019": 8342,
|
107 |
+
"pca-2020": 9232,
|
108 |
+
"pca-2021": 10167,
|
109 |
+
"pca-2022": 10370,
|
110 |
+
"pca-2023": 9496,
|
111 |
+
"pca-2024": 9824,
|
112 |
+
"linear_regression-2015": 9508,
|
113 |
+
"linear_regression-2016": 10092,
|
114 |
+
"linear_regression-2017": 10640,
|
115 |
+
"linear_regression-2018": 11826,
|
116 |
+
"linear_regression-2019": 13019,
|
117 |
+
"linear_regression-2020": 15565,
|
118 |
+
"linear_regression-2021": 18227,
|
119 |
+
"linear_regression-2022": 19244,
|
120 |
+
"linear_regression-2023": 18409,
|
121 |
+
"linear_regression-2024": 19206,
|
122 |
+
"cnn-2015": 116,
|
123 |
+
"cnn-2016": 273,
|
124 |
+
"cnn-2017": 719,
|
125 |
+
"cnn-2018": 1676,
|
126 |
+
"cnn-2019": 2912,
|
127 |
+
"cnn-2020": 4256,
|
128 |
+
"cnn-2021": 6357,
|
129 |
+
"cnn-2022": 8084,
|
130 |
+
"cnn-2023": 7326,
|
131 |
+
"cnn-2024": 7451,
|
132 |
+
"lstm-2015": 21,
|
133 |
+
"lstm-2016": 40,
|
134 |
+
"lstm-2017": 117,
|
135 |
+
"lstm-2018": 272,
|
136 |
+
"lstm-2019": 513,
|
137 |
+
"lstm-2020": 773,
|
138 |
+
"lstm-2021": 1334,
|
139 |
+
"lstm-2022": 1969,
|
140 |
+
"lstm-2023": 1826,
|
141 |
+
"lstm-2024": 2059,
|
142 |
+
"transformer-2015": 111,
|
143 |
+
"transformer-2016": 108,
|
144 |
+
"transformer-2017": 102,
|
145 |
+
"transformer-2018": 138,
|
146 |
+
"transformer-2019": 184,
|
147 |
+
"transformer-2020": 354,
|
148 |
+
"transformer-2021": 683,
|
149 |
+
"transformer-2022": 1698,
|
150 |
+
"transformer-2023": 3208,
|
151 |
+
"transformer-2024": 4961,
|
152 |
+
"resnet-2015": 2,
|
153 |
+
"resnet-2016": 4,
|
154 |
+
"resnet-2017": 23,
|
155 |
+
"resnet-2018": 65,
|
156 |
+
"resnet-2019": 228,
|
157 |
+
"resnet-2020": 382,
|
158 |
+
"resnet-2021": 752,
|
159 |
+
"resnet-2022": 1102,
|
160 |
+
"resnet-2023": 1131,
|
161 |
+
"resnet-2024": 1205,
|
162 |
+
"unet-2015": 3,
|
163 |
+
"unet-2016": 2,
|
164 |
+
"unet-2017": 14,
|
165 |
+
"unet-2018": 108,
|
166 |
+
"unet-2019": 350,
|
167 |
+
"unet-2020": 670,
|
168 |
+
"unet-2021": 1103,
|
169 |
+
"unet-2022": 1552,
|
170 |
+
"unet-2023": 1698,
|
171 |
+
"unet-2024": 1818,
|
172 |
+
"gan-2015": 3093,
|
173 |
+
"gan-2016": 3349,
|
174 |
+
"gan-2017": 3312,
|
175 |
+
"gan-2018": 3863,
|
176 |
+
"gan-2019": 4344,
|
177 |
+
"gan-2020": 5126,
|
178 |
+
"gan-2021": 5986,
|
179 |
+
"gan-2022": 6676,
|
180 |
+
"gan-2023": 6611,
|
181 |
+
"gan-2024": 6944,
|
182 |
+
"autoencoder-2015": 44,
|
183 |
+
"autoencoder-2016": 102,
|
184 |
+
"autoencoder-2017": 162,
|
185 |
+
"autoencoder-2018": 300,
|
186 |
+
"autoencoder-2019": 397,
|
187 |
+
"autoencoder-2020": 563,
|
188 |
+
"autoencoder-2021": 821,
|
189 |
+
"autoencoder-2022": 1075,
|
190 |
+
"autoencoder-2023": 1181,
|
191 |
+
"autoencoder-2024": 1358,
|
192 |
+
"vgg-2015": 4,
|
193 |
+
"vgg-2016": 10,
|
194 |
+
"vgg-2017": 25,
|
195 |
+
"vgg-2018": 43,
|
196 |
+
"vgg-2019": 107,
|
197 |
+
"vgg-2020": 142,
|
198 |
+
"vgg-2021": 293,
|
199 |
+
"vgg-2022": 365,
|
200 |
+
"vgg-2023": 322,
|
201 |
+
"vgg-2024": 287,
|
202 |
+
"inception-2015": 551,
|
203 |
+
"inception-2016": 550,
|
204 |
+
"inception-2017": 696,
|
205 |
+
"inception-2018": 924,
|
206 |
+
"inception-2019": 1189,
|
207 |
+
"inception-2020": 1757,
|
208 |
+
"inception-2021": 1892,
|
209 |
+
"inception-2022": 1995,
|
210 |
+
"inception-2023": 1931,
|
211 |
+
"inception-2024": 2033,
|
212 |
+
"rnn-2015": 105,
|
213 |
+
"rnn-2016": 116,
|
214 |
+
"rnn-2017": 166,
|
215 |
+
"rnn-2018": 325,
|
216 |
+
"rnn-2019": 462,
|
217 |
+
"rnn-2020": 503,
|
218 |
+
"rnn-2021": 747,
|
219 |
+
"rnn-2022": 854,
|
220 |
+
"rnn-2023": 767,
|
221 |
+
"rnn-2024": 734,
|
222 |
+
"gru-2015": 307,
|
223 |
+
"gru-2016": 230,
|
224 |
+
"gru-2017": 89,
|
225 |
+
"gru-2018": 107,
|
226 |
+
"gru-2019": 138,
|
227 |
+
"gru-2020": 169,
|
228 |
+
"gru-2021": 297,
|
229 |
+
"gru-2022": 438,
|
230 |
+
"gru-2023": 447,
|
231 |
+
"gru-2024": 560,
|
232 |
+
"yolo-2015": 10,
|
233 |
+
"yolo-2016": 7,
|
234 |
+
"yolo-2017": 16,
|
235 |
+
"yolo-2018": 25,
|
236 |
+
"yolo-2019": 40,
|
237 |
+
"yolo-2020": 108,
|
238 |
+
"yolo-2021": 246,
|
239 |
+
"yolo-2022": 588,
|
240 |
+
"yolo-2023": 774,
|
241 |
+
"yolo-2024": 886,
|
242 |
+
"capsnet-2015": 8,
|
243 |
+
"capsnet-2016": 4,
|
244 |
+
"capsnet-2017": 5,
|
245 |
+
"capsnet-2018": 9,
|
246 |
+
"capsnet-2019": 22,
|
247 |
+
"capsnet-2020": 42,
|
248 |
+
"capsnet-2021": 72,
|
249 |
+
"capsnet-2022": 103,
|
250 |
+
"capsnet-2023": 82,
|
251 |
+
"capsnet-2024": 73,
|
252 |
+
"attention-2015": 22456,
|
253 |
+
"attention-2016": 23429,
|
254 |
+
"attention-2017": 24706,
|
255 |
+
"attention-2018": 26810,
|
256 |
+
"attention-2019": 29325,
|
257 |
+
"attention-2020": 35137,
|
258 |
+
"attention-2021": 40508,
|
259 |
+
"attention-2022": 44763,
|
260 |
+
"attention-2023": 44196,
|
261 |
+
"attention-2024": 46082,
|
262 |
+
"gpt-2015": 110,
|
263 |
+
"gpt-2016": 93,
|
264 |
+
"gpt-2017": 96,
|
265 |
+
"gpt-2018": 96,
|
266 |
+
"gpt-2019": 97,
|
267 |
+
"gpt-2020": 118,
|
268 |
+
"gpt-2021": 151,
|
269 |
+
"gpt-2022": 167,
|
270 |
+
"gpt-2023": 2314,
|
271 |
+
"gpt-2024": 4243,
|
272 |
+
"claude-2015": 1555,
|
273 |
+
"claude-2016": 1864,
|
274 |
+
"claude-2017": 2127,
|
275 |
+
"claude-2018": 2282,
|
276 |
+
"claude-2019": 2379,
|
277 |
+
"claude-2020": 2694,
|
278 |
+
"claude-2021": 3013,
|
279 |
+
"claude-2022": 2895,
|
280 |
+
"claude-2023": 2760,
|
281 |
+
"claude-2024": 2979,
|
282 |
+
"bert-2015": 147,
|
283 |
+
"bert-2016": 152,
|
284 |
+
"bert-2017": 129,
|
285 |
+
"bert-2018": 142,
|
286 |
+
"bert-2019": 141,
|
287 |
+
"bert-2020": 227,
|
288 |
+
"bert-2021": 391,
|
289 |
+
"bert-2022": 588,
|
290 |
+
"bert-2023": 577,
|
291 |
+
"bert-2024": 706,
|
292 |
+
"gemini-2015": 158,
|
293 |
+
"gemini-2016": 181,
|
294 |
+
"gemini-2017": 190,
|
295 |
+
"gemini-2018": 175,
|
296 |
+
"gemini-2019": 189,
|
297 |
+
"gemini-2020": 233,
|
298 |
+
"gemini-2021": 250,
|
299 |
+
"gemini-2022": 228,
|
300 |
+
"gemini-2023": 219,
|
301 |
+
"gemini-2024": 491,
|
302 |
+
"llama-2015": 50,
|
303 |
+
"llama-2016": 32,
|
304 |
+
"llama-2017": 50,
|
305 |
+
"llama-2018": 57,
|
306 |
+
"llama-2019": 46,
|
307 |
+
"llama-2020": 62,
|
308 |
+
"llama-2021": 72,
|
309 |
+
"llama-2022": 60,
|
310 |
+
"llama-2023": 66,
|
311 |
+
"llama-2024": 224,
|
312 |
+
"qwen-2015": 0,
|
313 |
+
"qwen-2016": 0,
|
314 |
+
"qwen-2017": 0,
|
315 |
+
"qwen-2018": 3,
|
316 |
+
"qwen-2019": 1,
|
317 |
+
"qwen-2020": 1,
|
318 |
+
"qwen-2021": 2,
|
319 |
+
"qwen-2022": 2,
|
320 |
+
"qwen-2023": 1,
|
321 |
+
"qwen-2024": 11,
|
322 |
+
"deepseek-2015": 0,
|
323 |
+
"deepseek-2016": 0,
|
324 |
+
"deepseek-2017": 0,
|
325 |
+
"deepseek-2018": 0,
|
326 |
+
"deepseek-2019": 0,
|
327 |
+
"deepseek-2020": 0,
|
328 |
+
"deepseek-2021": 0,
|
329 |
+
"deepseek-2022": 0,
|
330 |
+
"deepseek-2023": 0,
|
331 |
+
"deepseek-2024": 1,
|
332 |
+
"mistral-2015": 11,
|
333 |
+
"mistral-2016": 14,
|
334 |
+
"mistral-2017": 14,
|
335 |
+
"mistral-2018": 8,
|
336 |
+
"mistral-2019": 22,
|
337 |
+
"mistral-2020": 33,
|
338 |
+
"mistral-2021": 36,
|
339 |
+
"mistral-2022": 25,
|
340 |
+
"mistral-2023": 25,
|
341 |
+
"mistral-2024": 73,
|
342 |
+
"palm-2015": 1078,
|
343 |
+
"palm-2016": 1126,
|
344 |
+
"palm-2017": 1298,
|
345 |
+
"palm-2018": 1297,
|
346 |
+
"palm-2019": 1443,
|
347 |
+
"palm-2020": 1685,
|
348 |
+
"palm-2021": 1834,
|
349 |
+
"palm-2022": 1752,
|
350 |
+
"palm-2023": 1708,
|
351 |
+
"palm-2024": 1778,
|
352 |
+
"t5-2015": 554,
|
353 |
+
"t5-2016": 492,
|
354 |
+
"t5-2017": 601,
|
355 |
+
"t5-2018": 670,
|
356 |
+
"t5-2019": 699,
|
357 |
+
"t5-2020": 762,
|
358 |
+
"t5-2021": 850,
|
359 |
+
"t5-2022": 848,
|
360 |
+
"t5-2023": 890,
|
361 |
+
"t5-2024": 947,
|
362 |
+
"roberta-2015": 144,
|
363 |
+
"roberta-2016": 136,
|
364 |
+
"roberta-2017": 140,
|
365 |
+
"roberta-2018": 162,
|
366 |
+
"roberta-2019": 227,
|
367 |
+
"roberta-2020": 188,
|
368 |
+
"roberta-2021": 176,
|
369 |
+
"roberta-2022": 224,
|
370 |
+
"roberta-2023": 267,
|
371 |
+
"roberta-2024": 306,
|
372 |
+
"phi-2015": 758,
|
373 |
+
"phi-2016": 644,
|
374 |
+
"phi-2017": 812,
|
375 |
+
"phi-2018": 874,
|
376 |
+
"phi-2019": 937,
|
377 |
+
"phi-2020": 1028,
|
378 |
+
"phi-2021": 1094,
|
379 |
+
"phi-2022": 1167,
|
380 |
+
"phi-2023": 1172,
|
381 |
+
"phi-2024": 1230,
|
382 |
+
"falcon-2015": 117,
|
383 |
+
"falcon-2016": 122,
|
384 |
+
"falcon-2017": 116,
|
385 |
+
"falcon-2018": 135,
|
386 |
+
"falcon-2019": 154,
|
387 |
+
"falcon-2020": 178,
|
388 |
+
"falcon-2021": 215,
|
389 |
+
"falcon-2022": 181,
|
390 |
+
"falcon-2023": 184,
|
391 |
+
"falcon-2024": 182
|
392 |
+
}
|
frontend/index.html
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8" />
|
5 |
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
+
<title>PubMed AI Explorer</title>
|
8 |
+
</head>
|
9 |
+
<body>
|
10 |
+
<div id="root"></div>
|
11 |
+
<script type="module" src="/src/main.tsx"></script>
|
12 |
+
</body>
|
13 |
+
</html>
|
frontend/package-lock.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
frontend/package.json
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "pubmed-ai-explorer-frontend",
|
3 |
+
"version": "1.0.0",
|
4 |
+
"private": true,
|
5 |
+
"dependencies": {
|
6 |
+
"axios": "^1.6.2",
|
7 |
+
"lucide-react": "^0.294.0",
|
8 |
+
"react": "^18.2.0",
|
9 |
+
"react-dom": "^18.2.0",
|
10 |
+
"react-markdown": "^10.1.0",
|
11 |
+
"react-router-dom": "^6.8.1",
|
12 |
+
"recharts": "^2.8.0"
|
13 |
+
},
|
14 |
+
"scripts": {
|
15 |
+
"dev": "vite",
|
16 |
+
"build": "vite build",
|
17 |
+
"preview": "vite preview"
|
18 |
+
},
|
19 |
+
"devDependencies": {
|
20 |
+
"@types/react": "^18.2.43",
|
21 |
+
"@types/react-dom": "^18.2.17",
|
22 |
+
"@vitejs/plugin-react": "^4.2.1",
|
23 |
+
"autoprefixer": "^10.4.16",
|
24 |
+
"postcss": "^8.4.32",
|
25 |
+
"tailwindcss": "^3.3.6",
|
26 |
+
"typescript": "^5.2.2",
|
27 |
+
"vite": "^5.0.8"
|
28 |
+
}
|
29 |
+
}
|
frontend/postcss.config.js
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export default {
|
2 |
+
plugins: {
|
3 |
+
tailwindcss: {},
|
4 |
+
autoprefixer: {},
|
5 |
+
},
|
6 |
+
}
|
frontend/src/App.tsx
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react';
|
2 |
+
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
3 |
+
import Navbar from './components/Navbar';
|
4 |
+
import Dashboard from './pages/Dashboard';
|
5 |
+
import Search from './pages/Search';
|
6 |
+
import Timeline from './pages/Timeline';
|
7 |
+
import Roadmap from './pages/Roadmap';
|
8 |
+
import AlgorithmDetail from './pages/AlgorithmDetail';
|
9 |
+
|
10 |
+
function App() {
|
11 |
+
return (
|
12 |
+
<Router>
|
13 |
+
<div className="min-h-screen bg-gray-50 flex flex-col">
|
14 |
+
<Navbar />
|
15 |
+
<main className="container mx-auto px-4 py-8 flex-grow">
|
16 |
+
<Routes>
|
17 |
+
<Route path="/" element={<Dashboard />} />
|
18 |
+
<Route path="/search" element={<Search />} />
|
19 |
+
<Route path="/timeline" element={<Timeline />} />
|
20 |
+
<Route path="/roadmap" element={<Roadmap />} />
|
21 |
+
<Route path="/algorithm/:algorithmId" element={<AlgorithmDetail />} />
|
22 |
+
</Routes>
|
23 |
+
</main>
|
24 |
+
<footer className="bg-white border-t border-gray-200 py-4">
|
25 |
+
<div className="container mx-auto px-4 text-center text-sm text-gray-600">
|
26 |
+
made by{' '}
|
27 |
+
<a
|
28 |
+
href="https://www.linkedin.com/in/aleksander-obuchowski/"
|
29 |
+
target="_blank"
|
30 |
+
rel="noopener noreferrer"
|
31 |
+
className="text-blue-600 hover:text-blue-800 underline"
|
32 |
+
>
|
33 |
+
Aleksander Obuchowski
|
34 |
+
</a>
|
35 |
+
{' '}@{' '}
|
36 |
+
<a
|
37 |
+
href="https://thelion.ai"
|
38 |
+
target="_blank"
|
39 |
+
rel="noopener noreferrer"
|
40 |
+
className="text-blue-600 hover:text-blue-800 underline"
|
41 |
+
>
|
42 |
+
thelion.ai
|
43 |
+
</a>
|
44 |
+
</div>
|
45 |
+
</footer>
|
46 |
+
</div>
|
47 |
+
</Router>
|
48 |
+
);
|
49 |
+
}
|
50 |
+
|
51 |
+
export default App;
|
frontend/src/components/AlgorithmCard.tsx
ADDED
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState, useEffect } from 'react';
|
2 |
+
import { ExternalLink, FileText, Loader2, Brain, Cpu, MessageSquare } from 'lucide-react';
|
3 |
+
import { Link } from 'react-router-dom';
|
4 |
+
import api from '../services/api';
|
5 |
+
|
6 |
+
interface Algorithm {
|
7 |
+
algorithm: string;
|
8 |
+
name: string;
|
9 |
+
category: string;
|
10 |
+
description: string;
|
11 |
+
count: number;
|
12 |
+
sampleIds: string[];
|
13 |
+
}
|
14 |
+
|
15 |
+
interface Paper {
|
16 |
+
pmid: string;
|
17 |
+
title: string;
|
18 |
+
authors: string[];
|
19 |
+
journal: string;
|
20 |
+
pubDate: string;
|
21 |
+
}
|
22 |
+
|
23 |
+
interface AlgorithmCardProps {
|
24 |
+
algorithm: Algorithm;
|
25 |
+
problem: string;
|
26 |
+
onSeeMorePapers: (algorithm: string) => void;
|
27 |
+
}
|
28 |
+
|
29 |
+
const AlgorithmCard: React.FC<AlgorithmCardProps> = ({ algorithm, problem, onSeeMorePapers }) => {
|
30 |
+
const [samplePapers, setSamplePapers] = useState<Paper[]>([]);
|
31 |
+
const [loadingPapers, setLoadingPapers] = useState(false);
|
32 |
+
|
33 |
+
useEffect(() => {
|
34 |
+
// Temporarily disabled to avoid PubMed API rate limiting issues
|
35 |
+
// if (algorithm.sampleIds.length > 0) {
|
36 |
+
// fetchSamplePapers();
|
37 |
+
// }
|
38 |
+
}, [algorithm.sampleIds]);
|
39 |
+
|
40 |
+
const fetchSamplePapers = async () => {
|
41 |
+
try {
|
42 |
+
setLoadingPapers(true);
|
43 |
+
console.log(`[${algorithm.name}] Fetching sample papers for IDs:`, algorithm.sampleIds);
|
44 |
+
|
45 |
+
const papers = [];
|
46 |
+
// Fetch papers sequentially with delays to avoid rate limiting
|
47 |
+
for (let i = 0; i < algorithm.sampleIds.length && i < 2; i++) {
|
48 |
+
try {
|
49 |
+
console.log(`[${algorithm.name}] Fetching paper ${i + 1}/${Math.min(algorithm.sampleIds.length, 2)}: ${algorithm.sampleIds[i]}`);
|
50 |
+
|
51 |
+
if (i > 0) {
|
52 |
+
// Add delay between requests to avoid rate limiting
|
53 |
+
await new Promise(resolve => setTimeout(resolve, 500));
|
54 |
+
}
|
55 |
+
|
56 |
+
const response = await api.get(`/pubmed/paper/${algorithm.sampleIds[i]}`);
|
57 |
+
console.log(`[${algorithm.name}] Response for ${algorithm.sampleIds[i]}:`, response.status, response.data);
|
58 |
+
|
59 |
+
if (response.data && response.data.title) {
|
60 |
+
papers.push(response.data);
|
61 |
+
console.log(`[${algorithm.name}] Successfully added paper: ${response.data.title}`);
|
62 |
+
} else {
|
63 |
+
console.log(`[${algorithm.name}] Invalid paper data for ${algorithm.sampleIds[i]}:`, response.data);
|
64 |
+
}
|
65 |
+
} catch (error) {
|
66 |
+
console.error(`[${algorithm.name}] Failed to fetch paper ${algorithm.sampleIds[i]}:`, {
|
67 |
+
status: error.response?.status,
|
68 |
+
statusText: error.response?.statusText,
|
69 |
+
data: error.response?.data,
|
70 |
+
message: error.message
|
71 |
+
});
|
72 |
+
// Continue with next paper if one fails
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
console.log(`[${algorithm.name}] Final fetched papers:`, papers);
|
77 |
+
setSamplePapers(papers);
|
78 |
+
} catch (error) {
|
79 |
+
console.error(`[${algorithm.name}] Failed to fetch sample papers:`, error);
|
80 |
+
} finally {
|
81 |
+
setLoadingPapers(false);
|
82 |
+
}
|
83 |
+
};
|
84 |
+
|
85 |
+
const getCategoryIcon = (category: string) => {
|
86 |
+
switch (category) {
|
87 |
+
case 'deep_learning':
|
88 |
+
return <Cpu className="h-5 w-5 text-purple-600" />;
|
89 |
+
case 'llms':
|
90 |
+
return <MessageSquare className="h-5 w-5 text-green-600" />;
|
91 |
+
default:
|
92 |
+
return <Brain className="h-5 w-5 text-blue-600" />;
|
93 |
+
}
|
94 |
+
};
|
95 |
+
|
96 |
+
const getCategoryColor = (category: string) => {
|
97 |
+
switch (category) {
|
98 |
+
case 'deep_learning':
|
99 |
+
return 'bg-purple-100 text-purple-800';
|
100 |
+
case 'llms':
|
101 |
+
return 'bg-green-100 text-green-800';
|
102 |
+
default:
|
103 |
+
return 'bg-blue-100 text-blue-800';
|
104 |
+
}
|
105 |
+
};
|
106 |
+
|
107 |
+
return (
|
108 |
+
<div className="bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow duration-200 overflow-hidden h-full flex flex-col">
|
109 |
+
<div className="p-6 flex-grow">
|
110 |
+
<div className="flex items-start justify-between mb-4">
|
111 |
+
<div className="flex items-center space-x-3">
|
112 |
+
{getCategoryIcon(algorithm.category)}
|
113 |
+
<Link
|
114 |
+
to={`/algorithm/${algorithm.algorithm}`}
|
115 |
+
className="text-lg font-semibold text-gray-900 leading-tight hover:text-blue-600 transition-colors"
|
116 |
+
>
|
117 |
+
{algorithm.name}
|
118 |
+
</Link>
|
119 |
+
</div>
|
120 |
+
<span className={`px-3 py-1 rounded-full text-xs font-semibold ${getCategoryColor(algorithm.category)} whitespace-nowrap ml-2`}>
|
121 |
+
{algorithm.category === 'deep_learning' ? 'Deep Learning' : algorithm.category === 'llms' ? 'LLMs' : 'Classical ML'}
|
122 |
+
</span>
|
123 |
+
</div>
|
124 |
+
|
125 |
+
<p className="text-gray-600 text-sm mb-4">{algorithm.description}</p>
|
126 |
+
|
127 |
+
<div className="mb-4">
|
128 |
+
<div className="text-center bg-gray-50 rounded-lg p-4">
|
129 |
+
<div className="text-3xl font-bold text-gray-900">{algorithm.count.toLocaleString()}</div>
|
130 |
+
<div className="text-sm text-gray-500 font-medium">Papers Found</div>
|
131 |
+
</div>
|
132 |
+
</div>
|
133 |
+
|
134 |
+
{algorithm.sampleIds.length > 0 && (
|
135 |
+
<div className="mb-4">
|
136 |
+
<h4 className="text-sm font-medium text-gray-900 mb-2">Sample Papers:</h4>
|
137 |
+
{loadingPapers ? (
|
138 |
+
<div className="flex items-center justify-center py-4">
|
139 |
+
<Loader2 className="h-4 w-4 animate-spin text-gray-400" />
|
140 |
+
<span className="ml-2 text-sm text-gray-500">Loading papers...</span>
|
141 |
+
</div>
|
142 |
+
) : samplePapers.length > 0 ? (
|
143 |
+
<div className="space-y-3">
|
144 |
+
{samplePapers.slice(0, 2).map((paper) => (
|
145 |
+
<div key={paper.pmid} className="p-3 bg-gray-50 rounded-lg border border-gray-100">
|
146 |
+
<p className="font-medium text-gray-900 text-sm line-clamp-2 mb-1">{paper.title}</p>
|
147 |
+
<p className="text-gray-500 text-xs">
|
148 |
+
{paper.journal} {paper.pubDate && `(${paper.pubDate})`}
|
149 |
+
</p>
|
150 |
+
</div>
|
151 |
+
))}
|
152 |
+
</div>
|
153 |
+
) : (
|
154 |
+
<div className="p-3 bg-blue-50 rounded-lg border border-blue-100">
|
155 |
+
<p className="text-xs text-blue-600 text-center">
|
156 |
+
{algorithm.count} papers found - Click "See More Papers on PubMed" to view
|
157 |
+
</p>
|
158 |
+
</div>
|
159 |
+
)}
|
160 |
+
</div>
|
161 |
+
)}
|
162 |
+
|
163 |
+
</div>
|
164 |
+
<div className="p-6 pt-0 mt-auto">
|
165 |
+
<button
|
166 |
+
onClick={() => onSeeMorePapers(algorithm.algorithm)}
|
167 |
+
className="w-full bg-blue-600 text-white py-3 px-4 rounded-lg hover:bg-blue-700 transition-colors flex items-center justify-center text-sm font-medium shadow-sm"
|
168 |
+
>
|
169 |
+
<ExternalLink className="h-4 w-4 mr-2" />
|
170 |
+
See More Papers on PubMed
|
171 |
+
</button>
|
172 |
+
</div>
|
173 |
+
</div>
|
174 |
+
);
|
175 |
+
};
|
176 |
+
|
177 |
+
export default AlgorithmCard;
|
frontend/src/components/Navbar.tsx
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react';
|
2 |
+
import { Link, useLocation } from 'react-router-dom';
|
3 |
+
import { BarChart3, Search, Calendar, Map } from 'lucide-react';
|
4 |
+
|
5 |
+
const Navbar: React.FC = () => {
|
6 |
+
const location = useLocation();
|
7 |
+
|
8 |
+
return (
|
9 |
+
<nav className="bg-white shadow-lg">
|
10 |
+
<div className="container mx-auto px-4">
|
11 |
+
<div className="flex justify-between h-16">
|
12 |
+
<div className="flex items-center">
|
13 |
+
<Link to="/" className="flex items-center space-x-2">
|
14 |
+
<BarChart3 className="h-8 w-8 text-blue-600" />
|
15 |
+
<span className="text-xl font-bold text-gray-900">PubMed AI Explorer</span>
|
16 |
+
</Link>
|
17 |
+
</div>
|
18 |
+
|
19 |
+
<div className="flex items-center space-x-4">
|
20 |
+
<Link
|
21 |
+
to="/"
|
22 |
+
className={`flex items-center space-x-1 px-3 py-2 rounded-md text-sm font-medium ${
|
23 |
+
location.pathname === '/'
|
24 |
+
? 'bg-blue-100 text-blue-700'
|
25 |
+
: 'text-gray-500 hover:text-gray-700'
|
26 |
+
}`}
|
27 |
+
>
|
28 |
+
<BarChart3 className="h-4 w-4" />
|
29 |
+
<span>Dashboard</span>
|
30 |
+
</Link>
|
31 |
+
|
32 |
+
<Link
|
33 |
+
to="/search"
|
34 |
+
className={`flex items-center space-x-1 px-3 py-2 rounded-md text-sm font-medium ${
|
35 |
+
location.pathname === '/search'
|
36 |
+
? 'bg-blue-100 text-blue-700'
|
37 |
+
: 'text-gray-500 hover:text-gray-700'
|
38 |
+
}`}
|
39 |
+
>
|
40 |
+
<Search className="h-4 w-4" />
|
41 |
+
<span>Search</span>
|
42 |
+
</Link>
|
43 |
+
|
44 |
+
<Link
|
45 |
+
to="/timeline"
|
46 |
+
className={`flex items-center space-x-1 px-3 py-2 rounded-md text-sm font-medium ${
|
47 |
+
location.pathname === '/timeline'
|
48 |
+
? 'bg-blue-100 text-blue-700'
|
49 |
+
: 'text-gray-500 hover:text-gray-700'
|
50 |
+
}`}
|
51 |
+
>
|
52 |
+
<Calendar className="h-4 w-4" />
|
53 |
+
<span>Timeline</span>
|
54 |
+
</Link>
|
55 |
+
|
56 |
+
<Link
|
57 |
+
to="/roadmap"
|
58 |
+
className={`flex items-center space-x-1 px-3 py-2 rounded-md text-sm font-medium ${
|
59 |
+
location.pathname === '/roadmap'
|
60 |
+
? 'bg-blue-100 text-blue-700'
|
61 |
+
: 'text-gray-500 hover:text-gray-700'
|
62 |
+
}`}
|
63 |
+
>
|
64 |
+
<Map className="h-4 w-4" />
|
65 |
+
<span>Roadmap</span>
|
66 |
+
</Link>
|
67 |
+
</div>
|
68 |
+
</div>
|
69 |
+
</div>
|
70 |
+
</nav>
|
71 |
+
);
|
72 |
+
};
|
73 |
+
|
74 |
+
export default Navbar;
|
frontend/src/docs/cnn.md
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Convolutional Neural Networks (CNNs) are a sophisticated machine learning technique that has found extensive applications in medical image analysis, disease diagnosis, and scientific research. For medical professionals, understanding the basics of how CNNs work can be crucial for effectively utilizing AI-based diagnostic tools. In this article, we will explain how convolutional neural networks operate and why they are so useful in medicine.
|
2 |
+
|
3 |
+
### Basics of Convolutional Neural Networks
|
4 |
+
|
5 |
+
#### 1. Architecture of CNN
|
6 |
+
|
7 |
+
Convolutional Neural Networks consist of several layers, each serving a specific function:
|
8 |
+
|
9 |
+
- **Convolutional Layer:** This is the core of a CNN. This layer applies a set of filters (or kernels) to the input image to extract features such as edges, textures, and patterns. Each filter moves across the image, performing a convolution operation, which involves multiplying pixel values by the filter weights and summing the results.
|
10 |
+
|
11 |
+
- **ReLU Layer (Rectified Linear Unit):** After the convolution operation, a ReLU activation function is applied, introducing non-linearity to the model. ReLU replaces all negative values with zeros, which speeds up the learning process and improves the network's performance.
|
12 |
+
|
13 |
+
- **Pooling Layer:** This layer reduces the dimensionality of the data while retaining the most important information. The most commonly used type of pooling is max pooling, which selects the maximum value from a specific area, reducing the image size and the number of parameters, which translates to lower computational resource usage.
|
14 |
+
|
15 |
+
- **Fully Connected Layer:** At the end of the network, there are fully connected layers that combine all the extracted features and make the final classification. These layers work similarly to traditional neural networks, where each neuron is connected to every neuron in the previous layer.
|
16 |
+
|
17 |
+
#### 2. Learning Process
|
18 |
+
|
19 |
+
The learning process of a CNN involves adjusting the weights of the filters to minimize the error between the network's predictions and the actual labels of the training data. This is achieved using the backpropagation algorithm and various optimizers such as Stochastic Gradient Descent (SGD) or Adam.
|
20 |
+
|
21 |
+
### Applications in Medicine
|
22 |
+
|
23 |
+
#### 1. Diagnostic Imaging
|
24 |
+
|
25 |
+
One of the most important applications of CNNs in medicine is the analysis of medical images such as X-rays, computed tomography (CT) scans, and magnetic resonance imaging (MRI). These networks can detect abnormalities such as tumors, fractures, or pathological changes with high accuracy, often matching or surpassing human capabilities.
|
26 |
+
|
27 |
+
#### 2. Image Segmentation
|
28 |
+
|
29 |
+
CNNs are also used for segmenting medical images, dividing the image into different regions corresponding to various tissues or anatomical structures. This is particularly useful in treatment planning and monitoring disease progression.
|
30 |
+
|
31 |
+
#### 3. Disease Classification
|
32 |
+
|
33 |
+
Convolutional Neural Networks can classify medical images into different disease categories. For example, they can distinguish healthy tissue from diseased tissue, identify various stages of disease, or classify types of tumors.
|
34 |
+
|
35 |
+
### Benefits of Using CNNs in Medicine
|
36 |
+
|
37 |
+
- **Improved Diagnostic Accuracy:** CNNs can detect subtle patterns and features that may be difficult for the human eye to notice.
|
38 |
+
- **Speed and Efficiency:** Automating the analysis of medical images with CNNs can significantly speed up the diagnostic process, allowing doctors to focus on other aspects of patient care.
|
39 |
+
- **Reduction of Errors:** Decreasing the risk of diagnostic errors by supporting doctors' decisions with artificial intelligence.
|
40 |
+
|
41 |
+
### Challenges and Limitations
|
42 |
+
|
43 |
+
Despite many advantages, the application of CNNs in medicine also comes with certain challenges:
|
44 |
+
|
45 |
+
- **Need for Large Datasets:** CNNs require large amounts of training data, which must be appropriately labeled by experts. In medicine, obtaining such data can be difficult and expensive.
|
46 |
+
- **Interpretability of Models:** CNNs are often seen as "black boxes," meaning their decisions can be difficult for doctors to interpret.
|
47 |
+
- **Ethical and Legal Issues:** The use of artificial intelligence in medicine raises questions about responsibility for diagnostic errors, patient data protection, and the ethics of using algorithms in the decision-making process.
|
48 |
+
|
49 |
+
### Conclusion
|
50 |
+
|
51 |
+
Convolutional Neural Networks are a powerful tool that can significantly aid medical professionals, improving the accuracy and speed of diagnostics. Despite the challenges associated with their implementation, the potential of CNNs in medicine is vast, and it is worth investing in further research and development of this technology. For doctors, understanding the basics of how CNNs work can be key to effectively using AI-based tools and better serving their patients.
|
frontend/src/docs/linear_regression.md
ADDED
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Linear Regression
|
2 |
+
|
3 |
+
**Category:** Classical Machine Learning
|
4 |
+
|
5 |
+
## Overview
|
6 |
+
|
7 |
+
Linear regression establishes a mathematical relationship between predictor variables and a continuous outcome variable. The algorithm identifies the optimal linear combination of input features that best predicts the target variable by minimizing the sum of squared residuals between predicted and observed values.
|
8 |
+
|
9 |
+
## Mathematical Foundation
|
10 |
+
|
11 |
+
### Linear Model Equation
|
12 |
+
```
|
13 |
+
y = β₀ + β₁x₁ + β₂x₂ + ... + βₚxₚ + ε
|
14 |
+
```
|
15 |
+
|
16 |
+
Where:
|
17 |
+
- **y** = dependent variable (outcome)
|
18 |
+
- **β₀** = intercept (baseline value when all predictors = 0)
|
19 |
+
- **β₁, β₂, ..., βₚ** = regression coefficients
|
20 |
+
- **x₁, x₂, ..., xₚ** = independent variables (predictors)
|
21 |
+
- **ε** = error term (residual)
|
22 |
+
|
23 |
+
### Ordinary Least Squares (OLS)
|
24 |
+
|
25 |
+
The method minimizes the sum of squared residuals:
|
26 |
+
```
|
27 |
+
minimize: Σ(yᵢ - ŷᵢ)²
|
28 |
+
```
|
29 |
+
|
30 |
+
This optimization finds coefficient values that provide the best linear fit to the observed data.
|
31 |
+
|
32 |
+
## Medical Applications
|
33 |
+
|
34 |
+
### Clinical Research
|
35 |
+
- **Dose-response modeling** for pharmaceutical studies
|
36 |
+
- **Biomarker relationship analysis** in clinical trials
|
37 |
+
- **Treatment outcome prediction** based on patient characteristics
|
38 |
+
- **Healthcare cost prediction** models
|
39 |
+
|
40 |
+
### Diagnostic Applications
|
41 |
+
- Predicting laboratory values based on patient demographics
|
42 |
+
- Modeling disease progression rates
|
43 |
+
- Risk factor quantification for preventive medicine
|
44 |
+
|
45 |
+
### Epidemiological Studies
|
46 |
+
- Population health trend analysis
|
47 |
+
- Environmental factor impact assessment
|
48 |
+
- Disease prevalence modeling
|
49 |
+
|
50 |
+
## Key Assumptions
|
51 |
+
|
52 |
+
### 1. Linearity
|
53 |
+
The relationship between independent and dependent variables must be linear. Non-linear relationships require transformation or alternative methods.
|
54 |
+
|
55 |
+
### 2. Independence
|
56 |
+
Observations must be independent of each other. Violations occur in:
|
57 |
+
- Repeated measures data
|
58 |
+
- Clustered sampling
|
59 |
+
- Time series data
|
60 |
+
|
61 |
+
### 3. Homoscedasticity
|
62 |
+
Residual variance should be constant across all fitted values. Heteroscedasticity can be detected through:
|
63 |
+
- Residual plots
|
64 |
+
- Breusch-Pagan test
|
65 |
+
- White test
|
66 |
+
|
67 |
+
### 4. Normality of Residuals
|
68 |
+
Residuals should follow a normal distribution, particularly important for:
|
69 |
+
- Confidence intervals
|
70 |
+
- Hypothesis testing
|
71 |
+
- Prediction intervals
|
72 |
+
|
73 |
+
## Clinical Considerations
|
74 |
+
|
75 |
+
### Multicollinearity
|
76 |
+
**Problem:** Highly correlated predictors can make coefficient interpretation unreliable.
|
77 |
+
|
78 |
+
**Solutions:**
|
79 |
+
- Variance Inflation Factor (VIF) assessment
|
80 |
+
- Principal component analysis
|
81 |
+
- Ridge or LASSO regression
|
82 |
+
|
83 |
+
### Outliers and Influential Points
|
84 |
+
**Detection Methods:**
|
85 |
+
- Cook's distance
|
86 |
+
- Leverage values
|
87 |
+
- Studentized residuals
|
88 |
+
|
89 |
+
**Impact:** Single outliers can significantly alter regression coefficients and predictions.
|
90 |
+
|
91 |
+
### Sample Size Requirements
|
92 |
+
**General Rule:** Minimum 10-15 observations per predictor variable.
|
93 |
+
|
94 |
+
**Considerations:**
|
95 |
+
- Effect size expectations
|
96 |
+
- Desired statistical power
|
97 |
+
- Expected R² value
|
98 |
+
|
99 |
+
### Model Validation
|
100 |
+
- **Cross-validation** for generalizability assessment
|
101 |
+
- **Hold-out validation** sets
|
102 |
+
- **Bootstrap methods** for confidence intervals
|
103 |
+
|
104 |
+
## Performance Metrics
|
105 |
+
|
106 |
+
### R² (Coefficient of Determination)
|
107 |
+
- **Range:** 0 to 1
|
108 |
+
- **Interpretation:** Proportion of variance explained by the model
|
109 |
+
- **Medical Context:** Higher values indicate better predictive capability
|
110 |
+
|
111 |
+
### Root Mean Square Error (RMSE)
|
112 |
+
- **Units:** Same as dependent variable
|
113 |
+
- **Interpretation:** Average prediction error magnitude
|
114 |
+
- **Clinical Relevance:** Should be within acceptable clinical tolerance
|
115 |
+
|
116 |
+
### F-statistic
|
117 |
+
- **Purpose:** Tests overall model significance
|
118 |
+
- **Interpretation:** Higher values with low p-values indicate significant predictive ability
|
119 |
+
- **Threshold:** p < 0.05 for statistical significance
|
120 |
+
|
121 |
+
### Adjusted R²
|
122 |
+
- **Advantage:** Penalizes for additional predictors
|
123 |
+
- **Use Case:** Model comparison with different numbers of variables
|
124 |
+
|
125 |
+
## Limitations in Medical Context
|
126 |
+
|
127 |
+
### Extrapolation Risks
|
128 |
+
Predictions outside the observed data range may be unreliable, particularly concerning for:
|
129 |
+
- Extreme dosages
|
130 |
+
- Unusual patient populations
|
131 |
+
- Novel treatment combinations
|
132 |
+
|
133 |
+
### Assumption Violations
|
134 |
+
Many medical datasets violate linear regression assumptions:
|
135 |
+
- Non-linear dose-response relationships
|
136 |
+
- Heteroscedastic error patterns
|
137 |
+
- Non-normal distributions
|
138 |
+
|
139 |
+
### Interpretation Challenges
|
140 |
+
- Correlation vs. causation distinctions
|
141 |
+
- Confounding variable effects
|
142 |
+
- Clinical vs. statistical significance
|
143 |
+
|
144 |
+
## Best Practices for Medical Applications
|
145 |
+
|
146 |
+
### Model Development
|
147 |
+
1. Conduct thorough exploratory data analysis
|
148 |
+
2. Test all assumptions before model fitting
|
149 |
+
3. Consider clinical knowledge in variable selection
|
150 |
+
4. Validate findings with independent datasets
|
151 |
+
|
152 |
+
### Reporting Standards
|
153 |
+
1. Report confidence intervals alongside point estimates
|
154 |
+
2. Discuss clinical significance of coefficients
|
155 |
+
3. Acknowledge model limitations
|
156 |
+
4. Provide goodness-of-fit statistics
|
157 |
+
|
158 |
+
### Clinical Implementation
|
159 |
+
1. Validate in target population
|
160 |
+
2. Consider integration with clinical workflow
|
161 |
+
3. Monitor performance over time
|
162 |
+
4. Update models as new data becomes available
|
frontend/src/index.css
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
@tailwind base;
|
2 |
+
@tailwind components;
|
3 |
+
@tailwind utilities;
|
frontend/src/main.tsx
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react'
|
2 |
+
import ReactDOM from 'react-dom/client'
|
3 |
+
import App from './App.tsx'
|
4 |
+
import './index.css'
|
5 |
+
|
6 |
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
7 |
+
<React.StrictMode>
|
8 |
+
<App />
|
9 |
+
</React.StrictMode>,
|
10 |
+
)
|
frontend/src/pages/AlgorithmDetail.tsx
ADDED
@@ -0,0 +1,262 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState, useEffect } from 'react';
|
2 |
+
import { useParams, useNavigate } from 'react-router-dom';
|
3 |
+
import { ArrowLeft, Brain, Cpu, Loader2 } from 'lucide-react';
|
4 |
+
import ReactMarkdown from 'react-markdown';
|
5 |
+
|
6 |
+
// Import markdown content
|
7 |
+
import linearRegressionMd from '../docs/linear_regression.md?raw';
|
8 |
+
import cnnMd from '../docs/cnn.md?raw';
|
9 |
+
|
10 |
+
const AlgorithmDetail: React.FC = () => {
|
11 |
+
const { algorithmId } = useParams<{ algorithmId: string }>();
|
12 |
+
const navigate = useNavigate();
|
13 |
+
const [markdownContent, setMarkdownContent] = useState<string>('');
|
14 |
+
const [loading, setLoading] = useState(true);
|
15 |
+
const [error, setError] = useState<string | null>(null);
|
16 |
+
|
17 |
+
useEffect(() => {
|
18 |
+
loadMarkdownContent();
|
19 |
+
}, [algorithmId]);
|
20 |
+
|
21 |
+
const loadMarkdownContent = async () => {
|
22 |
+
try {
|
23 |
+
setLoading(true);
|
24 |
+
setError(null);
|
25 |
+
|
26 |
+
let content = '';
|
27 |
+
switch (algorithmId) {
|
28 |
+
case 'linear_regression':
|
29 |
+
content = linearRegressionMd;
|
30 |
+
break;
|
31 |
+
case 'cnn':
|
32 |
+
content = cnnMd;
|
33 |
+
break;
|
34 |
+
default:
|
35 |
+
setError('Algorithm documentation not found');
|
36 |
+
setLoading(false);
|
37 |
+
return;
|
38 |
+
}
|
39 |
+
|
40 |
+
setMarkdownContent(content);
|
41 |
+
} catch (err) {
|
42 |
+
setError('Failed to load algorithm documentation');
|
43 |
+
} finally {
|
44 |
+
setLoading(false);
|
45 |
+
}
|
46 |
+
};
|
47 |
+
|
48 |
+
const getAlgorithmInfo = () => {
|
49 |
+
switch (algorithmId) {
|
50 |
+
case 'linear_regression':
|
51 |
+
return {
|
52 |
+
name: 'Linear Regression',
|
53 |
+
category: 'Classical ML',
|
54 |
+
icon: <Brain className="h-8 w-8 text-blue-600" />,
|
55 |
+
categoryColor: 'bg-blue-100 text-blue-800'
|
56 |
+
};
|
57 |
+
case 'cnn':
|
58 |
+
return {
|
59 |
+
name: 'Convolutional Neural Network',
|
60 |
+
category: 'Deep Learning',
|
61 |
+
icon: <Cpu className="h-8 w-8 text-purple-600" />,
|
62 |
+
categoryColor: 'bg-purple-100 text-purple-800'
|
63 |
+
};
|
64 |
+
default:
|
65 |
+
return {
|
66 |
+
name: 'Unknown Algorithm',
|
67 |
+
category: 'Unknown',
|
68 |
+
icon: <Brain className="h-8 w-8 text-gray-600" />,
|
69 |
+
categoryColor: 'bg-gray-100 text-gray-800'
|
70 |
+
};
|
71 |
+
}
|
72 |
+
};
|
73 |
+
|
74 |
+
const algorithmInfo = getAlgorithmInfo();
|
75 |
+
|
76 |
+
if (loading) {
|
77 |
+
return (
|
78 |
+
<div className="max-w-4xl mx-auto">
|
79 |
+
<button
|
80 |
+
onClick={() => navigate(-1)}
|
81 |
+
className="mb-6 flex items-center text-blue-600 hover:text-blue-800 transition-colors"
|
82 |
+
>
|
83 |
+
<ArrowLeft className="h-4 w-4 mr-2" />
|
84 |
+
Back
|
85 |
+
</button>
|
86 |
+
<div className="flex justify-center items-center h-64">
|
87 |
+
<Loader2 className="h-8 w-8 animate-spin text-blue-600" />
|
88 |
+
<span className="ml-2 text-gray-600">Loading documentation...</span>
|
89 |
+
</div>
|
90 |
+
</div>
|
91 |
+
);
|
92 |
+
}
|
93 |
+
|
94 |
+
if (error) {
|
95 |
+
return (
|
96 |
+
<div className="max-w-4xl mx-auto">
|
97 |
+
<button
|
98 |
+
onClick={() => navigate(-1)}
|
99 |
+
className="mb-6 flex items-center text-blue-600 hover:text-blue-800 transition-colors"
|
100 |
+
>
|
101 |
+
<ArrowLeft className="h-4 w-4 mr-2" />
|
102 |
+
Back
|
103 |
+
</button>
|
104 |
+
<div className="bg-white rounded-lg shadow-md p-8 text-center">
|
105 |
+
<h1 className="text-2xl font-bold text-gray-900 mb-4">Documentation Not Available</h1>
|
106 |
+
<p className="text-gray-600 mb-4">{error}</p>
|
107 |
+
<button
|
108 |
+
onClick={loadMarkdownContent}
|
109 |
+
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
|
110 |
+
>
|
111 |
+
Try Again
|
112 |
+
</button>
|
113 |
+
</div>
|
114 |
+
</div>
|
115 |
+
);
|
116 |
+
}
|
117 |
+
|
118 |
+
return (
|
119 |
+
<div className="max-w-4xl mx-auto">
|
120 |
+
<button
|
121 |
+
onClick={() => navigate(-1)}
|
122 |
+
className="mb-6 flex items-center text-blue-600 hover:text-blue-800 transition-colors"
|
123 |
+
>
|
124 |
+
<ArrowLeft className="h-4 w-4 mr-2" />
|
125 |
+
Back
|
126 |
+
</button>
|
127 |
+
|
128 |
+
{/* Algorithm Header */}
|
129 |
+
<div className="bg-white rounded-lg shadow-md p-6 mb-6">
|
130 |
+
<div className="flex items-center justify-between">
|
131 |
+
<div className="flex items-center">
|
132 |
+
{algorithmInfo.icon}
|
133 |
+
<div className="ml-4">
|
134 |
+
<h1 className="text-2xl font-bold text-gray-900">{algorithmInfo.name}</h1>
|
135 |
+
</div>
|
136 |
+
</div>
|
137 |
+
<span className={`px-3 py-1 rounded-full text-sm font-semibold ${algorithmInfo.categoryColor}`}>
|
138 |
+
{algorithmInfo.category}
|
139 |
+
</span>
|
140 |
+
</div>
|
141 |
+
</div>
|
142 |
+
|
143 |
+
{/* Markdown Content */}
|
144 |
+
<div className="bg-white rounded-lg shadow-md p-8">
|
145 |
+
<div className="prose prose-lg max-w-none">
|
146 |
+
<ReactMarkdown
|
147 |
+
components={{
|
148 |
+
h1: ({ children }) => (
|
149 |
+
<h1 className="text-3xl font-bold text-gray-900 mb-6 border-b-2 border-gray-200 pb-2">
|
150 |
+
{children}
|
151 |
+
</h1>
|
152 |
+
),
|
153 |
+
h2: ({ children }) => (
|
154 |
+
<h2 className="text-2xl font-semibold text-gray-900 mt-8 mb-4 border-b border-gray-200 pb-2">
|
155 |
+
{children}
|
156 |
+
</h2>
|
157 |
+
),
|
158 |
+
h3: ({ children }) => (
|
159 |
+
<h3 className="text-xl font-semibold text-gray-900 mt-6 mb-3">
|
160 |
+
{children}
|
161 |
+
</h3>
|
162 |
+
),
|
163 |
+
h4: ({ children }) => (
|
164 |
+
<h4 className="text-lg font-semibold text-gray-900 mt-4 mb-2">
|
165 |
+
{children}
|
166 |
+
</h4>
|
167 |
+
),
|
168 |
+
p: ({ children }) => (
|
169 |
+
<p className="text-gray-700 leading-relaxed mb-4">
|
170 |
+
{children}
|
171 |
+
</p>
|
172 |
+
),
|
173 |
+
ul: ({ children }) => (
|
174 |
+
<ul className="list-disc list-outside text-gray-700 mb-4 space-y-2 ml-6">
|
175 |
+
{children}
|
176 |
+
</ul>
|
177 |
+
),
|
178 |
+
ol: ({ children }) => (
|
179 |
+
<ol className="list-decimal list-outside text-gray-700 mb-4 space-y-2 ml-6">
|
180 |
+
{children}
|
181 |
+
</ol>
|
182 |
+
),
|
183 |
+
li: ({ children }) => (
|
184 |
+
<li className="pl-2">
|
185 |
+
{children}
|
186 |
+
</li>
|
187 |
+
),
|
188 |
+
code: ({ children, className }) => {
|
189 |
+
const isBlock = className?.includes('language-');
|
190 |
+
if (isBlock) {
|
191 |
+
return (
|
192 |
+
<pre className="bg-gray-50 border border-gray-200 rounded-lg p-4 mb-4 overflow-x-auto">
|
193 |
+
<code className="text-sm font-mono text-gray-800">
|
194 |
+
{children}
|
195 |
+
</code>
|
196 |
+
</pre>
|
197 |
+
);
|
198 |
+
}
|
199 |
+
return (
|
200 |
+
<code className="bg-gray-100 px-2 py-1 rounded text-sm font-mono text-gray-800">
|
201 |
+
{children}
|
202 |
+
</code>
|
203 |
+
);
|
204 |
+
},
|
205 |
+
blockquote: ({ children }) => (
|
206 |
+
<blockquote className="border-l-4 border-blue-500 pl-4 italic text-gray-600 mb-4">
|
207 |
+
{children}
|
208 |
+
</blockquote>
|
209 |
+
),
|
210 |
+
strong: ({ children }) => (
|
211 |
+
<strong className="font-semibold text-gray-900">
|
212 |
+
{children}
|
213 |
+
</strong>
|
214 |
+
),
|
215 |
+
em: ({ children }) => (
|
216 |
+
<em className="italic text-gray-700">
|
217 |
+
{children}
|
218 |
+
</em>
|
219 |
+
),
|
220 |
+
table: ({ children }) => (
|
221 |
+
<div className="overflow-x-auto mb-4">
|
222 |
+
<table className="min-w-full divide-y divide-gray-200">
|
223 |
+
{children}
|
224 |
+
</table>
|
225 |
+
</div>
|
226 |
+
),
|
227 |
+
thead: ({ children }) => (
|
228 |
+
<thead className="bg-gray-50">
|
229 |
+
{children}
|
230 |
+
</thead>
|
231 |
+
),
|
232 |
+
tbody: ({ children }) => (
|
233 |
+
<tbody className="bg-white divide-y divide-gray-200">
|
234 |
+
{children}
|
235 |
+
</tbody>
|
236 |
+
),
|
237 |
+
tr: ({ children }) => (
|
238 |
+
<tr>
|
239 |
+
{children}
|
240 |
+
</tr>
|
241 |
+
),
|
242 |
+
th: ({ children }) => (
|
243 |
+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
244 |
+
{children}
|
245 |
+
</th>
|
246 |
+
),
|
247 |
+
td: ({ children }) => (
|
248 |
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
249 |
+
{children}
|
250 |
+
</td>
|
251 |
+
)
|
252 |
+
}}
|
253 |
+
>
|
254 |
+
{markdownContent}
|
255 |
+
</ReactMarkdown>
|
256 |
+
</div>
|
257 |
+
</div>
|
258 |
+
</div>
|
259 |
+
);
|
260 |
+
};
|
261 |
+
|
262 |
+
export default AlgorithmDetail;
|
frontend/src/pages/Dashboard.tsx
ADDED
@@ -0,0 +1,255 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState, useEffect } from 'react';
|
2 |
+
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, PieChart, Pie, Cell } from 'recharts';
|
3 |
+
import { Loader2, TrendingUp, Brain, Cpu, MessageSquare } from 'lucide-react';
|
4 |
+
import { Link } from 'react-router-dom';
|
5 |
+
import api from '../services/api';
|
6 |
+
|
7 |
+
interface AlgorithmStat {
|
8 |
+
algorithm: string;
|
9 |
+
name: string;
|
10 |
+
count: number;
|
11 |
+
}
|
12 |
+
|
13 |
+
interface DashboardStats {
|
14 |
+
classical_ml: AlgorithmStat[];
|
15 |
+
deep_learning: AlgorithmStat[];
|
16 |
+
llms: AlgorithmStat[];
|
17 |
+
}
|
18 |
+
|
19 |
+
const Dashboard: React.FC = () => {
|
20 |
+
const [stats, setStats] = useState<DashboardStats | null>(null);
|
21 |
+
const [loading, setLoading] = useState(true);
|
22 |
+
const [error, setError] = useState<string | null>(null);
|
23 |
+
|
24 |
+
useEffect(() => {
|
25 |
+
fetchDashboardStats();
|
26 |
+
}, []);
|
27 |
+
|
28 |
+
const fetchDashboardStats = async () => {
|
29 |
+
try {
|
30 |
+
setLoading(true);
|
31 |
+
const response = await api.get('/search/dashboard-stats');
|
32 |
+
setStats(response.data);
|
33 |
+
} catch (err) {
|
34 |
+
setError('Failed to load dashboard statistics');
|
35 |
+
} finally {
|
36 |
+
setLoading(false);
|
37 |
+
}
|
38 |
+
};
|
39 |
+
|
40 |
+
if (loading) {
|
41 |
+
return (
|
42 |
+
<div className="flex justify-center items-center h-64">
|
43 |
+
<Loader2 className="h-8 w-8 animate-spin text-blue-600" />
|
44 |
+
<span className="ml-2 text-gray-600">Loading dashboard...</span>
|
45 |
+
</div>
|
46 |
+
);
|
47 |
+
}
|
48 |
+
|
49 |
+
if (error) {
|
50 |
+
return (
|
51 |
+
<div className="text-center text-red-600 p-8">
|
52 |
+
<p>{error}</p>
|
53 |
+
<button
|
54 |
+
onClick={fetchDashboardStats}
|
55 |
+
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
|
56 |
+
>
|
57 |
+
Retry
|
58 |
+
</button>
|
59 |
+
</div>
|
60 |
+
);
|
61 |
+
}
|
62 |
+
|
63 |
+
if (!stats) return null;
|
64 |
+
|
65 |
+
const combinedData = [
|
66 |
+
...stats.classical_ml.map(item => ({ ...item, category: 'Classical ML' })),
|
67 |
+
...stats.deep_learning.map(item => ({ ...item, category: 'Deep Learning' })),
|
68 |
+
...stats.llms.map(item => ({ ...item, category: 'LLMs' }))
|
69 |
+
].sort((a, b) => b.count - a.count).slice(0, 12);
|
70 |
+
|
71 |
+
const categoryTotals = [
|
72 |
+
{
|
73 |
+
name: 'Classical ML',
|
74 |
+
value: stats.classical_ml.reduce((sum, item) => sum + item.count, 0),
|
75 |
+
color: '#3B82F6'
|
76 |
+
},
|
77 |
+
{
|
78 |
+
name: 'Deep Learning',
|
79 |
+
value: stats.deep_learning.reduce((sum, item) => sum + item.count, 0),
|
80 |
+
color: '#8B5CF6'
|
81 |
+
},
|
82 |
+
{
|
83 |
+
name: 'LLMs',
|
84 |
+
value: stats.llms.reduce((sum, item) => sum + item.count, 0),
|
85 |
+
color: '#10B981'
|
86 |
+
}
|
87 |
+
];
|
88 |
+
|
89 |
+
const totalPapers = categoryTotals.reduce((sum, cat) => sum + cat.value, 0);
|
90 |
+
|
91 |
+
return (
|
92 |
+
<div className="space-y-8">
|
93 |
+
<div className="text-center">
|
94 |
+
<h1 className="text-4xl font-bold text-gray-900 mb-2">
|
95 |
+
AI Algorithms in Medical Research
|
96 |
+
</h1>
|
97 |
+
<p className="text-xl text-gray-600">
|
98 |
+
Explore the landscape of AI algorithms used in PubMed medical papers
|
99 |
+
</p>
|
100 |
+
</div>
|
101 |
+
|
102 |
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
103 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
104 |
+
<div className="flex items-center">
|
105 |
+
<TrendingUp className="h-8 w-8 text-blue-600" />
|
106 |
+
<div className="ml-4">
|
107 |
+
<p className="text-sm font-medium text-gray-600">Total Papers</p>
|
108 |
+
<p className="text-2xl font-bold text-gray-900">{totalPapers.toLocaleString()}</p>
|
109 |
+
</div>
|
110 |
+
</div>
|
111 |
+
</div>
|
112 |
+
|
113 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
114 |
+
<div className="flex items-center">
|
115 |
+
<Brain className="h-8 w-8 text-blue-600" />
|
116 |
+
<div className="ml-4">
|
117 |
+
<p className="text-sm font-medium text-gray-600">Classical ML Papers</p>
|
118 |
+
<p className="text-2xl font-bold text-gray-900">{categoryTotals[0].value.toLocaleString()}</p>
|
119 |
+
</div>
|
120 |
+
</div>
|
121 |
+
</div>
|
122 |
+
|
123 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
124 |
+
<div className="flex items-center">
|
125 |
+
<Cpu className="h-8 w-8 text-purple-600" />
|
126 |
+
<div className="ml-4">
|
127 |
+
<p className="text-sm font-medium text-gray-600">Deep Learning Papers</p>
|
128 |
+
<p className="text-2xl font-bold text-gray-900">{categoryTotals[1].value.toLocaleString()}</p>
|
129 |
+
</div>
|
130 |
+
</div>
|
131 |
+
</div>
|
132 |
+
|
133 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
134 |
+
<div className="flex items-center">
|
135 |
+
<MessageSquare className="h-8 w-8 text-green-600" />
|
136 |
+
<div className="ml-4">
|
137 |
+
<p className="text-sm font-medium text-gray-600">LLMs Papers</p>
|
138 |
+
<p className="text-2xl font-bold text-gray-900">{categoryTotals[2].value.toLocaleString()}</p>
|
139 |
+
</div>
|
140 |
+
</div>
|
141 |
+
</div>
|
142 |
+
</div>
|
143 |
+
|
144 |
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
145 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
146 |
+
<h2 className="text-xl font-bold text-gray-900 mb-4">Algorithm Distribution</h2>
|
147 |
+
<ResponsiveContainer width="100%" height={300}>
|
148 |
+
<PieChart>
|
149 |
+
<Pie
|
150 |
+
data={categoryTotals}
|
151 |
+
cx="50%"
|
152 |
+
cy="50%"
|
153 |
+
labelLine={false}
|
154 |
+
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(1)}%`}
|
155 |
+
outerRadius={80}
|
156 |
+
fill="#8884d8"
|
157 |
+
dataKey="value"
|
158 |
+
>
|
159 |
+
{categoryTotals.map((entry, index) => (
|
160 |
+
<Cell key={`cell-${index}`} fill={entry.color} />
|
161 |
+
))}
|
162 |
+
</Pie>
|
163 |
+
<Tooltip />
|
164 |
+
</PieChart>
|
165 |
+
</ResponsiveContainer>
|
166 |
+
</div>
|
167 |
+
|
168 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
169 |
+
<h2 className="text-xl font-bold text-gray-900 mb-4">Top Algorithms by Usage</h2>
|
170 |
+
<ResponsiveContainer width="100%" height={300}>
|
171 |
+
<BarChart data={combinedData}>
|
172 |
+
<CartesianGrid strokeDasharray="3 3" />
|
173 |
+
<XAxis
|
174 |
+
dataKey="name"
|
175 |
+
angle={-45}
|
176 |
+
textAnchor="end"
|
177 |
+
height={80}
|
178 |
+
fontSize={12}
|
179 |
+
/>
|
180 |
+
<YAxis />
|
181 |
+
<Tooltip />
|
182 |
+
<Legend />
|
183 |
+
<Bar
|
184 |
+
dataKey="count"
|
185 |
+
fill="#3B82F6"
|
186 |
+
name="Paper Count"
|
187 |
+
/>
|
188 |
+
</BarChart>
|
189 |
+
</ResponsiveContainer>
|
190 |
+
</div>
|
191 |
+
</div>
|
192 |
+
|
193 |
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
194 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
195 |
+
<h2 className="text-xl font-bold text-gray-900 mb-4">Classical ML Algorithms</h2>
|
196 |
+
<div className="space-y-3">
|
197 |
+
{stats.classical_ml.slice(0, 8).map((algo, index) => (
|
198 |
+
<div key={algo.algorithm} className="flex justify-between items-center p-3 bg-gray-50 rounded">
|
199 |
+
<div>
|
200 |
+
<Link
|
201 |
+
to={`/algorithm/${algo.algorithm}`}
|
202 |
+
className="font-medium text-gray-900 hover:text-blue-600 transition-colors"
|
203 |
+
>
|
204 |
+
{algo.name}
|
205 |
+
</Link>
|
206 |
+
</div>
|
207 |
+
<span className="text-blue-600 font-bold">{algo.count.toLocaleString()}</span>
|
208 |
+
</div>
|
209 |
+
))}
|
210 |
+
</div>
|
211 |
+
</div>
|
212 |
+
|
213 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
214 |
+
<h2 className="text-xl font-bold text-gray-900 mb-4">Deep Learning Algorithms</h2>
|
215 |
+
<div className="space-y-3">
|
216 |
+
{stats.deep_learning.slice(0, 8).map((algo, index) => (
|
217 |
+
<div key={algo.algorithm} className="flex justify-between items-center p-3 bg-gray-50 rounded">
|
218 |
+
<div>
|
219 |
+
<Link
|
220 |
+
to={`/algorithm/${algo.algorithm}`}
|
221 |
+
className="font-medium text-gray-900 hover:text-purple-600 transition-colors"
|
222 |
+
>
|
223 |
+
{algo.name}
|
224 |
+
</Link>
|
225 |
+
</div>
|
226 |
+
<span className="text-purple-600 font-bold">{algo.count.toLocaleString()}</span>
|
227 |
+
</div>
|
228 |
+
))}
|
229 |
+
</div>
|
230 |
+
</div>
|
231 |
+
|
232 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
233 |
+
<h2 className="text-xl font-bold text-gray-900 mb-4">Large Language Models</h2>
|
234 |
+
<div className="space-y-3">
|
235 |
+
{stats.llms.slice(0, 8).map((algo, index) => (
|
236 |
+
<div key={algo.algorithm} className="flex justify-between items-center p-3 bg-gray-50 rounded">
|
237 |
+
<div>
|
238 |
+
<Link
|
239 |
+
to={`/algorithm/${algo.algorithm}`}
|
240 |
+
className="font-medium text-gray-900 hover:text-green-600 transition-colors"
|
241 |
+
>
|
242 |
+
{algo.name}
|
243 |
+
</Link>
|
244 |
+
</div>
|
245 |
+
<span className="text-green-600 font-bold">{algo.count.toLocaleString()}</span>
|
246 |
+
</div>
|
247 |
+
))}
|
248 |
+
</div>
|
249 |
+
</div>
|
250 |
+
</div>
|
251 |
+
</div>
|
252 |
+
);
|
253 |
+
};
|
254 |
+
|
255 |
+
export default Dashboard;
|
frontend/src/pages/Roadmap.tsx
ADDED
@@ -0,0 +1,865 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react';
|
2 |
+
import { BookOpen, Code, Database, Brain, Stethoscope, Shield, Wrench, Users, TrendingUp, Award, Target, Crown, ExternalLink, Clock, Star, Users2, Globe, Video, FileText, Laptop } from 'lucide-react';
|
3 |
+
|
4 |
+
interface Course {
|
5 |
+
title: string;
|
6 |
+
platform: string;
|
7 |
+
url: string;
|
8 |
+
duration: string;
|
9 |
+
level: 'Beginner' | 'Intermediate' | 'Advanced';
|
10 |
+
rating?: number;
|
11 |
+
}
|
12 |
+
|
13 |
+
interface Book {
|
14 |
+
title: string;
|
15 |
+
author: string;
|
16 |
+
url: string;
|
17 |
+
description: string;
|
18 |
+
}
|
19 |
+
|
20 |
+
interface PhaseProps {
|
21 |
+
phaseNumber: number;
|
22 |
+
title: string;
|
23 |
+
description: string;
|
24 |
+
items: {
|
25 |
+
title: string;
|
26 |
+
objective: string;
|
27 |
+
icon: React.ReactNode;
|
28 |
+
courses: Course[];
|
29 |
+
books?: Book[];
|
30 |
+
topics: string[];
|
31 |
+
practicalProjects?: string[];
|
32 |
+
estimatedTime: string;
|
33 |
+
}[];
|
34 |
+
icon: React.ReactNode;
|
35 |
+
color: string;
|
36 |
+
isLast?: boolean;
|
37 |
+
}
|
38 |
+
|
39 |
+
const Phase: React.FC<PhaseProps> = ({ phaseNumber, title, description, items, icon, color, isLast }) => {
|
40 |
+
return (
|
41 |
+
<div className="relative">
|
42 |
+
{/* Timeline line */}
|
43 |
+
{!isLast && (
|
44 |
+
<div className="absolute left-8 top-20 w-0.5 h-full bg-gray-300 z-0"></div>
|
45 |
+
)}
|
46 |
+
|
47 |
+
<div className="relative z-10 flex items-start mb-16">
|
48 |
+
{/* Phase circle indicator */}
|
49 |
+
<div className={`flex-shrink-0 w-16 h-16 rounded-full ${color} flex items-center justify-center mr-8 shadow-lg`}>
|
50 |
+
<div className="text-white font-bold text-lg">{phaseNumber}</div>
|
51 |
+
</div>
|
52 |
+
|
53 |
+
{/* Phase content */}
|
54 |
+
<div className="flex-grow">
|
55 |
+
<div className="mb-6">
|
56 |
+
<h2 className="text-2xl font-bold text-gray-900 mb-2">{title}</h2>
|
57 |
+
<p className="text-gray-600 text-lg">{description}</p>
|
58 |
+
</div>
|
59 |
+
|
60 |
+
<div className="space-y-8">
|
61 |
+
{items.map((item, index) => (
|
62 |
+
<div key={index} className="bg-gradient-to-br from-white to-gray-50 rounded-xl shadow-lg p-8 border border-gray-200 hover:shadow-2xl hover:scale-[1.02] transition-all duration-300 ml-8 group">
|
63 |
+
{/* Header with icon and title */}
|
64 |
+
<div className="flex items-center mb-6">
|
65 |
+
<div className="bg-gradient-to-br from-blue-500 to-purple-600 p-3 rounded-lg mr-4 group-hover:scale-110 transition-transform duration-300">
|
66 |
+
{item.icon}
|
67 |
+
</div>
|
68 |
+
<div className="flex-grow">
|
69 |
+
<h3 className="text-xl font-bold text-gray-900 mb-1">{item.title}</h3>
|
70 |
+
<div className="flex items-center text-sm text-gray-500">
|
71 |
+
<Clock className="h-4 w-4 mr-1" />
|
72 |
+
<span>Estimated time: {item.estimatedTime}</span>
|
73 |
+
</div>
|
74 |
+
</div>
|
75 |
+
</div>
|
76 |
+
|
77 |
+
{/* Objective */}
|
78 |
+
<div className="mb-6 p-4 bg-blue-50 rounded-lg border-l-4 border-blue-500">
|
79 |
+
<h4 className="text-sm font-semibold text-blue-900 mb-2 flex items-center">
|
80 |
+
<Target className="h-4 w-4 mr-2" />
|
81 |
+
Objective
|
82 |
+
</h4>
|
83 |
+
<p className="text-sm text-blue-800">{item.objective}</p>
|
84 |
+
</div>
|
85 |
+
|
86 |
+
{/* Courses */}
|
87 |
+
<div className="mb-6">
|
88 |
+
<h4 className="text-sm font-semibold text-gray-700 mb-3 flex items-center">
|
89 |
+
<Video className="h-4 w-4 mr-2" />
|
90 |
+
Recommended Courses
|
91 |
+
</h4>
|
92 |
+
<div className="grid gap-3 sm:grid-cols-2">
|
93 |
+
{item.courses.map((course, courseIndex) => (
|
94 |
+
<a
|
95 |
+
key={courseIndex}
|
96 |
+
href={course.url}
|
97 |
+
target="_blank"
|
98 |
+
rel="noopener noreferrer"
|
99 |
+
className="block p-4 bg-white rounded-lg border border-gray-200 hover:border-blue-300 hover:shadow-md transition-all duration-200 group/course"
|
100 |
+
>
|
101 |
+
<div className="flex items-start justify-between mb-2">
|
102 |
+
<h5 className="font-medium text-gray-900 text-sm group-hover/course:text-blue-600 transition-colors">{course.title}</h5>
|
103 |
+
<ExternalLink className="h-3 w-3 text-gray-400 group-hover/course:text-blue-500 flex-shrink-0 ml-2" />
|
104 |
+
</div>
|
105 |
+
<div className="flex items-center justify-between text-xs text-gray-500">
|
106 |
+
<span className="bg-gray-100 px-2 py-1 rounded">{course.platform}</span>
|
107 |
+
<div className="flex items-center space-x-2">
|
108 |
+
<span className={`px-2 py-1 rounded text-xs font-medium ${
|
109 |
+
course.level === 'Beginner' ? 'bg-green-100 text-green-700' :
|
110 |
+
course.level === 'Intermediate' ? 'bg-yellow-100 text-yellow-700' :
|
111 |
+
'bg-red-100 text-red-700'
|
112 |
+
}`}>
|
113 |
+
{course.level}
|
114 |
+
</span>
|
115 |
+
<span>{course.duration}</span>
|
116 |
+
{course.rating && (
|
117 |
+
<div className="flex items-center">
|
118 |
+
<Star className="h-3 w-3 text-yellow-400 fill-current" />
|
119 |
+
<span className="ml-1">{course.rating}</span>
|
120 |
+
</div>
|
121 |
+
)}
|
122 |
+
</div>
|
123 |
+
</div>
|
124 |
+
</a>
|
125 |
+
))}
|
126 |
+
</div>
|
127 |
+
</div>
|
128 |
+
|
129 |
+
{/* Books */}
|
130 |
+
{item.books && item.books.length > 0 && (
|
131 |
+
<div className="mb-6">
|
132 |
+
<h4 className="text-sm font-semibold text-gray-700 mb-3 flex items-center">
|
133 |
+
<BookOpen className="h-4 w-4 mr-2" />
|
134 |
+
Essential Reading
|
135 |
+
</h4>
|
136 |
+
<div className="space-y-3">
|
137 |
+
{item.books.map((book, bookIndex) => (
|
138 |
+
<a
|
139 |
+
key={bookIndex}
|
140 |
+
href={book.url}
|
141 |
+
target="_blank"
|
142 |
+
rel="noopener noreferrer"
|
143 |
+
className="block p-4 bg-orange-50 rounded-lg border border-orange-200 hover:border-orange-300 hover:shadow-md transition-all duration-200 group/book"
|
144 |
+
>
|
145 |
+
<div className="flex items-start justify-between mb-2">
|
146 |
+
<div>
|
147 |
+
<h5 className="font-medium text-gray-900 text-sm group-hover/book:text-orange-600 transition-colors">{book.title}</h5>
|
148 |
+
<p className="text-xs text-gray-600">by {book.author}</p>
|
149 |
+
</div>
|
150 |
+
<ExternalLink className="h-3 w-3 text-gray-400 group-hover/book:text-orange-500 flex-shrink-0 ml-2" />
|
151 |
+
</div>
|
152 |
+
<p className="text-xs text-gray-600">{book.description}</p>
|
153 |
+
</a>
|
154 |
+
))}
|
155 |
+
</div>
|
156 |
+
</div>
|
157 |
+
)}
|
158 |
+
|
159 |
+
{/* Practical Projects */}
|
160 |
+
{item.practicalProjects && item.practicalProjects.length > 0 && (
|
161 |
+
<div className="mb-6">
|
162 |
+
<h4 className="text-sm font-semibold text-gray-700 mb-3 flex items-center">
|
163 |
+
<Laptop className="h-4 w-4 mr-2" />
|
164 |
+
Hands-on Projects
|
165 |
+
</h4>
|
166 |
+
<div className="bg-green-50 rounded-lg p-4">
|
167 |
+
<ul className="space-y-2">
|
168 |
+
{item.practicalProjects.map((project, projectIndex) => (
|
169 |
+
<li key={projectIndex} className="flex items-start text-sm text-green-800">
|
170 |
+
<div className="w-2 h-2 bg-green-500 rounded-full mt-2 mr-3 flex-shrink-0"></div>
|
171 |
+
{project}
|
172 |
+
</li>
|
173 |
+
))}
|
174 |
+
</ul>
|
175 |
+
</div>
|
176 |
+
</div>
|
177 |
+
)}
|
178 |
+
|
179 |
+
{/* Topics */}
|
180 |
+
<div>
|
181 |
+
<h4 className="text-sm font-semibold text-gray-700 mb-3 flex items-center">
|
182 |
+
<FileText className="h-4 w-4 mr-2" />
|
183 |
+
Key Topics to Master
|
184 |
+
</h4>
|
185 |
+
<div className="flex flex-wrap gap-2">
|
186 |
+
{item.topics.map((topic, topicIndex) => (
|
187 |
+
<span
|
188 |
+
key={topicIndex}
|
189 |
+
className="px-3 py-1 bg-purple-100 text-purple-700 rounded-full text-xs font-medium hover:bg-purple-200 transition-colors"
|
190 |
+
>
|
191 |
+
{topic}
|
192 |
+
</span>
|
193 |
+
))}
|
194 |
+
</div>
|
195 |
+
</div>
|
196 |
+
</div>
|
197 |
+
))}
|
198 |
+
</div>
|
199 |
+
</div>
|
200 |
+
</div>
|
201 |
+
</div>
|
202 |
+
);
|
203 |
+
};
|
204 |
+
|
205 |
+
const Roadmap: React.FC = () => {
|
206 |
+
const phases = [
|
207 |
+
{
|
208 |
+
phaseNumber: 1,
|
209 |
+
title: "Foundational Knowledge",
|
210 |
+
description: "Build essential understanding of AI concepts and programming skills",
|
211 |
+
icon: <BookOpen className="h-8 w-8 text-white" />,
|
212 |
+
color: "bg-blue-500",
|
213 |
+
items: [
|
214 |
+
{
|
215 |
+
title: "Introduction to AI",
|
216 |
+
objective: "Understand the basics of AI, its history, and key concepts.",
|
217 |
+
icon: <Brain className="h-6 w-6 text-white" />,
|
218 |
+
estimatedTime: "4-6 weeks",
|
219 |
+
courses: [
|
220 |
+
{
|
221 |
+
title: "AI For Everyone",
|
222 |
+
platform: "Coursera",
|
223 |
+
url: "https://www.coursera.org/learn/ai-for-everyone",
|
224 |
+
duration: "4 weeks",
|
225 |
+
level: "Beginner",
|
226 |
+
rating: 4.8
|
227 |
+
},
|
228 |
+
{
|
229 |
+
title: "Introduction to Artificial Intelligence",
|
230 |
+
platform: "edX MIT",
|
231 |
+
url: "https://www.edx.org/course/introduction-to-artificial-intelligence-ai",
|
232 |
+
duration: "5 weeks",
|
233 |
+
level: "Beginner",
|
234 |
+
rating: 4.6
|
235 |
+
},
|
236 |
+
{
|
237 |
+
title: "AI Fundamentals",
|
238 |
+
platform: "IBM Cognitive Class",
|
239 |
+
url: "https://cognitiveclass.ai/courses/artificial-intelligence-fundamentals",
|
240 |
+
duration: "3 weeks",
|
241 |
+
level: "Beginner"
|
242 |
+
}
|
243 |
+
],
|
244 |
+
books: [
|
245 |
+
{
|
246 |
+
title: "Artificial Intelligence: A Guide for Thinking Humans",
|
247 |
+
author: "Melanie Mitchell",
|
248 |
+
url: "https://www.amazon.com/Artificial-Intelligence-Guide-Thinking-Humans/dp/0374257833",
|
249 |
+
description: "An accessible introduction to AI concepts without technical jargon"
|
250 |
+
},
|
251 |
+
{
|
252 |
+
title: "Human Compatible: Artificial Intelligence and the Problem of Control",
|
253 |
+
author: "Stuart Russell",
|
254 |
+
url: "https://www.amazon.com/Human-Compatible-Artificial-Intelligence-Problem/dp/0525558616",
|
255 |
+
description: "Explores the future of AI and its implications for humanity"
|
256 |
+
}
|
257 |
+
],
|
258 |
+
topics: ["AI vs. ML vs. Deep Learning", "History of AI", "Types of AI", "AI Ethics", "Current Applications", "Future Trends"]
|
259 |
+
},
|
260 |
+
{
|
261 |
+
title: "Basic Programming Skills",
|
262 |
+
objective: "Gain proficiency in Python, the most commonly used programming language in AI.",
|
263 |
+
icon: <Code className="h-6 w-6 text-white" />,
|
264 |
+
estimatedTime: "8-12 weeks",
|
265 |
+
courses: [
|
266 |
+
{
|
267 |
+
title: "Python for Everybody Specialization",
|
268 |
+
platform: "Coursera",
|
269 |
+
url: "https://www.coursera.org/specializations/python",
|
270 |
+
duration: "8 months",
|
271 |
+
level: "Beginner",
|
272 |
+
rating: 4.8
|
273 |
+
},
|
274 |
+
{
|
275 |
+
title: "Learn Python 3",
|
276 |
+
platform: "Codecademy",
|
277 |
+
url: "https://www.codecademy.com/learn/learn-python-3",
|
278 |
+
duration: "25 hours",
|
279 |
+
level: "Beginner",
|
280 |
+
rating: 4.7
|
281 |
+
},
|
282 |
+
{
|
283 |
+
title: "Python Programming MOOC",
|
284 |
+
platform: "University of Helsinki",
|
285 |
+
url: "https://programming-23.mooc.fi/",
|
286 |
+
duration: "14 weeks",
|
287 |
+
level: "Beginner"
|
288 |
+
},
|
289 |
+
{
|
290 |
+
title: "CS50's Introduction to Programming with Python",
|
291 |
+
platform: "Harvard edX",
|
292 |
+
url: "https://www.edx.org/course/cs50s-introduction-to-programming-with-python",
|
293 |
+
duration: "10 weeks",
|
294 |
+
level: "Beginner",
|
295 |
+
rating: 4.9
|
296 |
+
}
|
297 |
+
],
|
298 |
+
books: [
|
299 |
+
{
|
300 |
+
title: "Python Crash Course",
|
301 |
+
author: "Eric Matthes",
|
302 |
+
url: "https://www.amazon.com/Python-Crash-Course-Hands-Project-Based/dp/1593279280",
|
303 |
+
description: "A hands-on, project-based introduction to programming"
|
304 |
+
}
|
305 |
+
],
|
306 |
+
practicalProjects: [
|
307 |
+
"Build a simple calculator application",
|
308 |
+
"Create a weather data scraper using APIs",
|
309 |
+
"Develop a basic web scraper with BeautifulSoup",
|
310 |
+
"Make a simple data visualization with matplotlib"
|
311 |
+
],
|
312 |
+
topics: ["Python Syntax", "Data Structures", "Functions & Classes", "NumPy", "Pandas", "File Handling", "Error Handling", "Libraries & Modules"]
|
313 |
+
},
|
314 |
+
{
|
315 |
+
title: "Data Literacy",
|
316 |
+
objective: "Learn about data types, collection, preprocessing, and analysis.",
|
317 |
+
icon: <Database className="h-6 w-6 text-white" />,
|
318 |
+
estimatedTime: "6-8 weeks",
|
319 |
+
courses: [
|
320 |
+
{
|
321 |
+
title: "Data Science Fundamentals",
|
322 |
+
platform: "DataCamp",
|
323 |
+
url: "https://www.datacamp.com/tracks/data-scientist-with-python",
|
324 |
+
duration: "87 hours",
|
325 |
+
level: "Beginner",
|
326 |
+
rating: 4.6
|
327 |
+
},
|
328 |
+
{
|
329 |
+
title: "Introduction to Data Science in Python",
|
330 |
+
platform: "Coursera (University of Michigan)",
|
331 |
+
url: "https://www.coursera.org/learn/python-data-analysis",
|
332 |
+
duration: "4 weeks",
|
333 |
+
level: "Intermediate",
|
334 |
+
rating: 4.5
|
335 |
+
},
|
336 |
+
{
|
337 |
+
title: "Data Analysis with Python",
|
338 |
+
platform: "freeCodeCamp",
|
339 |
+
url: "https://www.freecodecamp.org/learn/data-analysis-with-python/",
|
340 |
+
duration: "300 hours",
|
341 |
+
level: "Intermediate"
|
342 |
+
}
|
343 |
+
],
|
344 |
+
books: [
|
345 |
+
{
|
346 |
+
title: "Python for Data Analysis",
|
347 |
+
author: "Wes McKinney",
|
348 |
+
url: "https://www.amazon.com/Python-Data-Analysis-Wrangling-IPython/dp/1491957662",
|
349 |
+
description: "Essential guide to data manipulation and analysis with pandas"
|
350 |
+
}
|
351 |
+
],
|
352 |
+
practicalProjects: [
|
353 |
+
"Analyze a real dataset from Kaggle",
|
354 |
+
"Create comprehensive data visualizations",
|
355 |
+
"Build an interactive dashboard with Streamlit",
|
356 |
+
"Perform exploratory data analysis on healthcare data"
|
357 |
+
],
|
358 |
+
topics: ["Data Types", "Data Cleaning", "Exploratory Data Analysis", "Statistical Analysis", "Matplotlib", "Seaborn", "Plotly", "Data Ethics"]
|
359 |
+
}
|
360 |
+
]
|
361 |
+
},
|
362 |
+
{
|
363 |
+
phaseNumber: 2,
|
364 |
+
title: "Core AI Concepts",
|
365 |
+
description: "Master fundamental machine learning and deep learning techniques",
|
366 |
+
icon: <Brain className="h-8 w-8 text-white" />,
|
367 |
+
color: "bg-purple-500",
|
368 |
+
items: [
|
369 |
+
{
|
370 |
+
title: "Machine Learning Basics",
|
371 |
+
objective: "Study the fundamentals of machine learning algorithms and techniques.",
|
372 |
+
icon: <TrendingUp className="h-6 w-6 text-white" />,
|
373 |
+
estimatedTime: "10-12 weeks",
|
374 |
+
courses: [
|
375 |
+
{
|
376 |
+
title: "Machine Learning Course",
|
377 |
+
platform: "Coursera (Stanford)",
|
378 |
+
url: "https://www.coursera.org/learn/machine-learning",
|
379 |
+
duration: "11 weeks",
|
380 |
+
level: "Intermediate",
|
381 |
+
rating: 4.9
|
382 |
+
},
|
383 |
+
{
|
384 |
+
title: "Scikit-Learn Course",
|
385 |
+
platform: "DataCamp",
|
386 |
+
url: "https://www.datacamp.com/courses/supervised-learning-with-scikit-learn",
|
387 |
+
duration: "4 hours",
|
388 |
+
level: "Intermediate",
|
389 |
+
rating: 4.7
|
390 |
+
},
|
391 |
+
{
|
392 |
+
title: "Machine Learning A-Z",
|
393 |
+
platform: "Udemy",
|
394 |
+
url: "https://www.udemy.com/course/machinelearning/",
|
395 |
+
duration: "44 hours",
|
396 |
+
level: "Beginner",
|
397 |
+
rating: 4.5
|
398 |
+
},
|
399 |
+
{
|
400 |
+
title: "Introduction to Machine Learning",
|
401 |
+
platform: "MIT OpenCourseWare",
|
402 |
+
url: "https://ocw.mit.edu/courses/6-0002-introduction-to-computational-thinking-and-data-science-fall-2016/",
|
403 |
+
duration: "12 weeks",
|
404 |
+
level: "Intermediate"
|
405 |
+
}
|
406 |
+
],
|
407 |
+
books: [
|
408 |
+
{
|
409 |
+
title: "Hands-On Machine Learning",
|
410 |
+
author: "Aurélien Géron",
|
411 |
+
url: "https://www.amazon.com/Hands-Machine-Learning-Scikit-Learn-TensorFlow/dp/1492032646",
|
412 |
+
description: "Practical approach to ML with Python, scikit-learn, and TensorFlow"
|
413 |
+
},
|
414 |
+
{
|
415 |
+
title: "Pattern Recognition and Machine Learning",
|
416 |
+
author: "Christopher Bishop",
|
417 |
+
url: "https://www.amazon.com/Pattern-Recognition-Learning-Information-Statistics/dp/0387310738",
|
418 |
+
description: "Comprehensive theoretical foundation of machine learning"
|
419 |
+
}
|
420 |
+
],
|
421 |
+
practicalProjects: [
|
422 |
+
"Build a house price prediction model",
|
423 |
+
"Create a customer segmentation analysis",
|
424 |
+
"Develop a recommendation system",
|
425 |
+
"Implement classification for medical diagnosis"
|
426 |
+
],
|
427 |
+
topics: ["Supervised Learning", "Unsupervised Learning", "Regression", "Classification", "Clustering", "Model Evaluation", "Cross-Validation", "Feature Engineering"]
|
428 |
+
},
|
429 |
+
{
|
430 |
+
title: "Deep Learning",
|
431 |
+
objective: "Master neural networks and their applications in various domains.",
|
432 |
+
icon: <Brain className="h-6 w-6 text-white" />,
|
433 |
+
estimatedTime: "12-16 weeks",
|
434 |
+
courses: [
|
435 |
+
{
|
436 |
+
title: "Deep Learning Specialization",
|
437 |
+
platform: "Coursera (deeplearning.ai)",
|
438 |
+
url: "https://www.coursera.org/specializations/deep-learning",
|
439 |
+
duration: "4 months",
|
440 |
+
level: "Intermediate",
|
441 |
+
rating: 4.8
|
442 |
+
},
|
443 |
+
{
|
444 |
+
title: "CS231n: Convolutional Neural Networks",
|
445 |
+
platform: "Stanford Online",
|
446 |
+
url: "http://cs231n.stanford.edu/",
|
447 |
+
duration: "16 weeks",
|
448 |
+
level: "Advanced"
|
449 |
+
},
|
450 |
+
{
|
451 |
+
title: "Fast.ai Practical Deep Learning",
|
452 |
+
platform: "fast.ai",
|
453 |
+
url: "https://course.fast.ai/",
|
454 |
+
duration: "7 weeks",
|
455 |
+
level: "Intermediate",
|
456 |
+
rating: 4.9
|
457 |
+
},
|
458 |
+
{
|
459 |
+
title: "PyTorch for Deep Learning",
|
460 |
+
platform: "Udacity",
|
461 |
+
url: "https://www.udacity.com/course/deep-learning-pytorch--ud188",
|
462 |
+
duration: "2 months",
|
463 |
+
level: "Intermediate"
|
464 |
+
}
|
465 |
+
],
|
466 |
+
books: [
|
467 |
+
{
|
468 |
+
title: "Deep Learning",
|
469 |
+
author: "Ian Goodfellow, Yoshua Bengio, Aaron Courville",
|
470 |
+
url: "https://www.amazon.com/Deep-Learning-Ian-Goodfellow/dp/0262035618",
|
471 |
+
description: "The definitive textbook on deep learning theory and practice"
|
472 |
+
}
|
473 |
+
],
|
474 |
+
practicalProjects: [
|
475 |
+
"Build an image classifier for medical images",
|
476 |
+
"Create a neural network for time series forecasting",
|
477 |
+
"Develop a generative model for synthetic data",
|
478 |
+
"Implement transfer learning for medical imaging"
|
479 |
+
],
|
480 |
+
topics: ["Neural Networks", "CNNs", "RNNs", "LSTMs", "GANs", "Transfer Learning", "Optimization", "Regularization", "TensorFlow", "PyTorch"]
|
481 |
+
},
|
482 |
+
{
|
483 |
+
title: "Natural Language Processing",
|
484 |
+
objective: "Learn to process and analyze textual data, especially medical literature.",
|
485 |
+
icon: <FileText className="h-6 w-6 text-white" />,
|
486 |
+
estimatedTime: "8-10 weeks",
|
487 |
+
courses: [
|
488 |
+
{
|
489 |
+
title: "Natural Language Processing Specialization",
|
490 |
+
platform: "Coursera (deeplearning.ai)",
|
491 |
+
url: "https://www.coursera.org/specializations/natural-language-processing",
|
492 |
+
duration: "4 months",
|
493 |
+
level: "Intermediate",
|
494 |
+
rating: 4.6
|
495 |
+
},
|
496 |
+
{
|
497 |
+
title: "CS224n: Natural Language Processing with Deep Learning",
|
498 |
+
platform: "Stanford Online",
|
499 |
+
url: "http://web.stanford.edu/class/cs224n/",
|
500 |
+
duration: "10 weeks",
|
501 |
+
level: "Advanced"
|
502 |
+
},
|
503 |
+
{
|
504 |
+
title: "NLP with Python",
|
505 |
+
platform: "DataCamp",
|
506 |
+
url: "https://www.datacamp.com/tracks/natural-language-processing-in-python",
|
507 |
+
duration: "17 hours",
|
508 |
+
level: "Intermediate",
|
509 |
+
rating: 4.5
|
510 |
+
}
|
511 |
+
],
|
512 |
+
books: [
|
513 |
+
{
|
514 |
+
title: "Natural Language Processing with Python",
|
515 |
+
author: "Steven Bird, Ewan Klein, Edward Loper",
|
516 |
+
url: "https://www.amazon.com/Natural-Language-Processing-Python-Analyzing/dp/0596516495",
|
517 |
+
description: "Practical guide to NLP using NLTK and Python"
|
518 |
+
}
|
519 |
+
],
|
520 |
+
practicalProjects: [
|
521 |
+
"Build a medical text classifier",
|
522 |
+
"Create a clinical notes summarizer",
|
523 |
+
"Develop sentiment analysis for patient feedback",
|
524 |
+
"Implement named entity recognition for medical terms"
|
525 |
+
],
|
526 |
+
topics: ["Text Preprocessing", "Tokenization", "Word Embeddings", "Transformers", "BERT", "Sentiment Analysis", "Named Entity Recognition", "Language Models"]
|
527 |
+
}
|
528 |
+
]
|
529 |
+
},
|
530 |
+
{
|
531 |
+
phaseNumber: 3,
|
532 |
+
title: "AI in Healthcare",
|
533 |
+
description: "Apply AI knowledge specifically to healthcare and medical applications",
|
534 |
+
icon: <Stethoscope className="h-8 w-8 text-white" />,
|
535 |
+
color: "bg-green-500",
|
536 |
+
items: [
|
537 |
+
{
|
538 |
+
title: "Healthcare Data Standards",
|
539 |
+
objective: "Master healthcare data formats and interoperability standards.",
|
540 |
+
icon: <Database className="h-6 w-6 text-white" />,
|
541 |
+
estimatedTime: "6-8 weeks",
|
542 |
+
courses: [
|
543 |
+
{
|
544 |
+
title: "Health Informatics on FHIR",
|
545 |
+
platform: "Coursera (UC Davis)",
|
546 |
+
url: "https://www.coursera.org/learn/fhir",
|
547 |
+
duration: "4 weeks",
|
548 |
+
level: "Intermediate",
|
549 |
+
rating: 4.5
|
550 |
+
},
|
551 |
+
{
|
552 |
+
title: "Healthcare Data Models and APIs",
|
553 |
+
platform: "edX",
|
554 |
+
url: "https://www.edx.org/course/healthcare-data-models-and-apis",
|
555 |
+
duration: "6 weeks",
|
556 |
+
level: "Intermediate"
|
557 |
+
},
|
558 |
+
{
|
559 |
+
title: "DICOM and Medical Imaging",
|
560 |
+
platform: "RSNA",
|
561 |
+
url: "https://www.rsna.org/education",
|
562 |
+
duration: "Self-paced",
|
563 |
+
level: "Intermediate"
|
564 |
+
}
|
565 |
+
],
|
566 |
+
books: [
|
567 |
+
{
|
568 |
+
title: "Healthcare Information Systems",
|
569 |
+
author: "Marion J. Ball",
|
570 |
+
url: "https://www.amazon.com/Healthcare-Information-Systems-Marion-Ball/dp/0387403299",
|
571 |
+
description: "Comprehensive guide to healthcare IT systems and standards"
|
572 |
+
}
|
573 |
+
],
|
574 |
+
practicalProjects: [
|
575 |
+
"Parse and analyze FHIR resources",
|
576 |
+
"Build a DICOM image viewer",
|
577 |
+
"Create an HL7 message processor",
|
578 |
+
"Develop healthcare data pipeline"
|
579 |
+
],
|
580 |
+
topics: ["HL7", "FHIR", "DICOM", "EHR Systems", "Healthcare APIs", "Data Interoperability", "Medical Coding", "Healthcare Databases"]
|
581 |
+
},
|
582 |
+
{
|
583 |
+
title: "AI Applications in Medicine",
|
584 |
+
objective: "Study and implement AI solutions for specific medical domains.",
|
585 |
+
icon: <Stethoscope className="h-6 w-6 text-white" />,
|
586 |
+
estimatedTime: "10-12 weeks",
|
587 |
+
courses: [
|
588 |
+
{
|
589 |
+
title: "AI for Medical Diagnosis",
|
590 |
+
platform: "Coursera (deeplearning.ai)",
|
591 |
+
url: "https://www.coursera.org/learn/ai-for-medical-diagnosis",
|
592 |
+
duration: "3 weeks",
|
593 |
+
level: "Intermediate",
|
594 |
+
rating: 4.7
|
595 |
+
},
|
596 |
+
{
|
597 |
+
title: "Medical Image Analysis",
|
598 |
+
platform: "MIT OpenCourseWare",
|
599 |
+
url: "https://ocw.mit.edu/courses/health-sciences-and-technology/",
|
600 |
+
duration: "12 weeks",
|
601 |
+
level: "Advanced"
|
602 |
+
},
|
603 |
+
{
|
604 |
+
title: "Clinical Data Science",
|
605 |
+
platform: "Harvard T.H. Chan School",
|
606 |
+
url: "https://www.hsph.harvard.edu/biostatistics/",
|
607 |
+
duration: "8 weeks",
|
608 |
+
level: "Advanced"
|
609 |
+
}
|
610 |
+
],
|
611 |
+
books: [
|
612 |
+
{
|
613 |
+
title: "Artificial Intelligence in Medicine",
|
614 |
+
author: "Peter Lucas, Arie Hasman",
|
615 |
+
url: "https://www.amazon.com/Artificial-Intelligence-Medicine-Peter-Lucas/dp/0444502753",
|
616 |
+
description: "Comprehensive overview of AI applications in healthcare"
|
617 |
+
}
|
618 |
+
],
|
619 |
+
practicalProjects: [
|
620 |
+
"Build a medical image classification system",
|
621 |
+
"Create a clinical decision support tool",
|
622 |
+
"Develop a drug discovery pipeline",
|
623 |
+
"Implement predictive analytics for patient outcomes"
|
624 |
+
],
|
625 |
+
topics: ["Medical Imaging AI", "Clinical Decision Support", "Genomics", "Drug Discovery", "Predictive Analytics", "Personalized Medicine", "Telemedicine", "Robotic Surgery"]
|
626 |
+
},
|
627 |
+
{
|
628 |
+
title: "Healthcare AI Ethics & Regulation",
|
629 |
+
objective: "Navigate ethical and regulatory challenges in healthcare AI.",
|
630 |
+
icon: <Shield className="h-6 w-6 text-white" />,
|
631 |
+
estimatedTime: "4-6 weeks",
|
632 |
+
courses: [
|
633 |
+
{
|
634 |
+
title: "AI in Healthcare Ethics",
|
635 |
+
platform: "Stanford Medicine",
|
636 |
+
url: "https://med.stanford.edu/aiethics.html",
|
637 |
+
duration: "4 weeks",
|
638 |
+
level: "Intermediate"
|
639 |
+
},
|
640 |
+
{
|
641 |
+
title: "FDA Regulation of AI/ML",
|
642 |
+
platform: "FDA",
|
643 |
+
url: "https://www.fda.gov/medical-devices/software-medical-device-samd/artificial-intelligence-and-machine-learning-software-medical-device",
|
644 |
+
duration: "Self-paced",
|
645 |
+
level: "Intermediate"
|
646 |
+
}
|
647 |
+
],
|
648 |
+
books: [
|
649 |
+
{
|
650 |
+
title: "The Ethical Algorithm",
|
651 |
+
author: "Michael Kearns, Aaron Roth",
|
652 |
+
url: "https://www.amazon.com/Ethical-Algorithm-Science-Socially-Design/dp/0190948205",
|
653 |
+
description: "Framework for designing ethical AI systems"
|
654 |
+
}
|
655 |
+
],
|
656 |
+
practicalProjects: [
|
657 |
+
"Conduct bias analysis in medical AI models",
|
658 |
+
"Design privacy-preserving healthcare AI",
|
659 |
+
"Create AI governance framework",
|
660 |
+
"Develop explainable AI for medical decisions"
|
661 |
+
],
|
662 |
+
topics: ["AI Ethics", "HIPAA Compliance", "FDA Regulations", "Bias Detection", "Explainable AI", "Privacy Protection", "Algorithmic Fairness", "Regulatory Compliance"]
|
663 |
+
}
|
664 |
+
]
|
665 |
+
},
|
666 |
+
{
|
667 |
+
phaseNumber: 4,
|
668 |
+
title: "Practical Experience",
|
669 |
+
description: "Gain hands-on experience with real-world AI projects",
|
670 |
+
icon: <Wrench className="h-8 w-8 text-white" />,
|
671 |
+
color: "bg-orange-500",
|
672 |
+
items: [
|
673 |
+
{
|
674 |
+
title: "Healthcare AI Projects",
|
675 |
+
objective: "Build real-world AI solutions for healthcare challenges.",
|
676 |
+
icon: <Laptop className="h-6 w-6 text-white" />,
|
677 |
+
estimatedTime: "12-16 weeks",
|
678 |
+
courses: [
|
679 |
+
{
|
680 |
+
title: "Applied Data Science Capstone",
|
681 |
+
platform: "Coursera (IBM)",
|
682 |
+
url: "https://www.coursera.org/learn/applied-data-science-capstone",
|
683 |
+
duration: "6 weeks",
|
684 |
+
level: "Advanced",
|
685 |
+
rating: 4.4
|
686 |
+
},
|
687 |
+
{
|
688 |
+
title: "Kaggle Learn",
|
689 |
+
platform: "Kaggle",
|
690 |
+
url: "https://www.kaggle.com/learn",
|
691 |
+
duration: "Self-paced",
|
692 |
+
level: "Intermediate"
|
693 |
+
}
|
694 |
+
],
|
695 |
+
practicalProjects: [
|
696 |
+
"Medical image analysis with CNNs",
|
697 |
+
"Clinical trial outcome prediction",
|
698 |
+
"Drug-drug interaction detection",
|
699 |
+
"Electronic health record analysis",
|
700 |
+
"Medical chatbot development"
|
701 |
+
],
|
702 |
+
topics: ["Project Management", "Version Control", "Model Deployment", "Cloud Platforms", "API Development", "Database Management", "Testing", "Documentation"]
|
703 |
+
},
|
704 |
+
{
|
705 |
+
title: "Professional Development",
|
706 |
+
objective: "Build network and stay current with healthcare AI trends.",
|
707 |
+
icon: <Users2 className="h-6 w-6 text-white" />,
|
708 |
+
estimatedTime: "Ongoing",
|
709 |
+
courses: [
|
710 |
+
{
|
711 |
+
title: "Healthcare AI Leadership",
|
712 |
+
platform: "MIT xPRO",
|
713 |
+
url: "https://learn-xpro.mit.edu/",
|
714 |
+
duration: "8 weeks",
|
715 |
+
level: "Advanced"
|
716 |
+
}
|
717 |
+
],
|
718 |
+
practicalProjects: [
|
719 |
+
"Join AMIA and attend conferences",
|
720 |
+
"Contribute to open-source healthcare AI projects",
|
721 |
+
"Publish research papers",
|
722 |
+
"Present at healthcare AI meetups"
|
723 |
+
],
|
724 |
+
topics: ["Professional Networks", "Research Publications", "Conference Presentations", "Open Source Contribution", "Mentorship", "Industry Trends"]
|
725 |
+
}
|
726 |
+
]
|
727 |
+
},
|
728 |
+
{
|
729 |
+
phaseNumber: 5,
|
730 |
+
title: "Advanced Topics and Specialization",
|
731 |
+
description: "Explore cutting-edge research and develop specialized expertise",
|
732 |
+
icon: <TrendingUp className="h-8 w-8 text-white" />,
|
733 |
+
color: "bg-red-500",
|
734 |
+
items: [
|
735 |
+
{
|
736 |
+
title: "Advanced AI Research",
|
737 |
+
objective: "Master cutting-edge AI techniques and research methodologies.",
|
738 |
+
icon: <Award className="h-6 w-6 text-white" />,
|
739 |
+
estimatedTime: "16-20 weeks",
|
740 |
+
courses: [
|
741 |
+
{
|
742 |
+
title: "Reinforcement Learning Specialization",
|
743 |
+
platform: "Coursera (University of Alberta)",
|
744 |
+
url: "https://www.coursera.org/specializations/reinforcement-learning",
|
745 |
+
duration: "4 months",
|
746 |
+
level: "Advanced",
|
747 |
+
rating: 4.7
|
748 |
+
},
|
749 |
+
{
|
750 |
+
title: "Explainable AI",
|
751 |
+
platform: "MIT xPRO",
|
752 |
+
url: "https://learn-xpro.mit.edu/artificial-intelligence",
|
753 |
+
duration: "8 weeks",
|
754 |
+
level: "Advanced"
|
755 |
+
}
|
756 |
+
],
|
757 |
+
topics: ["Reinforcement Learning", "GANs", "Explainable AI", "AutoML", "Federated Learning", "Graph Neural Networks", "Meta-Learning", "Research Methods"]
|
758 |
+
},
|
759 |
+
{
|
760 |
+
title: "Healthcare Specialization",
|
761 |
+
objective: "Develop deep expertise in a specific healthcare AI domain.",
|
762 |
+
icon: <Target className="h-6 w-6 text-white" />,
|
763 |
+
estimatedTime: "6+ months",
|
764 |
+
courses: [
|
765 |
+
{
|
766 |
+
title: "Genomics Data Science",
|
767 |
+
platform: "Coursera (Johns Hopkins)",
|
768 |
+
url: "https://www.coursera.org/specializations/genomic-data-science",
|
769 |
+
duration: "6 months",
|
770 |
+
level: "Advanced",
|
771 |
+
rating: 4.5
|
772 |
+
}
|
773 |
+
],
|
774 |
+
topics: ["Medical Imaging", "Genomics", "Clinical Decision Support", "Drug Discovery", "Precision Medicine", "Digital Therapeutics", "Wearables", "Telemedicine"]
|
775 |
+
}
|
776 |
+
]
|
777 |
+
},
|
778 |
+
{
|
779 |
+
phaseNumber: 6,
|
780 |
+
title: "Implementation and Leadership",
|
781 |
+
description: "Lead AI initiatives and drive adoption in healthcare organizations",
|
782 |
+
icon: <Crown className="h-8 w-8 text-white" />,
|
783 |
+
color: "bg-indigo-500",
|
784 |
+
items: [
|
785 |
+
{
|
786 |
+
title: "Clinical Implementation",
|
787 |
+
objective: "Lead successful AI integration in healthcare organizations.",
|
788 |
+
icon: <Wrench className="h-6 w-6 text-white" />,
|
789 |
+
estimatedTime: "6+ months",
|
790 |
+
courses: [
|
791 |
+
{
|
792 |
+
title: "Healthcare Innovation and Entrepreneurship",
|
793 |
+
platform: "Harvard Business School Online",
|
794 |
+
url: "https://online.hbs.edu/courses/healthcare-innovation/",
|
795 |
+
duration: "8 weeks",
|
796 |
+
level: "Advanced"
|
797 |
+
}
|
798 |
+
],
|
799 |
+
topics: ["Change Management", "Workflow Integration", "ROI Analysis", "Quality Assurance", "Risk Management", "Stakeholder Engagement", "Pilot Studies", "Scale-up Strategies"]
|
800 |
+
},
|
801 |
+
{
|
802 |
+
title: "AI Leadership & Strategy",
|
803 |
+
objective: "Drive organizational AI strategy and policy development.",
|
804 |
+
icon: <Crown className="h-6 w-6 text-white" />,
|
805 |
+
estimatedTime: "Ongoing",
|
806 |
+
courses: [
|
807 |
+
{
|
808 |
+
title: "AI Strategy and Leadership",
|
809 |
+
platform: "MIT Sloan",
|
810 |
+
url: "https://executive.mit.edu/openenrollment/program/artificial_intelligence_strategy_and_leadership/",
|
811 |
+
duration: "3 days",
|
812 |
+
level: "Executive"
|
813 |
+
}
|
814 |
+
],
|
815 |
+
topics: ["Strategic Planning", "Policy Development", "Team Leadership", "Budget Management", "Regulatory Navigation", "Public Speaking", "Grant Writing", "Board Presentations"]
|
816 |
+
}
|
817 |
+
]
|
818 |
+
}
|
819 |
+
];
|
820 |
+
|
821 |
+
return (
|
822 |
+
<div className="max-w-7xl mx-auto">
|
823 |
+
<div className="text-center mb-12">
|
824 |
+
<h1 className="text-4xl font-bold text-gray-900 mb-4">AI Learning Roadmap</h1>
|
825 |
+
<p className="text-xl text-gray-600 max-w-3xl mx-auto">
|
826 |
+
A comprehensive guide to mastering artificial intelligence with a focus on healthcare applications.
|
827 |
+
Follow this structured path from foundational concepts to advanced implementation and leadership.
|
828 |
+
</p>
|
829 |
+
</div>
|
830 |
+
|
831 |
+
<div className="mb-8">
|
832 |
+
<div className="bg-blue-50 border border-blue-200 rounded-lg p-6">
|
833 |
+
<div className="flex items-center mb-3">
|
834 |
+
<Target className="h-6 w-6 text-blue-600 mr-2" />
|
835 |
+
<h3 className="text-lg font-semibold text-blue-900">Learning Journey Overview</h3>
|
836 |
+
</div>
|
837 |
+
<p className="text-blue-800">
|
838 |
+
This roadmap is designed as a progressive learning journey spanning 6 phases. Each phase builds upon the previous one,
|
839 |
+
taking you from AI fundamentals to becoming a leader in healthcare AI implementation. Expect to spend 6-12 months on each phase,
|
840 |
+
depending on your background and time commitment.
|
841 |
+
</p>
|
842 |
+
</div>
|
843 |
+
</div>
|
844 |
+
|
845 |
+
<div className="relative">
|
846 |
+
{phases.map((phase, index) => (
|
847 |
+
<Phase key={phase.phaseNumber} {...phase} isLast={index === phases.length - 1} />
|
848 |
+
))}
|
849 |
+
</div>
|
850 |
+
|
851 |
+
<div className="mt-12 text-center">
|
852 |
+
<div className="bg-gradient-to-r from-gray-50 to-gray-100 rounded-lg p-8">
|
853 |
+
<Award className="h-12 w-12 text-gray-600 mx-auto mb-4" />
|
854 |
+
<h3 className="text-2xl font-bold text-gray-900 mb-4">Ready to Begin Your Journey?</h3>
|
855 |
+
<p className="text-gray-700 max-w-2xl mx-auto">
|
856 |
+
Remember, this roadmap is a guide, not a rigid prescription. Adapt it to your specific interests,
|
857 |
+
background, and career goals. The key is consistent learning and practical application of knowledge.
|
858 |
+
</p>
|
859 |
+
</div>
|
860 |
+
</div>
|
861 |
+
</div>
|
862 |
+
);
|
863 |
+
};
|
864 |
+
|
865 |
+
export default Roadmap;
|
frontend/src/pages/Search.tsx
ADDED
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState } from 'react';
|
2 |
+
import { Search as SearchIcon, ExternalLink, FileText, Loader2 } from 'lucide-react';
|
3 |
+
import api from '../services/api';
|
4 |
+
import AlgorithmCard from '../components/AlgorithmCard';
|
5 |
+
|
6 |
+
interface SearchResult {
|
7 |
+
algorithm: string;
|
8 |
+
name: string;
|
9 |
+
category: string;
|
10 |
+
description: string;
|
11 |
+
count: number;
|
12 |
+
sampleIds: string[];
|
13 |
+
}
|
14 |
+
|
15 |
+
interface SearchResponse {
|
16 |
+
problem: string;
|
17 |
+
totalAlgorithms: number;
|
18 |
+
results: SearchResult[];
|
19 |
+
allResults: SearchResult[];
|
20 |
+
}
|
21 |
+
|
22 |
+
const Search: React.FC = () => {
|
23 |
+
const [problem, setProblem] = useState('');
|
24 |
+
const [results, setResults] = useState<SearchResponse | null>(null);
|
25 |
+
const [loading, setLoading] = useState(false);
|
26 |
+
const [error, setError] = useState<string | null>(null);
|
27 |
+
|
28 |
+
const handleSearch = async (e: React.FormEvent) => {
|
29 |
+
e.preventDefault();
|
30 |
+
if (!problem.trim()) return;
|
31 |
+
|
32 |
+
try {
|
33 |
+
setLoading(true);
|
34 |
+
setError(null);
|
35 |
+
const response = await api.post('/search/problem', { problem: problem.trim() });
|
36 |
+
setResults(response.data);
|
37 |
+
} catch (err) {
|
38 |
+
setError('Failed to search for algorithms. Please try again.');
|
39 |
+
} finally {
|
40 |
+
setLoading(false);
|
41 |
+
}
|
42 |
+
};
|
43 |
+
|
44 |
+
const handleSeeMorePapers = async (algorithm: string) => {
|
45 |
+
try {
|
46 |
+
const response = await api.get('/search/pubmed-link', {
|
47 |
+
params: { problem, algorithm }
|
48 |
+
});
|
49 |
+
window.open(response.data.url, '_blank');
|
50 |
+
} catch (err) {
|
51 |
+
console.error('Failed to generate PubMed link:', err);
|
52 |
+
}
|
53 |
+
};
|
54 |
+
|
55 |
+
return (
|
56 |
+
<div className="max-w-6xl mx-auto space-y-8">
|
57 |
+
<div className="text-center">
|
58 |
+
<h1 className="text-4xl font-bold text-gray-900 mb-2">
|
59 |
+
Algorithm Search
|
60 |
+
</h1>
|
61 |
+
<p className="text-xl text-gray-600">
|
62 |
+
Search for AI algorithms used in specific medical problems
|
63 |
+
</p>
|
64 |
+
</div>
|
65 |
+
|
66 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
67 |
+
<form onSubmit={handleSearch} className="space-y-4">
|
68 |
+
<div>
|
69 |
+
<label htmlFor="problem" className="block text-sm font-medium text-gray-700 mb-2">
|
70 |
+
Medical Problem or Condition
|
71 |
+
</label>
|
72 |
+
<div className="relative">
|
73 |
+
<input
|
74 |
+
type="text"
|
75 |
+
id="problem"
|
76 |
+
value={problem}
|
77 |
+
onChange={(e) => setProblem(e.target.value)}
|
78 |
+
placeholder="e.g., breast cancer detection, diabetes prediction, alzheimer's disease"
|
79 |
+
className="w-full px-4 py-3 pl-10 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500"
|
80 |
+
disabled={loading}
|
81 |
+
/>
|
82 |
+
<SearchIcon className="absolute left-3 top-3.5 h-5 w-5 text-gray-400" />
|
83 |
+
</div>
|
84 |
+
</div>
|
85 |
+
<button
|
86 |
+
type="submit"
|
87 |
+
disabled={loading || !problem.trim()}
|
88 |
+
className="w-full bg-blue-600 text-white py-3 px-4 rounded-md hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center"
|
89 |
+
>
|
90 |
+
{loading ? (
|
91 |
+
<>
|
92 |
+
<Loader2 className="animate-spin h-5 w-5 mr-2" />
|
93 |
+
Searching...
|
94 |
+
</>
|
95 |
+
) : (
|
96 |
+
<>
|
97 |
+
<SearchIcon className="h-5 w-5 mr-2" />
|
98 |
+
Search Algorithms
|
99 |
+
</>
|
100 |
+
)}
|
101 |
+
</button>
|
102 |
+
</form>
|
103 |
+
</div>
|
104 |
+
|
105 |
+
{error && (
|
106 |
+
<div className="bg-red-50 border border-red-200 rounded-md p-4">
|
107 |
+
<p className="text-red-800">{error}</p>
|
108 |
+
</div>
|
109 |
+
)}
|
110 |
+
|
111 |
+
{results && (
|
112 |
+
<div className="space-y-6">
|
113 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
114 |
+
<h2 className="text-2xl font-bold text-gray-900 mb-2">
|
115 |
+
Results for "{results.problem}"
|
116 |
+
</h2>
|
117 |
+
<p className="text-gray-600">
|
118 |
+
Found {results.results.length} algorithms with published research
|
119 |
+
</p>
|
120 |
+
</div>
|
121 |
+
|
122 |
+
{results.results.length > 0 ? (
|
123 |
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 auto-rows-fr">
|
124 |
+
{results.results.map((result) => (
|
125 |
+
<AlgorithmCard
|
126 |
+
key={result.algorithm}
|
127 |
+
algorithm={result}
|
128 |
+
problem={results.problem}
|
129 |
+
onSeeMorePapers={handleSeeMorePapers}
|
130 |
+
/>
|
131 |
+
))}
|
132 |
+
</div>
|
133 |
+
) : (
|
134 |
+
<div className="bg-gray-50 p-8 rounded-lg text-center">
|
135 |
+
<FileText className="h-12 w-12 text-gray-400 mx-auto mb-4" />
|
136 |
+
<h3 className="text-lg font-medium text-gray-900 mb-2">No Results Found</h3>
|
137 |
+
<p className="text-gray-600">
|
138 |
+
No algorithms found for this medical problem. Try searching with different terms.
|
139 |
+
</p>
|
140 |
+
</div>
|
141 |
+
)}
|
142 |
+
|
143 |
+
{results.allResults.some(r => r.count === 0) && (
|
144 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
145 |
+
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
146 |
+
Algorithms with No Results
|
147 |
+
</h3>
|
148 |
+
<div className="grid grid-cols-2 md:grid-cols-4 gap-2">
|
149 |
+
{results.allResults
|
150 |
+
.filter(r => r.count === 0)
|
151 |
+
.map(result => (
|
152 |
+
<span
|
153 |
+
key={result.algorithm}
|
154 |
+
className="px-3 py-1 bg-gray-100 text-gray-600 rounded-full text-sm"
|
155 |
+
>
|
156 |
+
{result.name}
|
157 |
+
</span>
|
158 |
+
))}
|
159 |
+
</div>
|
160 |
+
</div>
|
161 |
+
)}
|
162 |
+
</div>
|
163 |
+
)}
|
164 |
+
</div>
|
165 |
+
);
|
166 |
+
};
|
167 |
+
|
168 |
+
export default Search;
|
frontend/src/pages/Timeline.tsx
ADDED
@@ -0,0 +1,427 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState, useEffect } from 'react';
|
2 |
+
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
|
3 |
+
import { Loader2, Calendar, TrendingUp, Database, Clock } from 'lucide-react';
|
4 |
+
import api from '../services/api';
|
5 |
+
|
6 |
+
interface TimelineData {
|
7 |
+
year: number;
|
8 |
+
[key: string]: number;
|
9 |
+
}
|
10 |
+
|
11 |
+
interface AlgorithmTimeline {
|
12 |
+
algorithm: string;
|
13 |
+
name: string;
|
14 |
+
category: string;
|
15 |
+
data: { year: number; count: number }[];
|
16 |
+
}
|
17 |
+
|
18 |
+
interface CacheStats {
|
19 |
+
cached: number;
|
20 |
+
fetched: number;
|
21 |
+
}
|
22 |
+
|
23 |
+
interface ProgressState {
|
24 |
+
current: number;
|
25 |
+
total: number;
|
26 |
+
currentAlgorithm: string;
|
27 |
+
}
|
28 |
+
|
29 |
+
const Timeline: React.FC = () => {
|
30 |
+
const [timelineData, setTimelineData] = useState<TimelineData[]>([]);
|
31 |
+
const [algorithms, setAlgorithms] = useState<AlgorithmTimeline[]>([]);
|
32 |
+
const [loading, setLoading] = useState(true);
|
33 |
+
const [error, setError] = useState<string | null>(null);
|
34 |
+
const [selectedAlgorithms, setSelectedAlgorithms] = useState<string[]>([]);
|
35 |
+
const [yearRange, setYearRange] = useState({ start: 2015, end: 2024 });
|
36 |
+
const [cacheStats, setCacheStats] = useState<CacheStats | null>(null);
|
37 |
+
const [progress, setProgress] = useState<ProgressState | null>(null);
|
38 |
+
|
39 |
+
useEffect(() => {
|
40 |
+
fetchTimelineData();
|
41 |
+
}, [yearRange]);
|
42 |
+
|
43 |
+
const fetchTimelineData = async () => {
|
44 |
+
try {
|
45 |
+
setLoading(true);
|
46 |
+
setError(null);
|
47 |
+
setProgress(null);
|
48 |
+
setCacheStats(null);
|
49 |
+
|
50 |
+
// Use Server-Sent Events for real-time progress
|
51 |
+
const eventSource = new EventSource(`/api/search/timeline-stream?startYear=${yearRange.start}&endYear=${yearRange.end}`);
|
52 |
+
|
53 |
+
eventSource.onmessage = (event) => {
|
54 |
+
const data = JSON.parse(event.data);
|
55 |
+
|
56 |
+
switch (data.type) {
|
57 |
+
case 'init':
|
58 |
+
setProgress({
|
59 |
+
current: 0,
|
60 |
+
total: data.totalOperations,
|
61 |
+
currentAlgorithm: data.message
|
62 |
+
});
|
63 |
+
setCacheStats({
|
64 |
+
cached: data.cachedResults,
|
65 |
+
fetched: data.fetchedResults
|
66 |
+
});
|
67 |
+
break;
|
68 |
+
|
69 |
+
case 'algorithm_start':
|
70 |
+
setProgress({
|
71 |
+
current: data.completed,
|
72 |
+
total: data.total,
|
73 |
+
currentAlgorithm: `Processing ${data.algorithm}...`
|
74 |
+
});
|
75 |
+
break;
|
76 |
+
|
77 |
+
case 'year_complete':
|
78 |
+
setProgress({
|
79 |
+
current: data.completed,
|
80 |
+
total: data.total,
|
81 |
+
currentAlgorithm: `${data.algorithm} (${data.year}): ${data.count} papers`
|
82 |
+
});
|
83 |
+
break;
|
84 |
+
|
85 |
+
case 'algorithm_complete':
|
86 |
+
setProgress({
|
87 |
+
current: data.completed,
|
88 |
+
total: data.total,
|
89 |
+
currentAlgorithm: `Completed ${data.algorithm}`
|
90 |
+
});
|
91 |
+
break;
|
92 |
+
|
93 |
+
case 'cache_saved':
|
94 |
+
setProgress(prev => prev ? {
|
95 |
+
...prev,
|
96 |
+
currentAlgorithm: data.message
|
97 |
+
} : null);
|
98 |
+
break;
|
99 |
+
|
100 |
+
case 'complete':
|
101 |
+
setTimelineData(data.timelineData);
|
102 |
+
setAlgorithms(data.algorithms);
|
103 |
+
setCacheStats(data.cacheStats);
|
104 |
+
|
105 |
+
// Auto-select top 5 algorithms if none selected
|
106 |
+
if (selectedAlgorithms.length === 0) {
|
107 |
+
const topAlgorithms = data.algorithms
|
108 |
+
.sort((a: AlgorithmTimeline, b: AlgorithmTimeline) => {
|
109 |
+
const aTotal = a.data.reduce((sum, item) => sum + item.count, 0);
|
110 |
+
const bTotal = b.data.reduce((sum, item) => sum + item.count, 0);
|
111 |
+
return bTotal - aTotal;
|
112 |
+
})
|
113 |
+
.slice(0, 5)
|
114 |
+
.map((algo: AlgorithmTimeline) => algo.algorithm);
|
115 |
+
setSelectedAlgorithms(topAlgorithms);
|
116 |
+
}
|
117 |
+
|
118 |
+
setLoading(false);
|
119 |
+
setProgress(null);
|
120 |
+
eventSource.close();
|
121 |
+
break;
|
122 |
+
|
123 |
+
case 'error':
|
124 |
+
setError(`Failed to load timeline data: ${data.error}`);
|
125 |
+
setLoading(false);
|
126 |
+
setProgress(null);
|
127 |
+
eventSource.close();
|
128 |
+
break;
|
129 |
+
}
|
130 |
+
};
|
131 |
+
|
132 |
+
eventSource.onerror = () => {
|
133 |
+
setError('Connection lost while loading timeline data');
|
134 |
+
setLoading(false);
|
135 |
+
setProgress(null);
|
136 |
+
eventSource.close();
|
137 |
+
};
|
138 |
+
|
139 |
+
} catch (err) {
|
140 |
+
setError('Failed to start timeline data loading');
|
141 |
+
setLoading(false);
|
142 |
+
setProgress(null);
|
143 |
+
}
|
144 |
+
};
|
145 |
+
|
146 |
+
const handleAlgorithmToggle = (algorithmKey: string) => {
|
147 |
+
setSelectedAlgorithms(prev =>
|
148 |
+
prev.includes(algorithmKey)
|
149 |
+
? prev.filter(key => key !== algorithmKey)
|
150 |
+
: [...prev, algorithmKey]
|
151 |
+
);
|
152 |
+
};
|
153 |
+
|
154 |
+
const getAlgorithmColor = (algorithmKey: string, index: number): string => {
|
155 |
+
// Define a palette of distinct colors for better visualization
|
156 |
+
const colors = [
|
157 |
+
'#3B82F6', // Blue
|
158 |
+
'#EF4444', // Red
|
159 |
+
'#10B981', // Green
|
160 |
+
'#F59E0B', // Yellow
|
161 |
+
'#8B5CF6', // Purple
|
162 |
+
'#EC4899', // Pink
|
163 |
+
'#06B6D4', // Cyan
|
164 |
+
'#84CC16', // Lime
|
165 |
+
'#F97316', // Orange
|
166 |
+
'#6366F1', // Indigo
|
167 |
+
'#14B8A6', // Teal
|
168 |
+
'#F43F5E', // Rose
|
169 |
+
'#8B5A3C', // Brown
|
170 |
+
'#6B7280', // Gray
|
171 |
+
'#DC2626', // Dark Red
|
172 |
+
'#059669', // Dark Green
|
173 |
+
'#7C3AED', // Dark Purple
|
174 |
+
'#DB2777', // Dark Pink
|
175 |
+
'#0891B2', // Dark Cyan
|
176 |
+
'#65A30D' // Dark Lime
|
177 |
+
];
|
178 |
+
|
179 |
+
// Use algorithm key to get consistent color assignment
|
180 |
+
const colorIndex = selectedAlgorithms.indexOf(algorithmKey) % colors.length;
|
181 |
+
return colors[colorIndex];
|
182 |
+
};
|
183 |
+
|
184 |
+
if (loading) {
|
185 |
+
return (
|
186 |
+
<div className="space-y-8">
|
187 |
+
<div className="text-center">
|
188 |
+
<h1 className="text-4xl font-bold text-gray-900 mb-2">
|
189 |
+
Algorithm Usage Timeline
|
190 |
+
</h1>
|
191 |
+
<p className="text-xl text-gray-600">
|
192 |
+
Track how the usage of AI algorithms in medical research has evolved over time
|
193 |
+
</p>
|
194 |
+
</div>
|
195 |
+
|
196 |
+
<div className="bg-white p-8 rounded-lg shadow-lg max-w-2xl mx-auto">
|
197 |
+
<div className="flex items-center justify-center mb-6">
|
198 |
+
<Loader2 className="h-8 w-8 animate-spin text-blue-600" />
|
199 |
+
<span className="ml-3 text-lg text-gray-700">Loading timeline data...</span>
|
200 |
+
</div>
|
201 |
+
|
202 |
+
{progress && (
|
203 |
+
<div className="space-y-4">
|
204 |
+
<div className="flex justify-between items-center">
|
205 |
+
<span className="text-sm text-gray-600">
|
206 |
+
{progress.currentAlgorithm}
|
207 |
+
</span>
|
208 |
+
<span className="text-sm font-medium text-gray-900">
|
209 |
+
{Math.round((progress.current / progress.total) * 100)}%
|
210 |
+
</span>
|
211 |
+
</div>
|
212 |
+
|
213 |
+
<div className="w-full bg-gray-200 rounded-full h-2">
|
214 |
+
<div
|
215 |
+
className="bg-blue-600 h-2 rounded-full transition-all duration-300 ease-out"
|
216 |
+
style={{ width: `${Math.min((progress.current / progress.total) * 100, 100)}%` }}
|
217 |
+
></div>
|
218 |
+
</div>
|
219 |
+
|
220 |
+
<div className="flex justify-between text-xs text-gray-500">
|
221 |
+
<span>{progress.current} / {progress.total} requests</span>
|
222 |
+
<span>
|
223 |
+
{progress.total > 0 ? Math.round((progress.current / progress.total) * 100) : 0}% complete
|
224 |
+
</span>
|
225 |
+
</div>
|
226 |
+
</div>
|
227 |
+
)}
|
228 |
+
|
229 |
+
<div className="mt-6 p-4 bg-blue-50 rounded-lg">
|
230 |
+
<div className="flex items-center mb-2">
|
231 |
+
<Database className="h-4 w-4 text-blue-600 mr-2" />
|
232 |
+
<span className="text-sm font-medium text-blue-900">Cache Information</span>
|
233 |
+
</div>
|
234 |
+
<p className="text-xs text-blue-700">
|
235 |
+
Historical data is cached on disk to speed up future requests.
|
236 |
+
Only the current year data needs to be fetched from PubMed each time.
|
237 |
+
</p>
|
238 |
+
</div>
|
239 |
+
</div>
|
240 |
+
</div>
|
241 |
+
);
|
242 |
+
}
|
243 |
+
|
244 |
+
if (error) {
|
245 |
+
return (
|
246 |
+
<div className="text-center text-red-600 p-8">
|
247 |
+
<p>{error}</p>
|
248 |
+
<button
|
249 |
+
onClick={fetchTimelineData}
|
250 |
+
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
|
251 |
+
>
|
252 |
+
Retry
|
253 |
+
</button>
|
254 |
+
</div>
|
255 |
+
);
|
256 |
+
}
|
257 |
+
|
258 |
+
const filteredTimelineData = timelineData.map(yearData => {
|
259 |
+
const filtered: TimelineData = { year: yearData.year };
|
260 |
+
selectedAlgorithms.forEach(algoKey => {
|
261 |
+
if (yearData[algoKey] !== undefined) {
|
262 |
+
filtered[algoKey] = yearData[algoKey];
|
263 |
+
}
|
264 |
+
});
|
265 |
+
return filtered;
|
266 |
+
});
|
267 |
+
|
268 |
+
return (
|
269 |
+
<div className="space-y-8">
|
270 |
+
<div className="text-center">
|
271 |
+
<h1 className="text-4xl font-bold text-gray-900 mb-2">
|
272 |
+
Algorithm Usage Timeline
|
273 |
+
</h1>
|
274 |
+
<p className="text-xl text-gray-600">
|
275 |
+
Track how the usage of AI algorithms in medical research has evolved over time
|
276 |
+
</p>
|
277 |
+
</div>
|
278 |
+
|
279 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
280 |
+
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between mb-6">
|
281 |
+
<div className="flex items-center mb-4 lg:mb-0">
|
282 |
+
<Calendar className="h-5 w-5 text-gray-500 mr-2" />
|
283 |
+
<span className="text-sm font-medium text-gray-700">Year Range:</span>
|
284 |
+
<input
|
285 |
+
type="number"
|
286 |
+
min="2010"
|
287 |
+
max="2024"
|
288 |
+
value={yearRange.start}
|
289 |
+
onChange={(e) => setYearRange(prev => ({ ...prev, start: parseInt(e.target.value) }))}
|
290 |
+
className="ml-2 px-2 py-1 border border-gray-300 rounded text-sm w-20"
|
291 |
+
/>
|
292 |
+
<span className="mx-2 text-gray-500">to</span>
|
293 |
+
<input
|
294 |
+
type="number"
|
295 |
+
min="2010"
|
296 |
+
max="2024"
|
297 |
+
value={yearRange.end}
|
298 |
+
onChange={(e) => setYearRange(prev => ({ ...prev, end: parseInt(e.target.value) }))}
|
299 |
+
className="px-2 py-1 border border-gray-300 rounded text-sm w-20"
|
300 |
+
/>
|
301 |
+
</div>
|
302 |
+
|
303 |
+
<div className="flex items-center space-x-6">
|
304 |
+
<div className="flex items-center">
|
305 |
+
<TrendingUp className="h-5 w-5 text-green-500 mr-2" />
|
306 |
+
<span className="text-sm text-gray-600">
|
307 |
+
{selectedAlgorithms.length} algorithm{selectedAlgorithms.length !== 1 ? 's' : ''} selected
|
308 |
+
</span>
|
309 |
+
</div>
|
310 |
+
|
311 |
+
{cacheStats && (
|
312 |
+
<div className="flex items-center space-x-4">
|
313 |
+
<div className="flex items-center">
|
314 |
+
<Database className="h-4 w-4 text-blue-500 mr-1" />
|
315 |
+
<span className="text-xs text-gray-600">
|
316 |
+
{cacheStats.cached} cached
|
317 |
+
</span>
|
318 |
+
</div>
|
319 |
+
<div className="flex items-center">
|
320 |
+
<Clock className="h-4 w-4 text-orange-500 mr-1" />
|
321 |
+
<span className="text-xs text-gray-600">
|
322 |
+
{cacheStats.fetched} fetched
|
323 |
+
</span>
|
324 |
+
</div>
|
325 |
+
</div>
|
326 |
+
)}
|
327 |
+
</div>
|
328 |
+
</div>
|
329 |
+
|
330 |
+
<ResponsiveContainer width="100%" height={500}>
|
331 |
+
<LineChart data={filteredTimelineData}>
|
332 |
+
<CartesianGrid strokeDasharray="3 3" />
|
333 |
+
<XAxis
|
334 |
+
dataKey="year"
|
335 |
+
type="number"
|
336 |
+
scale="linear"
|
337 |
+
domain={[yearRange.start, yearRange.end]}
|
338 |
+
/>
|
339 |
+
<YAxis />
|
340 |
+
<Tooltip />
|
341 |
+
<Legend />
|
342 |
+
{selectedAlgorithms.map((algoKey, index) => {
|
343 |
+
const algo = algorithms.find(a => a.algorithm === algoKey);
|
344 |
+
if (!algo) return null;
|
345 |
+
|
346 |
+
return (
|
347 |
+
<Line
|
348 |
+
key={algoKey}
|
349 |
+
type="monotone"
|
350 |
+
dataKey={algoKey}
|
351 |
+
stroke={getAlgorithmColor(algoKey, index)}
|
352 |
+
strokeWidth={2}
|
353 |
+
name={algo.name}
|
354 |
+
connectNulls={false}
|
355 |
+
/>
|
356 |
+
);
|
357 |
+
})}
|
358 |
+
</LineChart>
|
359 |
+
</ResponsiveContainer>
|
360 |
+
</div>
|
361 |
+
|
362 |
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
363 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
364 |
+
<h3 className="text-lg font-bold text-gray-900 mb-4">Classical ML</h3>
|
365 |
+
<div className="space-y-2">
|
366 |
+
{algorithms
|
367 |
+
.filter(algo => algo.category === 'classical_ml')
|
368 |
+
.slice(0, 8)
|
369 |
+
.map(algo => (
|
370 |
+
<label key={algo.algorithm} className="flex items-center cursor-pointer">
|
371 |
+
<input
|
372 |
+
type="checkbox"
|
373 |
+
checked={selectedAlgorithms.includes(algo.algorithm)}
|
374 |
+
onChange={() => handleAlgorithmToggle(algo.algorithm)}
|
375 |
+
className="mr-2"
|
376 |
+
/>
|
377 |
+
<span className="text-sm text-gray-700">{algo.name}</span>
|
378 |
+
</label>
|
379 |
+
))}
|
380 |
+
</div>
|
381 |
+
</div>
|
382 |
+
|
383 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
384 |
+
<h3 className="text-lg font-bold text-gray-900 mb-4">Deep Learning</h3>
|
385 |
+
<div className="space-y-2">
|
386 |
+
{algorithms
|
387 |
+
.filter(algo => algo.category === 'deep_learning')
|
388 |
+
.slice(0, 8)
|
389 |
+
.map(algo => (
|
390 |
+
<label key={algo.algorithm} className="flex items-center cursor-pointer">
|
391 |
+
<input
|
392 |
+
type="checkbox"
|
393 |
+
checked={selectedAlgorithms.includes(algo.algorithm)}
|
394 |
+
onChange={() => handleAlgorithmToggle(algo.algorithm)}
|
395 |
+
className="mr-2"
|
396 |
+
/>
|
397 |
+
<span className="text-sm text-gray-700">{algo.name}</span>
|
398 |
+
</label>
|
399 |
+
))}
|
400 |
+
</div>
|
401 |
+
</div>
|
402 |
+
|
403 |
+
<div className="bg-white p-6 rounded-lg shadow-lg">
|
404 |
+
<h3 className="text-lg font-bold text-gray-900 mb-4">Large Language Models</h3>
|
405 |
+
<div className="space-y-2">
|
406 |
+
{algorithms
|
407 |
+
.filter(algo => algo.category === 'llms')
|
408 |
+
.slice(0, 8)
|
409 |
+
.map(algo => (
|
410 |
+
<label key={algo.algorithm} className="flex items-center cursor-pointer">
|
411 |
+
<input
|
412 |
+
type="checkbox"
|
413 |
+
checked={selectedAlgorithms.includes(algo.algorithm)}
|
414 |
+
onChange={() => handleAlgorithmToggle(algo.algorithm)}
|
415 |
+
className="mr-2"
|
416 |
+
/>
|
417 |
+
<span className="text-sm text-gray-700">{algo.name}</span>
|
418 |
+
</label>
|
419 |
+
))}
|
420 |
+
</div>
|
421 |
+
</div>
|
422 |
+
</div>
|
423 |
+
</div>
|
424 |
+
);
|
425 |
+
};
|
426 |
+
|
427 |
+
export default Timeline;
|
frontend/src/services/api.ts
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import axios from 'axios';
|
2 |
+
|
3 |
+
const api = axios.create({
|
4 |
+
baseURL: '/api',
|
5 |
+
timeout: 300000, // 5 minutes for timeline requests
|
6 |
+
});
|
7 |
+
|
8 |
+
api.interceptors.response.use(
|
9 |
+
(response) => response,
|
10 |
+
(error) => {
|
11 |
+
console.error('API Error:', error.response?.data || error.message);
|
12 |
+
return Promise.reject(error);
|
13 |
+
}
|
14 |
+
);
|
15 |
+
|
16 |
+
export default api;
|
frontend/tailwind.config.js
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/** @type {import('tailwindcss').Config} */
|
2 |
+
export default {
|
3 |
+
content: [
|
4 |
+
"./index.html",
|
5 |
+
"./src/**/*.{js,ts,jsx,tsx}",
|
6 |
+
],
|
7 |
+
theme: {
|
8 |
+
extend: {},
|
9 |
+
},
|
10 |
+
plugins: [],
|
11 |
+
}
|
frontend/vite.config.ts
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { defineConfig } from 'vite'
|
2 |
+
import react from '@vitejs/plugin-react'
|
3 |
+
|
4 |
+
export default defineConfig({
|
5 |
+
plugins: [react()],
|
6 |
+
server: {
|
7 |
+
port: 3000,
|
8 |
+
proxy: {
|
9 |
+
'/api': {
|
10 |
+
target: 'http://localhost:3001',
|
11 |
+
changeOrigin: true
|
12 |
+
}
|
13 |
+
}
|
14 |
+
}
|
15 |
+
})
|
package.json
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "pubmed-ai-explorer",
|
3 |
+
"version": "1.0.0",
|
4 |
+
"description": "Web app to explore AI algorithms used in medical papers on PubMed",
|
5 |
+
"scripts": {
|
6 |
+
"dev": "concurrently \"npm run dev:backend\" \"npm run dev:frontend\"",
|
7 |
+
"dev:backend": "cd backend && npm run dev",
|
8 |
+
"dev:frontend": "cd frontend && npm run dev",
|
9 |
+
"build": "cd frontend && npm run build",
|
10 |
+
"start": "cd backend && npm start",
|
11 |
+
"install:all": "npm install && cd backend && npm install && cd ../frontend && npm install",
|
12 |
+
"docker:build": "docker build -t pubmed-ai-explorer .",
|
13 |
+
"docker:run": "docker run -p 3001:3001 pubmed-ai-explorer",
|
14 |
+
"docker:up": "docker-compose up",
|
15 |
+
"docker:down": "docker-compose down",
|
16 |
+
"docker:dev": "docker-compose --profile dev up"
|
17 |
+
},
|
18 |
+
"devDependencies": {
|
19 |
+
"concurrently": "^8.2.2"
|
20 |
+
}
|
21 |
+
}
|