-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
35 changed files
with
5,603 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# Dependencies | ||
node_modules | ||
.pnp | ||
.pnp.js | ||
|
||
# Testing | ||
coverage | ||
|
||
# Production | ||
dist | ||
dist-ssr | ||
*.local | ||
|
||
# Editor directories and files | ||
.vscode/* | ||
!.vscode/extensions.json | ||
.idea | ||
.DS_Store | ||
*.suo | ||
*.ntvs* | ||
*.njsproj | ||
*.sln | ||
*.sw? | ||
|
||
# Environment files | ||
.env | ||
.env.local | ||
.env.development.local | ||
.env.test.local | ||
.env.production.local | ||
|
||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
pnpm-debug.log* | ||
lerna-debug.log* | ||
|
||
# Python | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
*.so | ||
.Python | ||
env/ | ||
build/ | ||
develop-eggs/ | ||
downloads/ | ||
eggs/ | ||
.eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
var/ | ||
wheels/ | ||
*.egg-info/ | ||
.installed.cfg | ||
*.egg | ||
venv/ | ||
.venv/ | ||
|
||
# Backend | ||
*.parquet |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
# OpenSciMetrics Javascript Dashboard | ||
|
||
## Features | ||
|
||
- 📊 Interactive data visualization of open science trends | ||
- 📈 Time series analysis of open code and data practices | ||
- 🔍 Advanced filtering capabilities | ||
- 📱 Responsive design for all devices | ||
- ⚡ Real-time data processing | ||
- 🎯 Performance-optimized for large datasets | ||
|
||
## Tech Stack | ||
|
||
### Frontend | ||
- React with TypeScript | ||
- Recharts for data visualization | ||
- Tailwind CSS for styling | ||
- Axios for API communication | ||
- Lucide React for icons | ||
|
||
### Backend | ||
- FastAPI (Python) | ||
- Pandas for data processing | ||
- Uvicorn ASGI server | ||
- Parquet file handling | ||
|
||
## Getting Started | ||
|
||
### Prerequisites | ||
|
||
- Python 3.8 or higher | ||
- Node.js 16 or higher | ||
- npm or yarn | ||
- Virtual environment (recommended) | ||
|
||
### Data Setup | ||
|
||
1. Place your `matches.parquet` file in the `backend` directory: | ||
``` | ||
backend/ | ||
├── matches.parquet # Place your Parquet file here | ||
├── main.py | ||
├── routers/ | ||
└── services/ | ||
``` | ||
|
||
### Backend Setup | ||
|
||
1. Create and activate virtual environment: | ||
```bash | ||
python -m venv venv | ||
source venv/bin/activate # Unix | ||
.\venv\Scripts\activate # Windows | ||
``` | ||
|
||
2. Install dependencies: | ||
```bash | ||
pip install fastapi uvicorn pandas pyarrow | ||
``` | ||
|
||
3. Start the server: | ||
```bash | ||
uvicorn main:app --reload | ||
``` | ||
|
||
The API will be available at `http://localhost:8000` | ||
|
||
### Frontend Setup | ||
|
||
1. Install dependencies: | ||
```bash | ||
npm install | ||
``` | ||
|
||
2. Start development server: | ||
```bash | ||
npm run dev | ||
``` | ||
|
||
The application will be available at `http://localhost:5173` | ||
|
||
## Project Structure | ||
|
||
``` | ||
. | ||
├── backend/ | ||
│ ├── main.py # FastAPI application entry point | ||
│ ├── routers/ | ||
│ │ └── metrics.py # API route definitions | ||
│ └── services/ | ||
│ └── data_service.py # Data processing logic | ||
│ | ||
├── src/ | ||
│ ├── components/ # React components | ||
│ │ ├── charts/ # Data visualization components | ||
│ │ ├── filters/ # Filter components | ||
│ │ ├── layout/ # Layout components | ||
│ │ └── metrics/ # Metric display components | ||
│ ├── hooks/ # Custom React hooks | ||
│ ├── services/ # API services | ||
│ ├── types/ # TypeScript type definitions | ||
│ └── utils/ # Utility functions | ||
│ | ||
└── public/ # Static assets | ||
``` | ||
|
||
## API Endpoints | ||
|
||
- `GET /api/summary` - Get overall metrics summary | ||
- `GET /api/timeseries` - Get time series data | ||
- `GET /api/country-distribution` - Get data distribution by country | ||
- `GET /api/journal-distribution` - Get data distribution by journal | ||
|
||
## Development | ||
|
||
### Code Style | ||
|
||
- Frontend follows TypeScript best practices | ||
- Backend uses Python type hints | ||
- ESLint and Prettier for code formatting | ||
- Pre-commit hooks for code quality | ||
|
||
## Acknowledgments | ||
|
||
- Data source: [OpenSciMetrics Dataset](https://github.com/nimh-dsst/osm) | ||
- Built with [FastAPI](https://fastapi.tiangolo.com/) and [React](https://reactjs.org/) | ||
- Visualization powered by [Recharts](https://recharts.org/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from fastapi import FastAPI | ||
from fastapi.middleware.cors import CORSMiddleware | ||
from routers import metrics | ||
|
||
app = FastAPI(title="OpenSciMetrics API") | ||
|
||
app.add_middleware( | ||
CORSMiddleware, | ||
allow_origins=["http://localhost:5173"], | ||
allow_credentials=True, | ||
allow_methods=["*"], | ||
allow_headers=["*"], | ||
) | ||
|
||
app.include_router(metrics.router, prefix="/api") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from fastapi import APIRouter | ||
from services.data_service import DataService | ||
|
||
router = APIRouter() | ||
data_service = DataService() | ||
|
||
@router.get("/summary") | ||
async def get_summary(): | ||
return data_service.get_summary() | ||
|
||
@router.get("/timeseries") | ||
async def get_timeseries(): | ||
return data_service.get_timeseries() | ||
|
||
@router.get("/country-distribution") | ||
async def get_country_distribution(): | ||
return data_service.get_country_distribution() | ||
|
||
@router.get("/journal-distribution") | ||
async def get_journal_distribution(): | ||
return data_service.get_journal_distribution() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import pandas as pd | ||
from typing import List, Dict | ||
|
||
class DataService: | ||
def __init__(self): | ||
self.df = pd.read_parquet("matches.parquet") | ||
|
||
def get_summary(self) -> Dict: | ||
return { | ||
"totalRecords": len(self.df), | ||
"openCodePercentage": float(self.df['is_open_code'].mean() * 100), | ||
"openDataPercentage": float(self.df['is_open_data'].mean() * 100), | ||
"uniqueJournals": int(self.df['journal'].nunique()), | ||
"uniqueCountries": int(self.df['affiliation_country'].nunique()), | ||
} | ||
|
||
def get_timeseries(self) -> List[Dict]: | ||
yearly_stats = self.df.groupby('year').agg({ | ||
'is_open_code': 'mean', | ||
'is_open_data': 'mean' | ||
}).reset_index() | ||
|
||
return [ | ||
{ | ||
"year": int(row['year']), | ||
"openCode": round(float(row['is_open_code'] * 100), 2), | ||
"openData": round(float(row['is_open_data'] * 100), 2) | ||
} | ||
for _, row in yearly_stats.iterrows() | ||
] | ||
|
||
def get_country_distribution(self) -> List[Dict]: | ||
return (self.df['affiliation_country'] | ||
.value_counts() | ||
.head(10) | ||
.to_dict()) | ||
|
||
def get_journal_distribution(self) -> List[Dict]: | ||
return (self.df['journal'] | ||
.value_counts() | ||
.head(10) | ||
.to_dict()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import js from '@eslint/js'; | ||
import globals from 'globals'; | ||
import reactHooks from 'eslint-plugin-react-hooks'; | ||
import reactRefresh from 'eslint-plugin-react-refresh'; | ||
import tseslint from 'typescript-eslint'; | ||
|
||
export default tseslint.config( | ||
{ ignores: ['dist'] }, | ||
{ | ||
extends: [js.configs.recommended, ...tseslint.configs.recommended], | ||
files: ['**/*.{ts,tsx}'], | ||
languageOptions: { | ||
ecmaVersion: 2020, | ||
globals: globals.browser, | ||
}, | ||
plugins: { | ||
'react-hooks': reactHooks, | ||
'react-refresh': reactRefresh, | ||
}, | ||
rules: { | ||
...reactHooks.configs.recommended.rules, | ||
'react-refresh/only-export-components': [ | ||
'warn', | ||
{ allowConstantExport: true }, | ||
], | ||
}, | ||
} | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>JS Dashboard</title> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
<script type="module" src="/src/main.tsx"></script> | ||
</body> | ||
</html> |
Oops, something went wrong.