diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..e4351a6c522e361828db85d13296a8b2934a8af3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,53 @@ +# Python cache +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python + +# Virtual environments +venv/ +env/ +ENV/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# Git +.git/ +.gitignore + +# Data files (will be regenerated) +backend/data/students.json + +# Documentation (not needed in runtime) +*.md +!README.md + +# Local development files +.env +.env.local + +# OS files +.DS_Store +Thumbs.db + +# Logs +*.log + +# PowerShell scripts (local dev only) +*.ps1 + +# Backup files +*.backup +*.bak + +# Render config (not needed for HuggingFace) +render.yaml + +# Railway config (not needed for HuggingFace) +Procfile +runtime.txt diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..a6344aac8c09253b3b630fb776ae94478aa0275b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,35 @@ +*.7z filter=lfs diff=lfs merge=lfs -text +*.arrow filter=lfs diff=lfs merge=lfs -text +*.bin filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.ckpt filter=lfs diff=lfs merge=lfs -text +*.ftz filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.h5 filter=lfs diff=lfs merge=lfs -text +*.joblib filter=lfs diff=lfs merge=lfs -text +*.lfs.* filter=lfs diff=lfs merge=lfs -text +*.mlmodel filter=lfs diff=lfs merge=lfs -text +*.model filter=lfs diff=lfs merge=lfs -text +*.msgpack filter=lfs diff=lfs merge=lfs -text +*.npy filter=lfs diff=lfs merge=lfs -text +*.npz filter=lfs diff=lfs merge=lfs -text +*.onnx filter=lfs diff=lfs merge=lfs -text +*.ot filter=lfs diff=lfs merge=lfs -text +*.parquet filter=lfs diff=lfs merge=lfs -text +*.pb filter=lfs diff=lfs merge=lfs -text +*.pickle filter=lfs diff=lfs merge=lfs -text +*.pkl filter=lfs diff=lfs merge=lfs -text +*.pt filter=lfs diff=lfs merge=lfs -text +*.pth filter=lfs diff=lfs merge=lfs -text +*.rar filter=lfs diff=lfs merge=lfs -text +*.safetensors filter=lfs diff=lfs merge=lfs -text +saved_model/**/* filter=lfs diff=lfs merge=lfs -text +*.tar.* filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.tflite filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.wasm filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text +*tfevents* filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..9f36a96573fb585cc41bd527ece8bda56040c2c2 Binary files /dev/null and b/.gitignore differ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..d0b88cbf1bbc267df223865c0c64224a90d8e347 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +# Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker +# Dockerfile for TalimBot - AI-Powered Student Grouping System + +FROM python:3.11 + +# Create non-root user (Hugging Face security requirement) +RUN useradd -m -u 1000 user +USER user + +# Set environment variables +ENV HOME=/home/user \ + PATH=/home/user/.local/bin:$PATH \ + PYTHONUNBUFFERED=1 + +# Set working directory +WORKDIR /app + +# Copy requirements and install dependencies +COPY --chown=user ./requirements.txt /app/requirements.txt +RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt + +# Copy the entire backend folder (contains main.py, static files, etc.) +COPY --chown=user ./backend /app/backend + +# Copy resources_references folder (optional, for documentation) +COPY --chown=user ./resources_references /app/resources_references + +# Expose port 7860 (Hugging Face Spaces requirement) +EXPOSE 7860 + +# Change to backend directory where main.py is located +WORKDIR /app/backend + +# Run the FastAPI application on port 7860 +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"] diff --git a/HUGGINGFACE_DEPLOYMENT.md b/HUGGINGFACE_DEPLOYMENT.md new file mode 100644 index 0000000000000000000000000000000000000000..003389a93747e95a7dcf4242ca650ae8b6382c19 --- /dev/null +++ b/HUGGINGFACE_DEPLOYMENT.md @@ -0,0 +1,232 @@ +# Hugging Face Spaces Deployment Guide for TalimBot + +## Status: Ready to Deploy ✅ + +All necessary files have been created and committed. Follow the steps below to complete the deployment. + +--- + +## Files Created + +1. **Dockerfile** - Docker configuration for HF Spaces (port 7860) +2. **.dockerignore** - Excludes unnecessary files from Docker build +3. **README_HF.md** - Hugging Face Space description with metadata + +--- + +## Step 1: Get Your Hugging Face Access Token + +1. Go to: https://huggingface.co/settings/tokens +2. Click **"New token"** +3. Name it: `TalimBot Deploy` +4. Type: **Write** (must have write permissions) +5. Click **"Generate a token"** +6. **COPY THE TOKEN** (you won't see it again!) + +--- + +## Step 2: Push to Hugging Face + +The git remote is already configured. Now push your code: + +```powershell +git push huggingface main +``` + +When prompted: +- **Username**: `parinazAkef` (your HF username) +- **Password**: Paste your **access token** (not your actual password!) + +--- + +## Step 3: Configure Environment Secret + +1. Go to: https://huggingface.co/spaces/parinazAkef/talimbot +2. Click the **Settings** tab (top of page) +3. Scroll to **"Variables and secrets"** +4. Click **"New secret"** +5. Name: `OPENROUTER_API_KEY` +6. Value: Your OpenRouter API key (starts with `sk-or-v1-`) +7. Click **"Save"** + +**✅ You've already done this step!** + +--- + +## Step 4: Wait for Build + +After pushing, Hugging Face will: +1. Show "Building" status (3-5 minutes) +2. Pull Docker image, install dependencies +3. Start your FastAPI app on port 7860 +4. Show "Running" status when ready + +--- + +## Step 5: Access Your Live App + +Once status shows **"Running"** (green), your app will be available at: + +**Primary URL:** +``` +https://huggingface.co/spaces/parinazAkef/talimbot +``` + +**Direct URL (no HF frame):** +``` +https://parinazakef-talimbot.hf.space +``` + +--- + +## Step 6: Test the Application + +### Teacher Login +1. Go to your HF Space URL +2. Click on **"معلم"** (Teacher) +3. Password: `teacher123` +4. You should see the teacher dashboard + +### Student Login +1. Click on **"دانش آموز"** (Student) +2. Enter national code: `0921111111` (demo account) +3. You should see: **پریناز عاکف** dashboard + +### Test Grouping +1. Login as teacher +2. Use **"پر کردن داده‌های تست"** to generate 10 sample students +3. Click **"شروع گروه‌بندی"** +4. Enter course name (e.g., "ریاضی") +5. Wait 30-60 seconds for AI grouping +6. Check results! + +--- + +## Troubleshooting + +### If Build Fails + +**Error: "Port 7860 not listening"** +- Fixed! Our Dockerfile uses `--port 7860` + +**Error: "OPENROUTER_API_KEY not found"** +- Go to Space Settings → Add secret `OPENROUTER_API_KEY` + +**Error: "File not found: main.py"** +- Fixed! Dockerfile copies `backend/` folder and sets working directory + +### If Push Fails + +**Error: "Authentication failed"** +- Make sure you're using an **access token**, not your password +- Generate new token with **Write** permissions + +**Error: "Repository not found"** +- Check space URL: https://huggingface.co/spaces/parinazAkef/talimbot +- Make sure space exists and is public + +--- + +## File Structure (Docker Container) + +``` +/app/ + ├── requirements.txt + ├── backend/ + │ ├── main.py # FastAPI app (runs on port 7860) + │ ├── grouping_logic.py # AI grouping algorithm + │ ├── data/ + │ │ └── students.json.backup # Initial data template + │ └── static/ + │ ├── pages/ # HTML pages + │ ├── assets/ # CSS, JS files + │ └── Icons/ # Images + └── resources_references/ # Documentation +``` + +--- + +## Environment Variables + +The Space automatically provides: +- `OPENROUTER_API_KEY` (from Secrets) +- `PORT=7860` (HF Spaces requirement) + +Your FastAPI app reads `OPENROUTER_API_KEY` via: +```python +api_key = os.getenv("OPENROUTER_API_KEY") +``` + +--- + +## Next Steps After Deployment + +1. **Update GitHub README** - Add HF Space badge and link +2. **Share URL** - Send to users/colleagues +3. **Monitor Usage** - Check Logs tab in HF Space +4. **Add to Profile** - Pin Space to your HF profile + +--- + +## Updating Your App + +To push changes: + +```powershell +# Make your code changes +git add . +git commit -m "Your update description" + +# Push to GitHub (optional) +git push fork main + +# Push to Hugging Face (triggers rebuild) +git push huggingface main +``` + +HF will automatically rebuild and redeploy! + +--- + +## Comparison: Railway vs Hugging Face + +| Feature | Railway | Hugging Face | +|---------|---------|--------------| +| Free Tier | 500 hours/month | Unlimited (24/7) | +| Sleep Mode | No | No | +| Cold Start | No | No | +| Iran Access | Sometimes blocked | More accessible | +| Custom Domain | Yes (paid) | Yes (subdomain free) | +| Secrets | Environment variables | Secrets (encrypted) | +| Logs | Real-time | Real-time | +| Auto-deploy | Git push | Git push | + +--- + +## Contact & Support + +- **Hugging Face Docs**: https://huggingface.co/docs/hub/spaces-sdks-docker +- **Your Space**: https://huggingface.co/spaces/parinazAkef/talimbot +- **OpenRouter Docs**: https://openrouter.ai/docs + +--- + +## Summary + +✅ **Completed:** +- Created Dockerfile (port 7860) +- Created .dockerignore +- Created README_HF.md +- Committed files to git +- Added HF remote + +🔄 **Your Action Required:** +1. Get HF access token from https://huggingface.co/settings/tokens +2. Run: `git push huggingface main` +3. Enter username + token when prompted +4. Wait for "Running" status +5. Test at: https://parinazakef-talimbot.hf.space + +--- + +Good luck! 🚀 diff --git a/MANUAL_UPDATE_INSTRUCTIONS.md b/MANUAL_UPDATE_INSTRUCTIONS.md new file mode 100644 index 0000000000000000000000000000000000000000..6aeaed55bd09e4f969c8921cc4d29c761a1aeaf4 --- /dev/null +++ b/MANUAL_UPDATE_INSTRUCTIONS.md @@ -0,0 +1,150 @@ +# Manual Update Instructions for HuggingFace Space + +Since git push is timing out, you can update the files manually on HuggingFace. + +## Files to Update + +### 1. backend/static/pages/login.html + +**Go to:** https://huggingface.co/spaces/TalimBot/talimbot/blob/main/backend/static/pages/login.html + +**Click "Edit" button, then find and replace these sections:** + +**CHANGE 1 (around line 90):** + +Find: +```html + +

کد ملی ۱۰ رقمی خود را وارد کنید

+``` + +Replace with: +```html + +

کد ملی خود را بدون صفر ابتدایی وارد کنید

+``` + +**CHANGE 2 (around line 207):** + +Find: +```javascript + if (selectedRole === 'student') { + let nationalCode = document.getElementById('nationalCode').value.trim(); + + if (!nationalCode) { + showError('لطفاً کد ملی خود را وارد کنید'); + return; + } + + if (nationalCode.length !== 10) { + showError('کد ملی باید ۱۰ رقم باشد'); + return; + } + + // Remove leading zero if present + if (nationalCode.startsWith('0')) { + nationalCode = nationalCode.substring(1); + } +``` + +Replace with: +```javascript + if (selectedRole === 'student') { + let nationalCode = document.getElementById('nationalCode').value.trim(); + + if (!nationalCode) { + showError('لطفاً کد ملی خود را وارد کنید'); + return; + } + + // Check if starts with zero and show warning + if (nationalCode.startsWith('0')) { + showError('لطفاً صفر ابتدایی را از کد ملی حذف کنید'); + return; + } + + // No length restriction - just check if it matches a student in database +``` + +**Then click "Commit changes to main"** + +--- + +### 2. backend/static/pages/group-view.html + +**Go to:** https://huggingface.co/spaces/TalimBot/talimbot/blob/main/backend/static/pages/group-view.html + +**Click "Edit" button, then find and replace this section:** + +**CHANGE (around line 379):** + +Find: +```html +
+ + MBTI: ${member.mbti || 'ندارد'} + + + سبک: ${member.learningStyle || 'ندارد'} + + + نمره: ${member.grade.toFixed(2)} + +
+``` + +Replace with: +```html +
+ + MBTI: ${member.mbti || 'ندارد'} + + + سبک: ${member.learningStyle || 'ندارد'} + + ${member.ams ? ` + AMS: ${member.ams} + ` : ''} + ${member.cooperative ? ` + همکاری: ${member.cooperative} + ` : ''} +
+``` + +**Then click "Commit changes to main"** + +--- + +## What These Changes Do + +1. **Login page**: Removes the 10-digit limit and shows a clear error if someone tries to enter a leading zero +2. **Group view page**: Hides teammates' grades (معدل) and shows AMS and Cooperative scores instead + +## After Making Changes + +The Space will automatically rebuild (takes 2-3 minutes) and you'll see the changes live at: +https://talimbot-talimbot.hf.space/pages/login.html + +--- + +**Alternative: Try pushing again later when connection is better** + +If you prefer to keep trying git push, you can run: +```powershell +git push huggingface main --force +``` + +But given the network issues, manual editing on the website is faster and more reliable. diff --git a/Procfile b/Procfile new file mode 100644 index 0000000000000000000000000000000000000000..90bf8771de1fbc2f53fe8f07bde825099610e57b --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: cd backend && uvicorn main:app --host 0.0.0.0 --port $PORT diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..141654b49db7e48cf73887afd07cfa852b2d258e --- /dev/null +++ b/README.md @@ -0,0 +1,278 @@ +--- +title: TalimBot +emoji: 🎓 +colorFrom: blue +colorTo: indigo +sdk: docker +pinned: false +license: mit +--- + +# TalimBot - AI-Powered Student Grouping System + +An intelligent educational platform that uses advanced psychology principles and AI to create optimal learning groups for adolescent students (ages 15-16). + +## Overview + +TalimBot is a comprehensive web-based system designed to help teachers form balanced, effective study groups by analyzing student personalities, learning styles, academic motivation, and cooperative skills. The system uses OpenAI's GPT-4o to create groups that maximize learning potential through Zone of Proximal Development (ZPD) theory and complementary personality matching. + +## Key Features + +### For Teachers +- **AI-Powered Grouping**: Automated group formation using educational psychology principles +- **Multi-Factor Analysis**: Considers 7 key criteria with weighted priorities: + - ZPD Optimization (30%) - Grade-based peer tutoring scaffolding + - MBTI Complementarity (25%) - Personality type balance + - VARK Diversity (20%) - Learning style variety + - Academic Motivation (15%) - AMS score distribution + - Cooperative Skills (10%) - Teamwork ability balance + - Course-Specific Requirements + - Student Preferences (5%) +- **Real-time Dashboard**: Monitor student profile completion and grouping status +- **Data Management**: Import/export student data via JSON, fill test data +- **Result Control**: Show/hide grouping results to students +- **Flexible Reset Options**: + - Reset grouping only (preserve student data) + - Reset all data (complete system reset) + +### For Students +- **Profile Management**: Complete personality and learning assessments +- **Integrated Tests**: + - MBTI personality test (16 types) + - VARK learning style questionnaire (Visual, Aural, Read/Write, Kinesthetic) + - AMS academic motivation scale (28 questions, 0-196 score) + - Cooperative learning skills assessment (25 questions, 0-125 score) +- **Peer Preferences**: Select up to 4 preferred groupmates +- **Group View**: See assigned group members and AI reasoning (when visible) +- **Progress Tracking**: Monitor test completion status + +## Technical Architecture + +### Backend +- **Framework**: FastAPI (Python) +- **Database**: JSON file-based storage (students.json) +- **AI Integration**: OpenRouter API with GPT-4o model +- **Authentication**: Password-based teacher authentication, national code verification for students + +### Frontend +- **UI Framework**: Tailwind CSS +- **Language Support**: Persian (RTL layout) with Vazirmatn font +- **Pages**: + - Login page (student/teacher authentication) + - Student dashboard (profile management, tests) + - Teacher dashboard (grouping management, analytics) + - Group view (display assigned groups) + - AMS questionnaire (academic motivation assessment) + - Cooperative questionnaire (teamwork skills assessment) + +### Deployment +- **Platform**: Railway.app +- **Server**: Uvicorn ASGI server +- **Static Files**: Served via FastAPI StaticFiles +- **Environment Variables**: OPENROUTER_API_KEY + +## Project Structure + +``` +talimbot/ +├── backend/ +│ ├── main.py # FastAPI application and API endpoints +│ ├── grouping_logic.py # AI grouping algorithm +│ ├── data/ +│ │ ├── students.json # Student data (gitignored) +│ │ └── students.json.backup # Clean backup template +│ └── static/ +│ ├── pages/ # HTML pages +│ │ ├── login.html +│ │ ├── student-dashboard.html +│ │ ├── teacher-dashboard.html +│ │ ├── group-view.html +│ │ ├── ams-questionnaire.html +│ │ ├── cooperative-questionnaire.html +│ │ └── student-data.html +│ ├── assets/ +│ │ ├── js/ +│ │ │ ├── data.js # API client functions +│ │ │ └── grouping.js # Grouping utilities +│ │ └── css/ +│ │ └── styles.css # Custom styles +│ └── index.html # Landing page +├── resources_references/ # Documentation and reference files +│ ├── RAILWAY_DEPLOYMENT.md # Deployment guide +│ ├── TEST_RESULTS_AND_SOLUTION.md +│ ├── angizesh_tahsili.txt # AMS questionnaire source +│ ├── cooperative.txt # Cooperative questionnaire source +│ ├── students_class_notebook.txt # Original student data +│ └── sample-student-data.json # JSON import example +├── requirements.txt # Python dependencies +├── runtime.txt # Python version for Railway +├── Procfile # Railway start command +└── .gitignore # Git ignore rules + +``` + +## Installation and Setup + +### Prerequisites +- Python 3.11+ +- OpenRouter API key (get from https://openrouter.ai/keys) + +### Local Development + +1. Clone the repository: +```bash +git clone https://github.com/talimbot/talimbot.git +cd talimbot +``` + +2. Install dependencies: +```bash +pip install -r requirements.txt +``` + +3. Set environment variable: +```bash +# Windows PowerShell +$env:OPENROUTER_API_KEY="your-api-key-here" + +# Linux/Mac +export OPENROUTER_API_KEY="your-api-key-here" +``` + +4. Run the server: +```bash +cd backend +uvicorn main:app --reload --host 0.0.0.0 --port 8000 +``` + +5. Access the application: +``` +http://localhost:8000 +``` + +### Railway Deployment + +1. Fork this repository to your GitHub account + +2. Create a new project on Railway.app + +3. Connect your GitHub repository + +4. Add environment variable in Railway dashboard: + - Key: `OPENROUTER_API_KEY` + - Value: Your OpenRouter API key + +5. Railway will auto-deploy using the Procfile + +## Usage Guide + +### Teacher Workflow + +1. **Login**: Access teacher dashboard with password (default: teacher123) + +2. **Monitor Students**: View student profile completion status + +3. **Fill Test Data** (Optional): Use the test data generator to fill 10 sample students for testing + +4. **Import Data** (Optional): Upload JSON file with student information + +5. **Perform Grouping**: + - Enter course name + - Click "Start Grouping" button + - Wait for AI processing (30-60 seconds) + - Review generated groups with detailed reasoning + +6. **Show Results**: Toggle result visibility to allow students to view their groups + +7. **Reset Options**: + - Reset Grouping: Clear groups, keep student data + - Reset All Data: Complete system wipe + +### Student Workflow + +1. **Login**: Enter student number (S001-S030) and national code + +2. **Complete Profile**: + - Take MBTI personality test (external link) + - Complete VARK learning style questionnaire + - Fill AMS academic motivation scale (28 questions) + - Complete cooperative skills assessment (25 questions) + - Select up to 4 preferred groupmates (optional) + +3. **Save Information**: Click "Save All Information" button + +4. **View Group**: Access group view page to see assigned group (when teacher makes it visible) + +## Grouping Algorithm + +The AI grouping system follows a sophisticated 7-tier priority framework: + +1. **ZPD Optimization (30%)**: Mixes high and medium performers for peer tutoring +2. **MBTI Complementarity (25%)**: Pairs complementary personality types (e.g., ENFP+INTJ) +3. **VARK Diversity (20%)**: Ensures multiple learning styles in each group +4. **Academic Motivation (15%)**: Distributes high-motivation students across groups +5. **Cooperative Skills (10%)**: Balances teamwork abilities +6. **Course-Specific Requirements**: Adapts to subject matter needs +7. **Student Preferences (5%)**: Honors preferences when they don't compromise other criteria + +Groups are typically 5 students each, with some groups of 4 to avoid very small groups. + +## Data Structure + +### Student Object +```json +{ + "studentNumber": "S001", + "name": "Student Name", + "nationalCode": "1234567890", + "mbti": "INTJ", + "learningStyle": "Visual", + "ams": "150", + "cooperative": "95", + "grade": 18.5, + "preferredStudents": ["S002", "S003"], + "group": 1 +} +``` + +## API Endpoints + +- `GET /api/students` - Get all students +- `GET /api/student/{student_number}` - Get specific student +- `PUT /api/student/{student_number}` - Update student data +- `POST /api/grouping/perform` - Execute AI grouping +- `GET /api/grouping/status` - Get grouping statistics +- `POST /api/grouping/reset` - Reset grouping only +- `POST /api/data/reset-all` - Reset all data +- `POST /api/grouping/toggle-visibility` - Show/hide results +- `POST /api/auth/teacher` - Verify teacher password +- `POST /api/auth/student` - Authenticate student +- `GET /api/student/{student_number}/group` - Get student's group + +## Security Notes + +- Student data stored in students.json (excluded from version control) +- Teacher password: "teacher123" (change in production) +- National codes used for student authentication +- API key required for AI grouping functionality + +## Educational Foundation + +This system is based on: +- **Vygotsky's Zone of Proximal Development (ZPD)**: Optimal learning occurs when students work slightly above their current level with peer support +- **MBTI Complementarity Research**: Diverse personality types enhance team creativity and problem-solving +- **VARK Learning Theory**: Multiple learning styles improve knowledge retention +- **Academic Motivation Scale (AMS)**: Measures intrinsic and extrinsic motivation factors +- **Cooperative Learning Principles**: Teamwork skills are essential for collaborative success + +## License + +This project is for educational purposes. + +## Contributors + +Developed for educational psychology research and classroom implementation. + +## Support + +For issues or questions, please refer to the documentation in the `resources_references/` folder. diff --git a/README_HF.md b/README_HF.md new file mode 100644 index 0000000000000000000000000000000000000000..cc89bfda529af82b2a81c3d06884edd4e4d8cd90 --- /dev/null +++ b/README_HF.md @@ -0,0 +1,54 @@ +--- +title: TalimBot +emoji: 🎓 +colorFrom: teal +colorTo: cyan +sdk: docker +pinned: false +license: mit +--- + +# TalimBot - AI-Powered Student Grouping System + +An intelligent educational platform that uses advanced psychology principles and AI to create optimal learning groups for adolescent students (ages 15-16). + +## Features + +- **AI-Powered Grouping**: Automated group formation using OpenAI GPT-4o +- **Educational Psychology**: Based on ZPD theory, MBTI complementarity, VARK learning styles +- **Teacher Dashboard**: Monitor students and manage grouping +- **Student Dashboard**: Complete personality and learning assessments +- **Persian Language**: Full RTL support with Vazirmatn font + +## Tech Stack + +- **Backend**: FastAPI (Python 3.11) +- **Frontend**: Vanilla JavaScript, Tailwind CSS +- **AI**: OpenRouter API with GPT-4o +- **Deployment**: Hugging Face Spaces (Docker) + +## Usage + +1. **Teacher Login**: Use password to access teacher dashboard +2. **Student Login**: Enter national code (کد ملی) to access student dashboard +3. **Complete Profiles**: Students fill MBTI, VARK, AMS, and Cooperative assessments +4. **Create Groups**: Teacher runs AI grouping algorithm +5. **View Results**: Students see their assigned groups + +## Demo Account + +For demonstration purposes, login with: +- National Code: 0921111111 +- Name: پریناز عاکف + +This account is for demo only and won't be included in grouping. + +## Configuration + +This Space requires the `OPENROUTER_API_KEY` environment variable to be set in the Secrets section. + +Get your free API key at: https://openrouter.ai/keys + +## License + +MIT License - For educational purposes diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000000000000000000000000000000000000..964baa495b03a387bf554f086f0a2f41f9ebdebd --- /dev/null +++ b/backend/README.md @@ -0,0 +1,53 @@ +# TalimBot Backend + +FastAPI backend server for the TalimBot student grouping system. + +## Quick Start + +```bash +# Install dependencies (one time only) +pip install -r requirements.txt + +# Start the server +python main.py +``` + +Server will run on `http://localhost:8000` + +## API Endpoints + +- `GET /` - Health check +- `GET /api/students` - Get all students +- `GET /api/student/{id}` - Get specific student +- `PUT /api/student/{id}` - Update student info +- `POST /api/grouping/perform` - Perform AI grouping +- `GET /api/grouping/status` - Get grouping statistics +- `POST /api/grouping/toggle-visibility` - Show/hide results to students +- `POST /api/grouping/reset` - Reset grouping + +## Configuration + +### OpenRouter API Key +Located in `grouping_logic.py`: +```python +OPENROUTER_API_KEY = 'your-key-here' +``` + +### Teacher Password +Located in `main.py` (SystemData model): +```python +teacherPassword: str = "teacher123" +``` + +## Data Storage + +Student data is stored in `data/students.json` (auto-created on first run) + +## Dependencies + +- fastapi - Web framework +- uvicorn - ASGI server +- pydantic - Data validation +- aiohttp - Async HTTP client for API calls + +See `requirements.txt` for versions. diff --git a/backend/__pycache__/grouping_logic.cpython-310.pyc b/backend/__pycache__/grouping_logic.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c53c889a22330cf5c5d0488044c1f9c0de53a279 Binary files /dev/null and b/backend/__pycache__/grouping_logic.cpython-310.pyc differ diff --git a/backend/__pycache__/main.cpython-310.pyc b/backend/__pycache__/main.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b219cbaa160f10528ca6a72243137290b8f2c3ec Binary files /dev/null and b/backend/__pycache__/main.cpython-310.pyc differ diff --git a/backend/data/students.json b/backend/data/students.json new file mode 100644 index 0000000000000000000000000000000000000000..0e4f06b77e1b993ce9a9b276ea027b251cb559f6 --- /dev/null +++ b/backend/data/students.json @@ -0,0 +1,388 @@ +{ + "students": [ + { + "studentNumber": "S001", + "name": "یاسمن آدینه پور", + "nationalCode": "929986644", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.77, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S002", + "name": "پریا احمدزاده", + "nationalCode": "980085330", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 17.28, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S003", + "name": "فاطمه اکبرزاده", + "nationalCode": "970154550", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 16.71, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S004", + "name": "آناهیتا الهی مهر", + "nationalCode": "26425955", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.05, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S005", + "name": "مریم امیری", + "nationalCode": "980093341", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.87, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S006", + "name": "باران برادران رحیمی", + "nationalCode": "960043985", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.07, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S007", + "name": "مایسا بصیری امین", + "nationalCode": "960089446", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.33, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S008", + "name": "دلارام ثابت عهد", + "nationalCode": "960125620", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.55, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S009", + "name": "شاینا جان محمدی", + "nationalCode": "960068041", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.47, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S010", + "name": "آیدا جوان", + "nationalCode": "95112313", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 16.77, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S011", + "name": "سارینا حاجی آبادی", + "nationalCode": "999216751", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 16.08, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S012", + "name": "هستی حسن پور جوان", + "nationalCode": "960074198", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.55, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S013", + "name": "فاطمه حسینی", + "nationalCode": "2400410259", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.07, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S014", + "name": "غزل خسروی", + "nationalCode": "929995767", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 15.05, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S015", + "name": "غزل ذباح", + "nationalCode": "960110186", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.25, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S016", + "name": "نازنین زهرا راشکی", + "nationalCode": "3661516087", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 17.02, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S017", + "name": "ویونا روح نواز", + "nationalCode": "314458344", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.7, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S018", + "name": "روژینا سعادتی", + "nationalCode": "960051023", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.2, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S019", + "name": "ترنم شعبانی", + "nationalCode": "950083100", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.37, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S020", + "name": "ستایش شفابخش", + "nationalCode": "960126899", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.36, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S021", + "name": "فاطمه شیرزادخان", + "nationalCode": "980120756", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.33, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S022", + "name": "آرزو علی جوی", + "nationalCode": "960054316", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 17.98, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S023", + "name": "آناهیتا قنادزاده", + "nationalCode": "960089836", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.84, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S024", + "name": "نیایش کارگر", + "nationalCode": "929956052", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 17.74, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S025", + "name": "باران کبریایی نسب", + "nationalCode": "980119588", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.82, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S026", + "name": "زینب کیانوش", + "nationalCode": "970072678", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.58, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S027", + "name": "ستایش محمودی", + "nationalCode": "929904656", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.33, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S028", + "name": "ستایش مشتاقی", + "nationalCode": "361282217", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 17.67, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S029", + "name": "مهتاب معلمی", + "nationalCode": "960070265", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.56, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S030", + "name": "باران وحدتی", + "nationalCode": "929916913", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 15.02, + "preferredStudents": [], + "group": null + } + ], + "courseName": "ریاضی", + "groupingComplete": true, + "groupingResults": { + "groups": [ + { + "groupNumber": 1, + "students": [ + "S001", + "S003", + "S004" + ], + "reasoning": "این گروه شامل ۲ درون‌گرا (S001, S003) و ۱ برون‌گرا (S004) است. همچنین این گروه شامل یک دانش‌آموز با سبک یادگیری بصری (S001)، یک دانش‌آموز با سبک یادگیری حرکتی (S003) و یک دانش‌آموز با سبک یادگیری بصوتی (S004) است. همچنین نمرات این دانش‌آموزان به ترتیب 18.77، 16.71 و 19.05 است که یک توازن خوب بین نمرات دانش‌آموزان در این گروه ایجاد می‌کند." + }, + { + "groupNumber": 2, + "students": [ + "S002" + ], + "reasoning": "این گروه شامل یک برون‌گرا (S002) است که با توجه به داده‌های ورودی تنها دانش‌آموز برون‌گرا در داده‌ها است. همچنین این دانش‌آموز با سبک یادگیری بصوتی و نمره 17.28 انتخاب شده است." + } + ] + }, + "resultsVisible": false, + "teacherPassword": "teacher123" +} \ No newline at end of file diff --git a/backend/data/students.json.backup b/backend/data/students.json.backup new file mode 100644 index 0000000000000000000000000000000000000000..0e4f06b77e1b993ce9a9b276ea027b251cb559f6 --- /dev/null +++ b/backend/data/students.json.backup @@ -0,0 +1,388 @@ +{ + "students": [ + { + "studentNumber": "S001", + "name": "یاسمن آدینه پور", + "nationalCode": "929986644", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.77, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S002", + "name": "پریا احمدزاده", + "nationalCode": "980085330", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 17.28, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S003", + "name": "فاطمه اکبرزاده", + "nationalCode": "970154550", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 16.71, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S004", + "name": "آناهیتا الهی مهر", + "nationalCode": "26425955", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.05, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S005", + "name": "مریم امیری", + "nationalCode": "980093341", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.87, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S006", + "name": "باران برادران رحیمی", + "nationalCode": "960043985", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.07, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S007", + "name": "مایسا بصیری امین", + "nationalCode": "960089446", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.33, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S008", + "name": "دلارام ثابت عهد", + "nationalCode": "960125620", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.55, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S009", + "name": "شاینا جان محمدی", + "nationalCode": "960068041", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.47, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S010", + "name": "آیدا جوان", + "nationalCode": "95112313", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 16.77, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S011", + "name": "سارینا حاجی آبادی", + "nationalCode": "999216751", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 16.08, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S012", + "name": "هستی حسن پور جوان", + "nationalCode": "960074198", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.55, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S013", + "name": "فاطمه حسینی", + "nationalCode": "2400410259", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.07, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S014", + "name": "غزل خسروی", + "nationalCode": "929995767", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 15.05, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S015", + "name": "غزل ذباح", + "nationalCode": "960110186", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.25, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S016", + "name": "نازنین زهرا راشکی", + "nationalCode": "3661516087", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 17.02, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S017", + "name": "ویونا روح نواز", + "nationalCode": "314458344", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.7, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S018", + "name": "روژینا سعادتی", + "nationalCode": "960051023", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.2, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S019", + "name": "ترنم شعبانی", + "nationalCode": "950083100", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.37, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S020", + "name": "ستایش شفابخش", + "nationalCode": "960126899", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.36, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S021", + "name": "فاطمه شیرزادخان", + "nationalCode": "980120756", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.33, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S022", + "name": "آرزو علی جوی", + "nationalCode": "960054316", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 17.98, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S023", + "name": "آناهیتا قنادزاده", + "nationalCode": "960089836", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.84, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S024", + "name": "نیایش کارگر", + "nationalCode": "929956052", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 17.74, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S025", + "name": "باران کبریایی نسب", + "nationalCode": "980119588", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.82, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S026", + "name": "زینب کیانوش", + "nationalCode": "970072678", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.58, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S027", + "name": "ستایش محمودی", + "nationalCode": "929904656", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 19.33, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S028", + "name": "ستایش مشتاقی", + "nationalCode": "361282217", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 17.67, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S029", + "name": "مهتاب معلمی", + "nationalCode": "960070265", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 18.56, + "preferredStudents": [], + "group": null + }, + { + "studentNumber": "S030", + "name": "باران وحدتی", + "nationalCode": "929916913", + "mbti": null, + "learningStyle": null, + "ams": null, + "cooperative": null, + "grade": 15.02, + "preferredStudents": [], + "group": null + } + ], + "courseName": "ریاضی", + "groupingComplete": true, + "groupingResults": { + "groups": [ + { + "groupNumber": 1, + "students": [ + "S001", + "S003", + "S004" + ], + "reasoning": "این گروه شامل ۲ درون‌گرا (S001, S003) و ۱ برون‌گرا (S004) است. همچنین این گروه شامل یک دانش‌آموز با سبک یادگیری بصری (S001)، یک دانش‌آموز با سبک یادگیری حرکتی (S003) و یک دانش‌آموز با سبک یادگیری بصوتی (S004) است. همچنین نمرات این دانش‌آموزان به ترتیب 18.77، 16.71 و 19.05 است که یک توازن خوب بین نمرات دانش‌آموزان در این گروه ایجاد می‌کند." + }, + { + "groupNumber": 2, + "students": [ + "S002" + ], + "reasoning": "این گروه شامل یک برون‌گرا (S002) است که با توجه به داده‌های ورودی تنها دانش‌آموز برون‌گرا در داده‌ها است. همچنین این دانش‌آموز با سبک یادگیری بصوتی و نمره 17.28 انتخاب شده است." + } + ] + }, + "resultsVisible": false, + "teacherPassword": "teacher123" +} \ No newline at end of file diff --git a/backend/grouping_logic.py b/backend/grouping_logic.py new file mode 100644 index 0000000000000000000000000000000000000000..ab3077e3be6c091f9787bd8d70d183d06c402c84 --- /dev/null +++ b/backend/grouping_logic.py @@ -0,0 +1,334 @@ +import requests +import json +import os +from typing import List, Dict, Any, Optional + +# API Configuration +OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions' + +def analyze_mbti(mbti: str) -> Dict[str, str]: + """Helper to break down MBTI into explicit tags""" + if not mbti or len(mbti) < 4: + return {"type": "Unknown", "tags": []} + + m = mbti.upper() + return { + "energy": 'Introvert (درون‌گرا)' if m[0] == 'I' else 'Extrovert (برون‌گرا)', + "info": 'Intuitive' if m[1] == 'N' else 'Sensing', + "decision": 'Thinking' if m[2] == 'T' else 'Feeling', + "structure": 'Judging' if m[3] == 'J' else 'Perceiving' + } + +def group_students_with_ai(students: List[Any], course_name: str, api_key: Optional[str] = None) -> Dict[str, Any]: + """ + Group students using OpenRouter API (ChatGPT) with advanced educational psychology principles + Args: + students: List of student objects + course_name: Name of the course + api_key: OpenRouter API key (optional, falls back to env var) + """ + # Get API key from parameter or environment variable + openrouter_key = api_key or os.getenv('OPENROUTER_API_KEY', '') + + # Clean the API key - remove any whitespace and newlines + if openrouter_key: + openrouter_key = openrouter_key.strip().replace('\n', '').replace('\r', '') + + if not openrouter_key or openrouter_key == '': + raise Exception( + "OpenRouter API key not configured! " + "Please add OPENROUTER_API_KEY in Railway Variables tab. " + "Get your free key at: https://openrouter.ai/keys" + ) + + # Validate API key format (but don't log the key) + if not openrouter_key.startswith('sk-or-v1-'): + raise Exception( + f"Invalid API key format. OpenRouter keys should start with 'sk-or-v1-'. " + f"Please check your OPENROUTER_API_KEY in Railway Variables." + ) + + # Sanitization & Data Enrichment + valid_student_ids = set(s.studentNumber for s in students) + + student_data = [] + for s in students: + mbti_details = analyze_mbti(s.mbti) + student_data.append({ + "id": s.studentNumber, + "name": s.name, + "mbti": s.mbti, + "mbti_analysis": mbti_details, + "learningStyle": s.learningStyle, + "ams": s.ams if hasattr(s, 'ams') else None, + "cooperative": s.cooperative if hasattr(s, 'cooperative') else None, + "grade": s.grade, + "preferredStudents": [id for id in (s.preferredStudents or []) if id in valid_student_ids] + }) + + # Dynamic Group Size Logic + total_students = len(students) + if total_students < 4: + size_guidance = "a single group" + elif total_students < 8: + size_guidance = "groups of 3-4 students" + else: + # Prefer groups of 5, but use groups of 4 if needed to avoid very small groups + # Examples: 30 students = 6 groups of 5 + # 27 students = 5 groups of 5 + 1 group of 2 (bad) → instead: 3 groups of 5 + 3 groups of 4 (good) + # 25 students = 5 groups of 5 + # 22 students = 4 groups of 5 + 1 group of 2 (bad) → instead: 2 groups of 5 + 3 groups of 4 (good) + remainder = total_students % 5 + if remainder == 1: + # e.g., 21 students: would be 4 groups of 5 + 1 of 1 → instead make 3 groups of 5 + 2 groups of 3 + size_guidance = "groups of 5 students, with some groups of 3-4 if needed to avoid groups smaller than 3" + elif remainder == 2: + # e.g., 22 students: would be 4 groups of 5 + 1 of 2 → instead make 2 groups of 5 + 3 groups of 4 + size_guidance = "groups of 5 students, with some groups of 4 if needed to avoid groups of 2" + else: + size_guidance = "groups of 5 students" + + # The Enhanced Prompt + prompt = f"""You are an expert educational psychologist specializing in adolescent team formation and Vygotsky's Zone of Proximal Development (ZPD). Create optimal learning groups for "{course_name}" course with 15-16 year old students. + +INPUT DATA: +{json.dumps(student_data, ensure_ascii=False, indent=2)} + +TOTAL STUDENTS: {total_students} +GROUPING STRATEGY: Prefer {size_guidance}. IMPORTANT: Avoid creating groups with only 1-2 students. If the math doesn't work out evenly with groups of 5, adjust by creating some groups of 4 to balance the numbers. For example: +- 30 students = 6 groups of 5 ✓ +- 27 students = 3 groups of 5 + 3 groups of 4 ✓ (NOT 5 groups of 5 + 1 group of 2 ✗) +- 25 students = 5 groups of 5 ✓ +- 22 students = 2 groups of 5 + 3 groups of 4 ✓ (NOT 4 groups of 5 + 1 group of 2 ✗) + +STUDENT AGE CONTEXT (15-16 years - Adolescence): +- High need for peer acceptance and social belonging +- Developing abstract thinking and metacognition +- Identity formation through social interactions +- Sensitivity to feedback from peers +- Collaborative learning enhances engagement + +GROUPING FRAMEWORK - PRIORITY ORDER: + +1. **ZPD OPTIMIZATION (Zone of Proximal Development)** - 30% + - Mix academic performance (grade field) to create ZPD scaffolding + - Place high performers (معدل بالا) with medium performers for peer tutoring + - Avoid grouping all high or all low performers together + - Target: Each group should have grade variance of 1-2 points to maximize learning + +2. **MBTI COMPLEMENTARITY (NOT Similarity)** - 25% + Research-based MBTI pairings for adolescent teamwork: + - ENFP + INTJ: Visionary creativity with strategic planning + - ENTP + INFJ: Innovation meets deep insight and empathy + - ENTJ + INFP: Leadership with values-driven creativity + - ESTJ + ISFP: Organization with practical creativity + - ESFJ + INTP: Social cohesion with analytical thinking + - ESTP + ISFJ: Action-oriented with detail consciousness + - ENFJ + ISTP: Motivational leadership with technical problem-solving + - ESFP + ISTJ: Enthusiasm with reliability and structure + + KEY PRINCIPLES: + - Balance E (Extrovert) and I (Introvert): 2-3 of each per group + - Complement T (Thinking) with F (Feeling) for balanced decision-making + - Mix N (Intuitive) with S (Sensing) for big-picture + detail focus + - Combine J (Judging) with P (Perceiving) for structure + flexibility + +3. **VARK DIVERSITY (Learning Styles)** - 20% + - Include different learning styles in each group: + * Visual (دیداری): Diagrams, charts, spatial understanding + * Aural (شنیداری): Discussions, verbal explanations + * Read/Write: Text-based learning, note-taking + * Kinesthetic (حرکتی): Hands-on, experiential learning + - Diversity ensures multiple teaching approaches within group + - Adolescents learn best when exposed to varied learning methods + +4. **ACADEMIC MOTIVATION (AMS Score)** - 15% + - AMS field: Academic Motivation Scale (0-196) + - Balance high and moderate motivation levels + - High motivation students (>140) can inspire others + - Avoid grouping all low-motivation (<100) students together + - Target: Each group has at least one high-motivation member + +5. **COOPERATIVE LEARNING SKILLS** - 10% + - Cooperative field: Cooperation ability (0-125) + - High cooperation students (>88) act as social facilitators + - Mix cooperation levels for peer modeling + - Students with strong cooperation skills help integrate introverts + +6. **COURSE-SPECIFIC REQUIREMENTS** - Based on "{course_name}": + - Math/Science: Prioritize T (Thinking) types, Visual/Kinesthetic learners + - Literature/Humanities: Include F (Feeling) types, Read/Write learners + - Projects/Labs: Need high Kinesthetic and ESTP/ISTP types + - Discussion-based: Ensure Aural learners and E (Extrovert) types + +7. **STUDENT PREFERENCES** - 5% (Secondary consideration) + - Honor "preferredStudents" field ONLY if it doesn't compromise above criteria + - Adolescents benefit from working outside comfort zones + - Strategic separation can reduce cliques and expand social circles + +CRITICAL RULES: +✓ ALL students MUST be assigned to a group +✓ PREFER groups of 5 students to minimize total number of groups +✓ Adjust group sizes (use groups of 4) to avoid creating groups with only 1-2 students +✓ Each group should have 3-5 students (never 1-2 students alone) +✓ Each group needs MBTI balance: 2-3 Introverts + 2-3 Extroverts +✓ Each group needs grade diversity: Mix high (>18) with medium (16-18) performers +✓ Prioritize complementary MBTI types over similar types +✓ Use provided data fields - DO NOT invent values +✓ **ABSOLUTELY CRITICAL**: Each student ID can appear in EXACTLY ONE group. NO DUPLICATES. Verify this before outputting. +✓ Double-check: Count total students in all groups = input students count + +OUTPUT FORMAT (Valid JSON Only): +{{ + "groups": [ + {{ + "groupNumber": 1, + "students": ["S001", "S002", "S003", "S004"], + "reasoning": "توضیحات کامل به فارسی - شامل: (1) تحلیل ZPD: معدل‌ها و چگونگی یادگیری همیاری (2) تکمیل MBTI: چرا این تیپ‌ها با هم سازگارند (3) تنوع VARK (4) سطح انگیزش و همکاری (5) مناسب بودن برای درس {course_name}. مثال: 'این گروه دارای ZPD مطلوب است: S001 (معدل 19.5) و S002 (معدل 17.2) به S003 (معدل 16) کمک می‌کنند. تکمیل MBTI: ENFP (S001) با خلاقیت و INTJ (S002) با برنامه‌ریزی استراتژیک همکاری می‌کنند. تنوع یادگیری: 2 Visual، 1 Aural، 1 Kinesthetic. انگیزش بالا (AMS>150) در S001 الهام‌بخش است.'" + }} + ] +}}""" + + # Make API call using requests library + headers = { + 'Content-Type': 'application/json', + 'Authorization': f'Bearer {openrouter_key}', + 'HTTP-Referer': 'https://talimbot.up.railway.app', + 'X-Title': 'TalimBot' + } + + payload = { + 'model': 'openai/gpt-4o', # Using GPT-4o for better accuracy and reasoning + 'messages': [ + { + 'role': 'system', + 'content': 'You are a precise algorithmic grouping assistant. You MUST output ONLY valid JSON - no markdown, no code blocks, no extra text. Start directly with { and end with }. CRITICAL RULE: Each student can appear in EXACTLY ONE group - no duplicates allowed. You rely on the explicit "mbti_analysis" fields provided in the user prompt for your reasoning. Verify that all student IDs appear exactly once across all groups.' + }, + { + 'role': 'user', + 'content': prompt + } + ], + 'temperature': 0.2 # Lower temperature for more consistent, logical grouping + } + + print(f"Sending request to OpenRouter API...") + + response = requests.post( + OPENROUTER_API_URL, + headers=headers, + json=payload, + timeout=60 + ) + + print(f"Response status: {response.status_code}") + print(f"Response preview: {response.text[:200]}") + + if response.status_code == 401: + try: + error_data = response.json() + error_msg = error_data.get('error', {}).get('message', 'Unauthorized') + except: + error_msg = response.text + + raise Exception( + f"OpenRouter Authentication Error: {error_msg}. " + f"Your API key is configured but invalid. Please:\n" + f"1. Go to https://openrouter.ai/keys\n" + f"2. Check if your key is active and has credits\n" + f"3. Create a NEW key if needed\n" + f"4. Update OPENROUTER_API_KEY in Railway Variables" + ) + + if response.status_code == 402: + raise Exception( + "OpenRouter Payment Required: Your account has no credits. " + "Add credits at https://openrouter.ai/credits" + ) + + if not response.ok: + try: + error_data = response.json() + error_detail = error_data.get('error', {}).get('message', response.text) + except: + error_detail = response.text + raise Exception(f"API request failed ({response.status_code}): {error_detail}") + + data = response.json() + content = data['choices'][0]['message']['content'] + print(f"🔍 DEBUG: Got response content, length: {len(content)}") + + # Parse Result - Extract JSON from markdown code blocks if present + try: + # Try direct JSON parse first + grouping_result = json.loads(content) + except json.JSONDecodeError as e: + # Try to extract JSON from markdown code blocks + import re + + # Look for JSON in ```json ... ``` or ``` ... ``` blocks + json_match = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', content, re.DOTALL) + if json_match: + try: + grouping_result = json.loads(json_match.group(1)) + print(f"✅ Extracted JSON from markdown code block") + except json.JSONDecodeError: + print(f"Failed to parse JSON from code block: {json_match.group(1)[:200]}") + raise Exception("Invalid JSON from API (even after markdown extraction)") + else: + # Try to find JSON object in the content + json_match = re.search(r'\{.*"groups".*\}', content, re.DOTALL) + if json_match: + try: + grouping_result = json.loads(json_match.group(0)) + print(f"✅ Extracted JSON object from response") + except json.JSONDecodeError: + print(f"Failed to parse extracted JSON: {json_match.group(0)[:200]}") + raise Exception("Invalid JSON from API (extraction failed)") + else: + print(f"❌ No JSON found in response. Full content:\n{content}") + raise Exception("Invalid JSON from API - no valid JSON structure found") + + # Failsafe: Add missing students if AI messed up + assigned_students = set() + for group in grouping_result['groups']: + if 'students' in group: + assigned_students.update(group['students']) + + all_ids = [s.studentNumber for s in students] + missing = [id for id in all_ids if id not in assigned_students] + + if missing: + print(f'AI missed students, adding to last group: {missing}') + if grouping_result['groups']: + grouping_result['groups'][-1]['students'].extend(missing) + grouping_result['groups'][-1]['reasoning'] += f" (سیستم دانش‌آموزان {', '.join(missing)} را به این گروه اضافه کرد)" + else: + grouping_result['groups'].append({ + "groupNumber": 1, + "students": missing, + "reasoning": "گروه بازیابی شده توسط سیستم" + }) + + return grouping_result + +async def random_grouping(students: List[Any]) -> Dict[str, Any]: + """Fallback random grouping if API fails""" + import random + + shuffled = students.copy() + random.shuffle(shuffled) + + group_size = 5 + num_groups = (len(shuffled) + group_size - 1) // group_size + + groups = [] + for i in range(num_groups): + group_students = shuffled[i * group_size:(i + 1) * group_size] + groups.append({ + "groupNumber": i + 1, + "students": [s.studentNumber for s in group_students], + "reasoning": "گروه‌بندی تصادفی (API در دسترس نبود)" + }) + + return {"groups": groups} diff --git a/backend/main.py b/backend/main.py new file mode 100644 index 0000000000000000000000000000000000000000..e7cd4acc083fb231373788c5afd32ac2a9430766 --- /dev/null +++ b/backend/main.py @@ -0,0 +1,428 @@ +from fastapi import FastAPI, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from fastapi.staticfiles import StaticFiles +from fastapi.responses import FileResponse +from pydantic import BaseModel +from typing import List, Optional, Dict, Any +import json +import os +from pathlib import Path +from dotenv import load_dotenv + +# Load environment variables from .env file (for local development) +load_dotenv() + +app = FastAPI() + +# CORS configuration - allow all origins for flexibility +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Data file path +DATA_FILE = Path(__file__).parent / "data" / "students.json" +DATA_FILE.parent.mkdir(exist_ok=True) + +# Pydantic models +class Student(BaseModel): + studentNumber: str + name: str + nationalCode: str = "" + mbti: Optional[str] = None + learningStyle: Optional[str] = None + ams: Optional[str] = None + cooperative: Optional[str] = None + grade: float + preferredStudents: List[str] = [] + group: Optional[int] = None + +class StudentUpdate(BaseModel): + mbti: Optional[str] = None + learningStyle: Optional[str] = None + ams: Optional[str] = None + cooperative: Optional[str] = None + preferredStudents: Optional[List[str]] = None + +class GroupingRequest(BaseModel): + courseName: str + +class TeacherAuthRequest(BaseModel): + password: str + +class StudentAuthRequest(BaseModel): + studentNumber: str + nationalCode: str + +class SystemData(BaseModel): + students: List[Student] + courseName: str = "" + groupingComplete: bool = False + groupingResults: Optional[Dict[str, Any]] = None + resultsVisible: bool = False + teacherPassword: str = "teacher123" + +# Initialize data +def load_data() -> SystemData: + if DATA_FILE.exists(): + with open(DATA_FILE, 'r', encoding='utf-8') as f: + data = json.load(f) + return SystemData(**data) + else: + # Initialize with real 30 students from class data + initial_students = [ + Student(studentNumber='S001', name='یاسمن آدینه پور', nationalCode='929986644', grade=18.77), + Student(studentNumber='S002', name='پریا احمدزاده', nationalCode='980085330', grade=17.28), + Student(studentNumber='S003', name='فاطمه اکبرزاده', nationalCode='970154550', grade=16.71), + Student(studentNumber='S004', name='آناهیتا الهی مهر', nationalCode='26425955', grade=19.05), + Student(studentNumber='S005', name='مریم امیری', nationalCode='980093341', grade=18.87), + Student(studentNumber='S006', name='باران برادران رحیمی', nationalCode='960043985', grade=19.07), + Student(studentNumber='S007', name='مایسا بصیری امین', nationalCode='960089446', grade=19.33), + Student(studentNumber='S008', name='دلارام ثابت عهد', nationalCode='960125620', grade=19.55), + Student(studentNumber='S009', name='شاینا جان محمدی', nationalCode='960068041', grade=19.47), + Student(studentNumber='S010', name='آیدا جوان', nationalCode='95112313', grade=16.77), + Student(studentNumber='S011', name='سارینا حاجی آبادی', nationalCode='999216751', grade=16.08), + Student(studentNumber='S012', name='هستی حسن پور جوان', nationalCode='960074198', grade=19.55), + Student(studentNumber='S013', name='فاطمه حسینی', nationalCode='2400410259', grade=19.07), + Student(studentNumber='S014', name='غزل خسروی', nationalCode='929995767', grade=15.05), + Student(studentNumber='S015', name='غزل ذباح', nationalCode='960110186', grade=19.25), + Student(studentNumber='S016', name='نازنین زهرا راشکی', nationalCode='3661516087', grade=17.02), + Student(studentNumber='S017', name='ویونا روح نواز', nationalCode='314458344', grade=18.70), + Student(studentNumber='S018', name='روژینا سعادتی', nationalCode='960051023', grade=18.20), + Student(studentNumber='S019', name='ترنم شعبانی', nationalCode='950083100', grade=19.37), + Student(studentNumber='S020', name='ستایش شفابخش', nationalCode='960126899', grade=18.36), + Student(studentNumber='S021', name='فاطمه شیرزادخان', nationalCode='980120756', grade=19.33), + Student(studentNumber='S022', name='آرزو علی جوی', nationalCode='960054316', grade=17.98), + Student(studentNumber='S023', name='آناهیتا قنادزاده', nationalCode='960089836', grade=18.84), + Student(studentNumber='S024', name='نیایش کارگر', nationalCode='929956052', grade=17.74), + Student(studentNumber='S025', name='باران کبریایی نسب', nationalCode='980119588', grade=18.82), + Student(studentNumber='S026', name='زینب کیانوش', nationalCode='970072678', grade=18.58), + Student(studentNumber='S027', name='ستایش محمودی', nationalCode='929904656', grade=19.33), + Student(studentNumber='S028', name='ستایش مشتاقی', nationalCode='361282217', grade=17.67), + Student(studentNumber='S029', name='مهتاب معلمی', nationalCode='960070265', grade=18.56), + Student(studentNumber='S030', name='باران وحدتی', nationalCode='929916913', grade=15.02), + ] + + data = SystemData(students=initial_students) + save_data(data) + return data + +def save_data(data: SystemData): + with open(DATA_FILE, 'w', encoding='utf-8') as f: + json.dump(data.dict(), f, ensure_ascii=False, indent=2) + +# API Endpoints +@app.get("/") +def read_root(): + return {"message": "TalimBot API is running"} + +@app.get("/api/students") +def get_all_students(): + data = load_data() + return {"students": data.students} + +@app.get("/api/student/{student_number}") +def get_student(student_number: str): + # Return demo account if requested + if student_number == "DEMO": + return Student( + studentNumber="DEMO", + name="پریناز عاکف", + nationalCode="0921111111", + grade=0.0, + mbti=None, + learningStyle=None, + ams=None, + cooperative=None, + preferredStudents=[], + group=None + ) + + data = load_data() + student = next((s for s in data.students if s.studentNumber == student_number), None) + if not student: + raise HTTPException(status_code=404, detail="Student not found") + return student + +@app.put("/api/student/{student_number}") +def update_student(student_number: str, updates: StudentUpdate): + # Silently ignore updates to demo account (pretend it worked) + if student_number == "DEMO": + demo_student = Student( + studentNumber="DEMO", + name="پریناز عاکف", + nationalCode="0921111111", + grade=0.0, + mbti=updates.mbti, + learningStyle=updates.learningStyle, + ams=updates.ams, + cooperative=updates.cooperative, + preferredStudents=updates.preferredStudents or [], + group=None + ) + return {"success": True, "student": demo_student} + + data = load_data() + student = next((s for s in data.students if s.studentNumber == student_number), None) + if not student: + raise HTTPException(status_code=404, detail="Student not found") + + # Update only provided fields + update_dict = updates.dict(exclude_unset=True) + for key, value in update_dict.items(): + setattr(student, key, value) + + save_data(data) + return {"success": True, "student": student} + +class GroupingRequest(BaseModel): + courseName: str + +@app.post("/api/grouping/perform") +async def perform_grouping(request: GroupingRequest): + data = load_data() + + # Get API key from environment variable + api_key = os.getenv("OPENROUTER_API_KEY") + + if not api_key: + raise HTTPException( + status_code=500, + detail="OpenRouter API key not configured. Please set OPENROUTER_API_KEY in Railway Variables tab" + ) + + # Get students with complete info (mbti and learningStyle required) + students_with_info = [s for s in data.students if s.mbti and s.learningStyle] + + if len(students_with_info) == 0: + raise HTTPException(status_code=400, detail="No students have completed their profiles yet") + + try: + # Import grouping logic + from grouping_logic import group_students_with_ai + + # group_students_with_ai is now synchronous (uses requests library) + # Run it in a thread pool to avoid blocking + import asyncio + from concurrent.futures import ThreadPoolExecutor + + loop = asyncio.get_event_loop() + with ThreadPoolExecutor() as executor: + grouping_result = await loop.run_in_executor( + executor, + lambda: group_students_with_ai(students_with_info, request.courseName, api_key) + ) + + # Apply grouping to students + for student in data.students: + student.group = None + + for group in grouping_result["groups"]: + for student_number in group["students"]: + student = next((s for s in data.students if s.studentNumber == student_number), None) + if student: + student.group = group["groupNumber"] + + data.courseName = request.courseName + data.groupingComplete = True + data.groupingResults = grouping_result + data.resultsVisible = False # Teacher must manually show results + + save_data(data) + return {"success": True, "results": grouping_result} + + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + +@app.get("/api/grouping/status") +def get_grouping_status(): + data = load_data() + students_with_info = [s for s in data.students if s.mbti and s.learningStyle] + students_grouped = [s for s in data.students if s.group is not None] + + return { + "totalStudents": len(data.students), + "studentsWithCompleteInfo": len(students_with_info), + "studentsGrouped": len(students_grouped), + "groupingComplete": data.groupingComplete, + "resultsVisible": data.resultsVisible, + "courseName": data.courseName, + "groups": data.groupingResults.get("groups", []) if data.groupingResults else [] + } + +class PasswordRequest(BaseModel): + password: str + +@app.post("/api/grouping/toggle-visibility") +def toggle_results_visibility(request: PasswordRequest): + data = load_data() + if request.password != data.teacherPassword: + raise HTTPException(status_code=403, detail="Invalid password") + + data.resultsVisible = not data.resultsVisible + save_data(data) + return {"success": True, "resultsVisible": data.resultsVisible} + +@app.post("/api/grouping/reset") +def reset_grouping(request: PasswordRequest): + data = load_data() + if request.password != data.teacherPassword: + raise HTTPException(status_code=403, detail="Invalid password") + + # Clear ONLY grouping-related data, keep student profiles intact + for student in data.students: + student.group = None + + data.groupingComplete = False + data.groupingResults = None + data.resultsVisible = False + data.courseName = "" + + save_data(data) + return {"success": True} + +@app.post("/api/data/reset-all") +def reset_all_data(request: PasswordRequest): + data = load_data() + if request.password != data.teacherPassword: + raise HTTPException(status_code=403, detail="Invalid password") + + # Clear ALL student data fields AND grouping + for student in data.students: + student.group = None + student.mbti = None + student.learningStyle = None + student.ams = None + student.cooperative = None + student.preferredStudents = [] + + data.groupingComplete = False + data.groupingResults = None + data.resultsVisible = False + data.courseName = "" + + save_data(data) + return {"success": True} + +@app.post("/api/auth/teacher") +def check_teacher_password(request: TeacherAuthRequest): + data = load_data() + return {"valid": request.password == data.teacherPassword} + +@app.post("/api/auth/student") +def authenticate_student(request: StudentAuthRequest): + # Special demo account - not stored in database + # Check for national code without leading zero (frontend strips it) + if request.nationalCode == "921111111": + demo_student = Student( + studentNumber="DEMO", + name="پریناز عاکف", + nationalCode="921111111", + grade=0.0, + mbti=None, + learningStyle=None, + ams=None, + cooperative=None, + preferredStudents=[], + group=None + ) + return {"valid": True, "student": demo_student} + + data = load_data() + student = next((s for s in data.students if s.studentNumber == request.studentNumber), None) + if not student: + raise HTTPException(status_code=404, detail="Student not found") + + if student.nationalCode != request.nationalCode: + raise HTTPException(status_code=401, detail="Invalid national code") + + return {"valid": True, "student": student} + +class NationalCodeAuthRequest(BaseModel): + nationalCode: str + +@app.post("/api/auth/student-by-nationalcode") +def authenticate_student_by_nationalcode(request: NationalCodeAuthRequest): + # Special demo account - not stored in database + # Check for national code without leading zero (frontend strips it) + if request.nationalCode == "921111111": + demo_student = Student( + studentNumber="DEMO", + name="پریناز عاکف", + nationalCode="921111111", + grade=0.0, + mbti=None, + learningStyle=None, + ams=None, + cooperative=None, + preferredStudents=[], + group=None + ) + return {"valid": True, "student": demo_student} + + data = load_data() + # Find student by national code (without leading zero) + student = next((s for s in data.students if s.nationalCode == request.nationalCode), None) + if not student: + raise HTTPException(status_code=404, detail="کد ملی در سیستم یافت نشد") + + return {"valid": True, "student": student} + +@app.get("/api/student/{student_number}/group") +def get_student_group(student_number: str): + # Demo account has no group + if student_number == "DEMO": + raise HTTPException(status_code=404, detail="Demo account is not part of any group") + + data = load_data() + + if not data.resultsVisible: + raise HTTPException(status_code=403, detail="Results are not yet visible") + + student = next((s for s in data.students if s.studentNumber == student_number), None) + if not student: + raise HTTPException(status_code=404, detail="Student not found") + + if student.group is None: + raise HTTPException(status_code=404, detail="Student not assigned to a group yet") + + # Find all students in the same group + group_members = [s for s in data.students if s.group == student.group] + + # Find the group details from results + group_info = None + if data.groupingResults: + for g in data.groupingResults.get("groups", []): + if g["groupNumber"] == student.group: + group_info = g + break + + return { + "groupNumber": student.group, + "members": group_members, + "reasoning": group_info.get("reasoning", "") if group_info else "", + "courseName": data.courseName + } + +@app.get("/api/data/backup") +def get_data_backup(): + """Download complete student data as JSON backup for safekeeping""" + data = load_data() + return data.dict() + +# ============================================ +# STATIC FILE SERVING (Frontend HTML/CSS/JS) +# ============================================ + +# Define static directory path +STATIC_DIR = Path(__file__).parent / "static" + +# Mount static files FIRST - this handles all non-API routes +app.mount("/", StaticFiles(directory=str(STATIC_DIR), html=True), name="static") + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..0549d5212e0b5d34cb0cd50a46451a3a4225070b --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,6 @@ +fastapi==0.104.1 +uvicorn==0.24.0 +pydantic==2.5.0 +requests==2.31.0 +python-multipart==0.0.6 +python-dotenv==1.0.0 diff --git a/backend/static/Icons/Additional/teacherIcon.png b/backend/static/Icons/Additional/teacherIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..11ea63b048234b9661ca53b6dcd970607a768d2e Binary files /dev/null and b/backend/static/Icons/Additional/teacherIcon.png differ diff --git a/backend/static/Icons/Additional/teacherIcon2.jpg b/backend/static/Icons/Additional/teacherIcon2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..00ba55f45c28c016de9dd78f3a9b0c622fdb2c99 Binary files /dev/null and b/backend/static/Icons/Additional/teacherIcon2.jpg differ diff --git a/backend/static/Icons/logo/Icon.png b/backend/static/Icons/logo/Icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d9dcbecafe8f1fa3138980865b9d392967f6940d Binary files /dev/null and b/backend/static/Icons/logo/Icon.png differ diff --git a/backend/static/Icons/logo/Logo_blueBackground.jpg b/backend/static/Icons/logo/Logo_blueBackground.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e8c775a86dbb733ff780963bb10ea73e203966b6 Binary files /dev/null and b/backend/static/Icons/logo/Logo_blueBackground.jpg differ diff --git a/backend/static/Icons/logo/Logo_noBackground.jpg b/backend/static/Icons/logo/Logo_noBackground.jpg new file mode 100644 index 0000000000000000000000000000000000000000..317d414f287b697d891103c98647a6c043f1c671 Binary files /dev/null and b/backend/static/Icons/logo/Logo_noBackground.jpg differ diff --git a/backend/static/Icons/logo/logo.png b/backend/static/Icons/logo/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d9dcbecafe8f1fa3138980865b9d392967f6940d Binary files /dev/null and b/backend/static/Icons/logo/logo.png differ diff --git a/backend/static/Icons/studentIcon.png b/backend/static/Icons/studentIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..1b3b4f96bca1bfada5b15c165a4469773a630aab Binary files /dev/null and b/backend/static/Icons/studentIcon.png differ diff --git a/backend/static/Icons/teacherIcon3.png b/backend/static/Icons/teacherIcon3.png new file mode 100644 index 0000000000000000000000000000000000000000..d19f6da898b38cb3391cb2705d7c09fdfdf44bf3 Binary files /dev/null and b/backend/static/Icons/teacherIcon3.png differ diff --git a/backend/static/assets/css/styles.css b/backend/static/assets/css/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..a3e7640bc78e2af49a383cc469c3c574b8720124 --- /dev/null +++ b/backend/static/assets/css/styles.css @@ -0,0 +1,727 @@ +/* سیستم گروه‌بندی دانشجویان - طراحی مدرن فارسی */ + +@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700&display=swap'); + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --primary-color: #381c80; + --primary-dark: #2a1560; + --secondary-color: #1e3a8a; + --accent-color: #5b21b6; + --success-color: #10b981; + --warning-color: #f59e0b; + --danger-color: #ef4444; + --text-dark: #1f2937; + --text-light: #6b7280; + --bg-light: #f8fafc; + --bg-white: #ffffff; + --border-color: #e2e8f0; + --shadow-sm: 0 1px 2px 0 rgba(56, 28, 128, 0.05); + --shadow-md: 0 4px 6px -1px rgba(56, 28, 128, 0.1), 0 2px 4px -1px rgba(56, 28, 128, 0.06); + --shadow-lg: 0 10px 15px -3px rgba(56, 28, 128, 0.15), 0 4px 6px -2px rgba(56, 28, 128, 0.08); + --shadow-xl: 0 20px 25px -5px rgba(56, 28, 128, 0.2), 0 10px 10px -5px rgba(56, 28, 128, 0.1); +} + +body { + font-family: 'Vazirmatn', sans-serif; + background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%); + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + color: var(--text-dark); + line-height: 1.8; + direction: rtl; + text-align: right; +} + +/* صفحات مختلف با تصاویر پس‌زمینه */ +body.login-page { + background: url('images/blueBackground.jpg') no-repeat center center fixed; + background-size: cover; +} + +body.dashboard-page { + background: url('images/darkBlueMixed.png') no-repeat center center fixed; + background-size: cover; + align-items: flex-start; +} + +body.teacher-page { + background: url('images/darkBluePlain.jpg') no-repeat center center fixed; + background-size: cover; + align-items: flex-start; +} + +/* اور‌لی برای خوانایی بهتر متن */ +body.login-page::before, +body.dashboard-page::before, +body.teacher-page::before { + content: ''; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(56, 28, 128, 0.15); + z-index: -1; +} + +/* انیمیشن‌ها */ +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* کانتینرها */ +.container { + background: rgba(255, 255, 255, 0.98); + backdrop-filter: blur(10px); + border-radius: 20px; + box-shadow: var(--shadow-xl); + padding: 35px; + max-width: 480px; + width: 100%; + animation: fadeInUp 0.5s ease; + border: 1px solid rgba(255, 255, 255, 0.3); +} + +.dashboard-container { + max-width: 1150px; + padding: 25px; + background: transparent; + box-shadow: none; + border: none; +} + +/* لوگو و هدر */ +.logo-section { + text-align: center; + margin-bottom: 20px; +} + +.logo-section img { + max-width: 200px; + height: auto; + margin-bottom: 15px; +} + +.header { + text-align: center; + margin-bottom: 25px; +} + +.header h1 { + font-size: 1.7rem; + font-weight: 700; + color: var(--primary-color); + margin-bottom: 8px; +} + +.header p { + color: var(--text-light); + font-size: 0.95rem; +} + +/* انتخاب نقش */ +.role-selector { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; + margin-bottom: 20px; +} + +.role-option { + padding: 18px 12px; + border: 2px solid var(--border-color); + border-radius: 12px; + text-align: center; + cursor: pointer; + transition: all 0.3s ease; + background: var(--bg-white); +} + +.role-option:hover { + border-color: var(--primary-color); + transform: translateY(-2px); + box-shadow: var(--shadow-md); +} + +.role-option.selected { + border-color: var(--primary-color); + background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%); + color: white; +} + +.role-option .icon { + font-size: 2.2rem; + margin-bottom: 8px; +} + +.role-option h3 { + font-size: 0.95rem; + font-weight: 600; +} + +/* فرم‌ها */ +.form-group { + margin-bottom: 18px; +} + +.form-group label { + display: block; + font-weight: 600; + margin-bottom: 8px; + color: var(--text-dark); + font-size: 0.9rem; +} + +.form-group input, +.form-group select, +.form-group textarea { + width: 100%; + padding: 12px 15px; + border: 2px solid var(--border-color); + border-radius: 10px; + font-size: 0.95rem; + transition: all 0.3s ease; + background: var(--bg-white); + font-family: 'Vazirmatn', sans-serif; +} + +.form-group input:focus, +.form-group select:focus, +.form-group textarea:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(56, 28, 128, 0.1); +} + +.form-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 15px; +} + +/* دکمه‌ها */ +.btn { + width: 100%; + padding: 13px 20px; + border: none; + border-radius: 10px; + font-size: 0.95rem; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + text-align: center; + text-decoration: none; + display: inline-block; + font-family: 'Vazirmatn', sans-serif; +} + +/* دکمه‌های جمع‌وجور که در وسط کانتینر قرار می‌گیرند (یک سوم/نیم عرض) */ +.btn--compact { + display: block; + width: 33%; + max-width: 360px; + min-width: 160px; + margin: 0.35rem auto; + padding: 10px 12px; + border-radius: 10px; +} + +.btn-primary { + background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%); + color: white; +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-lg); +} + +.btn-primary:active { + transform: translateY(0); +} + +.btn-secondary { + background: var(--bg-light); + color: var(--text-dark); + border: 2px solid var(--border-color); +} + +.btn-secondary:hover { + background: var(--border-color); +} + +.btn-success { + background: linear-gradient(135deg, #10b981 0%, #059669 100%); + color: white; +} + +.btn-success:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-lg); +} + +/* کارت‌ها */ +.card { + background: rgba(255, 255, 255, 0.98); + backdrop-filter: blur(10px); + border-radius: 16px; + padding: 22px; + margin-bottom: 18px; + border: 1px solid rgba(255, 255, 255, 0.4); + box-shadow: 0 8px 32px rgba(56, 28, 128, 0.12); + transition: all 0.3s ease; +} + +/* Header card tweaks: centered title and positioned logout button */ +.header-card { + padding-top: 14px; + padding-bottom: 14px; +} + +.header-card .logout-btn { + position: absolute; + right: 18px; /* put logout on the right like previous layout */ + top: 14px; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 8px 12px; + color: var(--text-dark); + background: var(--bg-light); + border: 1px solid var(--border-color); + border-radius: 10px; + text-decoration: none; + font-weight: 700; + transition: all 0.18s ease; +} + +.header-card .logout-btn:hover { + background: rgba(56, 28, 128, 0.04); + transform: translateY(-2px); +} + +/* two-column grid used in teacher page to align cards horizontally */ +.two-col-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 16px; +} + +@media (max-width: 900px) { + .two-col-grid { + grid-template-columns: 1fr; + } +} + +/* small inline button variant for header logout when needed */ +.btn--small { + display: inline-block; + width: auto; + padding: 8px 12px; +} + +/* Group results layout */ +#groupResultsList { + display: flex; + gap: 16px; + flex-wrap: wrap; +} + +.group-card { + flex: 1 1 calc(50% - 16px); + max-width: calc(50% - 16px); + box-sizing: border-box; + padding: 18px; + background: var(--bg-light); + border-radius: 12px; + border-right: 4px solid var(--primary-color); + display: flex; + flex-direction: column; + gap: 10px; +} + +.group-card-header { + color: var(--primary-color); + margin: 0; + font-size: 1.05rem; +} + +.group-reasoning { + color: var(--text-light); + font-size: 0.9rem; + margin: 0; +} + +.group-members-list { + list-style: none; + padding: 0; + margin: 0; + max-height: 220px; + overflow: auto; + display: flex; + flex-direction: column; + gap: 8px; +} + +.group-member-item { + padding: 10px; + background: white; + border-radius: 8px; + font-size: 0.9rem; +} + +@media (max-width: 900px) { + .group-card { + flex-basis: 100%; + max-width: 100%; + } +} + +/* cap the overall results area so it doesn't push the whole page too far; individual groups scroll internally */ +#groupResultsCard .card-body { + max-height: 520px; + overflow: auto; +} + +.card:hover { + box-shadow: 0 12px 40px rgba(56, 28, 128, 0.18); + transform: translateY(-3px); +} + +.card-header { + font-size: 1.15rem; + font-weight: 700; + color: var(--primary-color); + margin-bottom: 15px; + display: flex; + align-items: center; + gap: 8px; +} + +.card-body { + color: var(--text-dark); +} + +/* اطلاعات نمایشی */ +.info-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + gap: 12px; + margin-bottom: 15px; +} + +.info-display { + background: var(--bg-light); + padding: 12px 15px; + border-radius: 10px; + border-right: 3px solid var(--primary-color); +} + +.info-display .label { + font-weight: 500; + color: var(--text-light); + font-size: 0.8rem; + margin-bottom: 4px; +} + +.info-display .value { + font-size: 1.05rem; + color: var(--text-dark); + font-weight: 700; +} + +/* لینک‌های تست */ +.links-section { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; + margin-bottom: 15px; +} + +.external-link { + padding: 16px 12px; + background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%); + border: none; + border-radius: 12px; + text-align: center; + text-decoration: none; + color: white; + font-weight: 600; + transition: all 0.3s ease; + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + font-size: 0.9rem; + box-shadow: 0 4px 12px rgba(56, 28, 128, 0.2); +} + +.external-link:hover { + transform: translateY(-3px); + box-shadow: 0 8px 20px rgba(56, 28, 128, 0.3); + background: linear-gradient(135deg, var(--primary-dark) 0%, var(--primary-color) 100%); +} + +.external-link .icon { + font-size: 1.8rem; +} + +/* هشدارها */ +.alert { + padding: 12px 16px; + border-radius: 10px; + margin-bottom: 15px; + font-weight: 500; + font-size: 0.9rem; +} + +.alert-info { + background: rgba(56, 28, 128, 0.1); + color: var(--primary-color); + border: 1px solid rgba(56, 28, 128, 0.3); +} + +.alert-success { + background: rgba(16, 185, 129, 0.1); + color: var(--success-color); + border: 1px solid rgba(16, 185, 129, 0.3); +} + +.alert-warning { + background: rgba(245, 158, 11, 0.1); + color: var(--warning-color); + border: 1px solid rgba(245, 158, 11, 0.3); +} + +/* آمار */ +.stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 15px; + margin-bottom: 20px; +} + +.stat-card { + background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%); + color: white; + padding: 20px; + border-radius: 12px; + text-align: center; +} + +.stat-card .stat-value { + font-size: 2.2rem; + font-weight: 700; + margin-bottom: 5px; +} + +.stat-card .stat-label { + font-size: 0.9rem; + opacity: 0.95; +} + +/* لیست گروه */ +.group-list { + list-style: none; +} + +.group-member { + background: var(--bg-light); + padding: 12px 15px; + border-radius: 10px; + margin-bottom: 10px; + display: flex; + align-items: center; + gap: 12px; + transition: all 0.3s ease; +} + +.group-member:hover { + background: var(--border-color); + transform: translateX(5px); +} + +.group-member .member-icon { + font-size: 1.4rem; +} + +.group-member .member-info { + flex: 1; +} + +.group-member .member-name { + font-weight: 600; + color: var(--text-dark); + font-size: 0.95rem; + margin-bottom: 3px; +} + +.group-member .member-details { + font-size: 0.8rem; + color: var(--text-light); +} + +/* چک‌باکس‌ها */ +.checkbox-group { + max-height: 250px; + overflow-y: auto; + border: 2px solid var(--border-color); + border-radius: 10px; + padding: 8px; +} + +.checkbox-item { + padding: 10px; + display: flex; + align-items: center; + gap: 10px; + cursor: pointer; + border-radius: 8px; + transition: all 0.2s ease; +} + +.checkbox-item:hover { + background: var(--bg-light); +} + +.checkbox-item input[type="checkbox"] { + width: 18px; + height: 18px; + cursor: pointer; +} + +.checkbox-item label { + cursor: pointer; + margin: 0; + font-weight: 500; + font-size: 0.9rem; +} + +/* لینک بازگشت */ +.back-link { + display: inline-block; + margin-bottom: 15px; + color: var(--primary-color); + text-decoration: none; + font-weight: 600; + transition: all 0.3s ease; + font-size: 0.95rem; +} + +.back-link:hover { + transform: translateX(5px); +} + +/* حالت خالی */ +.empty-state { + text-align: center; + padding: 35px 20px; +} + +.empty-state .icon { + font-size: 3.5rem; + opacity: 0.3; + margin-bottom: 15px; +} + +.empty-state h3 { + font-size: 1.2rem; + color: var(--text-dark); + margin-bottom: 10px; +} + +.empty-state p { + color: var(--text-light); + font-size: 0.9rem; +} + +/* لودینگ */ +.loading { + display: inline-block; + width: 20px; + height: 20px; + border: 3px solid rgba(255, 255, 255, 0.3); + border-radius: 50%; + border-top-color: white; + animation: spin 1s ease-in-out infinite; +} + +/* طراحی ریسپانسیو */ +@media (max-width: 768px) { + .container { + padding: 25px; + } + + .header h1 { + font-size: 1.5rem; + } + + .role-selector { + grid-template-columns: 1fr; + } + + .links-section { + grid-template-columns: 1fr; + } + + .stats-grid { + grid-template-columns: 1fr; + } + + .form-row { + grid-template-columns: 1fr; + } + + .info-grid { + grid-template-columns: 1fr; + } +} + +@media (min-width: 769px) and (max-width: 1024px) { + .dashboard-container { + max-width: 900px; + } +} + +/* تنظیمات اسکرول */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: var(--bg-light); +} + +::-webkit-scrollbar-thumb { + background: var(--primary-color); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--primary-dark); +} + +/* ترانزیشن‌های نرم */ +a, button, input, select, .role-option, .card, .external-link, .checkbox-item { + transition: all 0.3s ease; +} diff --git a/backend/static/assets/js/data.js b/backend/static/assets/js/data.js new file mode 100644 index 0000000000000000000000000000000000000000..c2382272534ec015085a2cf14822bb433c278b44 --- /dev/null +++ b/backend/static/assets/js/data.js @@ -0,0 +1,244 @@ +// Student Grouping System - Data Management +// This file handles all data operations using Backend API + +// Backend API Configuration +// Auto-detects if running on Railway deployment or localhost +function getApiBaseUrl() { + // When deployed to Railway, it will use the same domain + // Railway serves both frontend and backend from the same URL + // So we use relative paths for API calls + if (window.location.hostname !== 'localhost' && window.location.hostname !== '127.0.0.1') { + // PRODUCTION: Running on Railway - use relative paths + return '/api'; + } + + // DEVELOPMENT: Running locally + return 'http://localhost:8000/api'; +} + +const API_BASE_URL = getApiBaseUrl(); + +// Utility function for API calls +async function apiCall(endpoint, options = {}) { + try { + const response = await fetch(`${API_BASE_URL}${endpoint}`, { + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.detail || 'API request failed'); + } + + return await response.json(); + } catch (error) { + console.error('API Error:', error); + throw error; + } +} + +// Get all data (for backward compatibility) +async function getData() { + const students = await getAllStudents(); + const status = await getGroupingStats(); + return { + students: students, + courseName: status.courseName, + groupingComplete: status.groupingComplete, + resultsVisible: status.resultsVisible, + teacherPassword: 'teacher123' + }; +} + +// Get student by student number +async function getStudent(studentNumber) { + try { + return await apiCall(`/student/${studentNumber}`); + } catch (error) { + return null; + } +} + +// Update student information +async function updateStudent(studentNumber, updates) { + try { + const result = await apiCall(`/student/${studentNumber}`, { + method: 'PUT', + body: JSON.stringify(updates) + }); + return result.success; + } catch (error) { + return false; + } +} + +// Get all students +async function getAllStudents() { + const result = await apiCall('/students'); + return result.students; +} + +// Check if a student number exists in the system +async function studentExists(studentNumber) { + const student = await getStudent(studentNumber); + return student !== null; +} + +// Get students with complete information (REQUIRED fields only: MBTI and Learning Style) +async function getStudentsWithCompleteInfo() { + const students = await getAllStudents(); + return students.filter(s => s.mbti && s.learningStyle); +} + +// Check if a student has complete profile (all fields filled) +async function hasCompleteProfile(studentNumber) { + const student = await getStudent(studentNumber); + if (!student) return false; + + return !!(student.mbti && student.learningStyle && + student.ams !== null && student.cooperative !== null); +} + +// Get completion percentage for a student +async function getProfileCompletion(studentNumber) { + const student = await getStudent(studentNumber); + if (!student) return 0; + + let completed = 0; + const total = 4; + + if (student.mbti) completed++; + if (student.learningStyle) completed++; + if (student.ams !== null) completed++; + if (student.cooperative !== null) completed++; + + return Math.round((completed / total) * 100); +} + +// Get grouping statistics +async function getGroupingStats() { + return await apiCall('/grouping/status'); +} + +// Check if grouping is complete +async function isGroupingComplete() { + const stats = await getGroupingStats(); + return stats.groupingComplete; +} + +// Get course name +async function getCourseName() { + const stats = await getGroupingStats(); + return stats.courseName; +} + +// Check teacher password +async function checkTeacherPassword(password) { + try { + const result = await apiCall('/auth/teacher', { + method: 'POST', + body: JSON.stringify({ password }) + }); + return result.valid; + } catch (error) { + return false; + } +} + +// Get student's group (only if results are visible) +async function getStudentGroup(studentNumber) { + try { + return await apiCall(`/student/${studentNumber}/group`); + } catch (error) { + if (error.message.includes('not yet visible')) { + return { error: 'results_not_visible', message: 'نتایج هنوز توسط معلم نمایش داده نشده است.' }; + } + if (error.message.includes('not assigned')) { + return { error: 'not_assigned', message: 'شما هنوز به گروهی اختصاص داده نشده‌اید.' }; + } + throw error; + } +} + +// Reset grouping only (keep student data) +async function resetGrouping(password) { + return await apiCall('/grouping/reset', { + method: 'POST', + body: JSON.stringify({ password }) + }); +} + +// Reset ALL data (student profiles + grouping) +async function resetAllData(password) { + return await apiCall('/data/reset-all', { + method: 'POST', + body: JSON.stringify({ password }) + }); +} + +// Toggle results visibility +async function toggleResultsVisibility(password) { + return await apiCall('/grouping/toggle-visibility', { + method: 'POST', + body: JSON.stringify({ password }) + }); +} + +// Legacy functions for backward compatibility (these are handled by backend now) +function saveData(data) { + console.warn('saveData is deprecated - data is automatically saved to backend'); +} + +function initializeData() { + console.log('Data is managed by backend'); + return true; +} + +function addNewStudent(studentNumber, name, grade = 15) { + console.warn('addNewStudent should be done through admin panel'); + return { success: false, message: 'Use backend API for adding students' }; +} + +function saveStudents(students) { + console.warn('saveStudents is deprecated - use backend API'); +} + +function deleteStudent(studentNumber) { + console.warn('deleteStudent should be done through admin panel'); + return false; +} + +function setGroupingComplete(status) { + console.warn('setGroupingComplete is handled by backend'); +} + +function setCourseName(name) { + console.warn('setCourseName is handled by backend'); +} + +function resetData() { + console.warn('resetData should be done through teacher dashboard'); +} + +// Export functions for use in other files +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + getData, + getStudent, + updateStudent, + getAllStudents, + studentExists, + getStudentsWithCompleteInfo, + hasCompleteProfile, + getProfileCompletion, + isGroupingComplete, + getCourseName, + checkTeacherPassword, + getGroupingStats, + getStudentGroup + }; +} diff --git a/backend/static/assets/js/grouping.js b/backend/static/assets/js/grouping.js new file mode 100644 index 0000000000000000000000000000000000000000..6d94520303c994b576facf2ddb59a40e3ade1c0b --- /dev/null +++ b/backend/static/assets/js/grouping.js @@ -0,0 +1,159 @@ +// Grouping Logic with Backend API Integration +// This file handles the student grouping process through the backend + +// API_BASE_URL is defined in data.js (loaded first) +// No need to redeclare it here to avoid "already been declared" error + +/** + * Main grouping function - calls backend API + * @param {string} courseName - The name of the course + * @returns {Promise} - Grouping results + */ +async function performGrouping(courseName) { + try { + console.log(`Requesting grouping from backend for course: ${courseName}`); + + const response = await fetch(`${API_BASE_URL}/grouping/perform`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + courseName + }) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.detail || 'Grouping failed'); + } + + const result = await response.json(); + console.log('Grouping successful!', result); + return result.results; + } catch (error) { + console.error('Grouping error:', error); + throw error; + } +} + +/** + * Get grouping statistics + * @returns {Promise} - Statistics about current grouping + */ +async function getGroupingStats() { + try { + const response = await fetch(`${API_BASE_URL}/grouping/status`); + if (!response.ok) { + throw new Error('Failed to get grouping status'); + } + return await response.json(); + } catch (error) { + console.error('Error getting grouping stats:', error); + throw error; + } +} + +/** + * Toggle visibility of results to students + * @param {string} password - Teacher password + * @returns {Promise} - New visibility status + */ +async function toggleResultsVisibility(password) { + try { + const response = await fetch(`${API_BASE_URL}/grouping/toggle-visibility`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ password }) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.detail || 'Failed to toggle visibility'); + } + + const result = await response.json(); + return result; + } catch (error) { + console.error('Error toggling visibility:', error); + throw error; + } +} + +/** + * Reset grouping (clear all group assignments) + * @param {string} password - Teacher password + */ +async function resetGrouping(password) { + try { + const response = await fetch(`${API_BASE_URL}/grouping/reset`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ password }) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.detail || 'Failed to reset grouping'); + } + + return await response.json(); + } catch (error) { + console.error('Error resetting grouping:', error); + throw error; + } +} + +/** + * Get student's group information + * @param {string} studentNumber - Student number + * @returns {Promise} - Group information + */ +async function getStudentGroup(studentNumber) { + try { + const response = await fetch(`${API_BASE_URL}/student/${studentNumber}/group`); + + if (!response.ok) { + const error = await response.json(); + if (error.detail.includes('not yet visible')) { + return { + error: 'not_visible', + message: 'نتایج هنوز توسط استاد نمایش داده نشده است.' + }; + } + if (error.detail.includes('not assigned')) { + return { + error: 'not_assigned', + message: 'شما هنوز به گروهی اختصاص داده نشده‌اید.' + }; + } + throw new Error(error.detail); + } + + return await response.json(); + } catch (error) { + console.error('Error getting student group:', error); + throw error; + } +} + +// Legacy function - now handled by backend +function applyGrouping(groupingResult, courseName) { + console.warn('applyGrouping is now handled automatically by the backend'); +} + +// Export functions +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + performGrouping, + getGroupingStats, + toggleResultsVisibility, + resetGrouping, + getStudentGroup, + applyGrouping + }; +} diff --git a/backend/static/index.html b/backend/static/index.html new file mode 100644 index 0000000000000000000000000000000000000000..903dd5808b2361d191525d64ccd61ea3cd42ab0c --- /dev/null +++ b/backend/static/index.html @@ -0,0 +1,13 @@ + + + + + + + TalimBot - سیستم گروه‌بندی هوشمند + + + +

در حال انتقال به صفحه ورود...

+ + diff --git a/backend/static/pages/ams-questionnaire.html b/backend/static/pages/ams-questionnaire.html new file mode 100644 index 0000000000000000000000000000000000000000..bb7fc73619b8ab8769a2e9daff631fd5723524e1 --- /dev/null +++ b/backend/static/pages/ams-questionnaire.html @@ -0,0 +1,436 @@ + + + + + + پرسشنامه انگیزش تحصیلی (AMS) - سیستم گروه‌بندی + + + + + + + + + + + + +
+ +
+

پرسشنامه انگیزش تحصیلی (AMS)

+

لطفاً برای هر جمله مشخص کنید که تا چه حد درباره شما درست است

+ + +
+
+
+

+ 0 از 28 سؤال پاسخ داده شده +

+
+ + +
+

مقیاس پاسخ‌دهی:

+
+
+
1
+
اصلاً درست نیست
+
+
+
2
+
خیلی کم درست است
+
+
+
3
+
کمی درست است
+
+
+
4
+
تا حدی درست است
+
+
+
5
+
نسبتاً درست است
+
+
+
6
+
خیلی درست است
+
+
+
7
+
کاملاً درست است
+
+
+
+ + +
+ +
+

+ + + + بی‌انگیزشی +

+
+
+ + +
+

+ + + + انگیزش بیرونی – تنظیم بیرونی +

+
+
+ + +
+

+ + + + انگیزش بیرونی – درون‌فکنی‌شده +

+
+
+ + +
+

+ + + + انگیزش بیرونی – همانندسازی‌شده +

+
+
+ + +
+

+ + + + انگیزش درونی برای کسب دانش +

+
+
+ + +
+

+ + + + انگیزش درونی برای کسب موفقیت +

+
+
+ + +
+

+ + + + انگیزش درونی برای تجربه تحریک/هیجان +

+
+
+ + +
+ +
+
+ + + +
+ + + + \ No newline at end of file diff --git a/backend/static/pages/cooperative-questionnaire.html b/backend/static/pages/cooperative-questionnaire.html new file mode 100644 index 0000000000000000000000000000000000000000..09093655ceeefd9bbb0e0811b94873c94cec0a51 --- /dev/null +++ b/backend/static/pages/cooperative-questionnaire.html @@ -0,0 +1,401 @@ + + + + + + پرسشنامه یادگیری مشارکتی - سیستم گروه‌بندی + + + + + + + + + + + + +
+ +
+

پرسشنامه یادگیری مشارکتی

+

بر اساس مدل جانسون و جانسون، ۱۹۹۴

+

لطفاً میزان موافقت خود را با هر جمله مشخص کنید

+ + +
+
+
+

+ 0 از 25 سؤال پاسخ داده شده +

+
+ + +
+

مقیاس پاسخ‌دهی:

+
+
+
1
+
کاملاً مخالفم
+
+
+
2
+
مخالفم
+
+
+
3
+
نظری ندارم
+
+
+
4
+
موافقم
+
+
+
5
+
کاملاً موافقم
+
+
+
+ + +
+ +
+

+ + + + بخش اول: وابستگی متقابل مثبت +

+
+
+ + +
+

+ + + + بخش دوم: پاسخگویی فردی +

+
+
+ + +
+

+ + + + بخش سوم: تعامل چهره‌به‌چهره / ارتقای متقابل +

+
+
+ + +
+

+ + + + بخش چهارم: مهارت‌های اجتماعی +

+
+
+ + +
+

+ + + + بخش پنجم: پردازش گروهی / بازاندیشی در عملکرد گروه +

+
+
+ + +
+ +
+
+ + + +
+ + + + \ No newline at end of file diff --git a/backend/static/pages/group-view.html b/backend/static/pages/group-view.html new file mode 100644 index 0000000000000000000000000000000000000000..9512ab4d458e7f74b491f84043f4b3cb382f4f0c --- /dev/null +++ b/backend/static/pages/group-view.html @@ -0,0 +1,405 @@ + + + + + + گروه من - سیستم گروه‌بندی + + + + + + + + + + + + +
+ +
+ + + +
+

گروه مطالعاتی شما

+

مشاهده اعضای گروه و اطلاعات همکاران

+
+
+ + + + + + + + + + + + + +
+ + +
+
+
+

© ۲۰۲۵ TalimBot - سیستم گروه‌بندی هوشمند دانش آموزان

+
+
+
+ + + + + + + diff --git a/backend/static/pages/login.html b/backend/static/pages/login.html new file mode 100644 index 0000000000000000000000000000000000000000..e867b513d3d27280945e601c69e38b76856fbd94 --- /dev/null +++ b/backend/static/pages/login.html @@ -0,0 +1,274 @@ + + + + + + ورود - سیستم گروه‌بندی دانش آموزان + + + + + + + + + +
+ +
+
+ +
+
+ TalimBot Logo +

سیستم گروه‌بندی هوشمند

+

مدیریت و گروه‌بندی دانش آموزان بر اساس شخصیت و سبک یادگیری

+
+
+

+ TalimBot +

+
+
+ + +
+
+ +
+

سیستم گروه‌بندی دانش آموزان

+

لطفاً نقش خود را انتخاب کنید

+
+ + + +
+ + +
+ + +
+ + + + + + + + +
+ + + + + +
+

نسخه 1.0.0 | آبان 1404

+
+
+
+
+ + + + + diff --git a/backend/static/pages/student-dashboard.html b/backend/static/pages/student-dashboard.html new file mode 100644 index 0000000000000000000000000000000000000000..3065f30b58df9fbb8b7b0080fcec55a6a842623a --- /dev/null +++ b/backend/static/pages/student-dashboard.html @@ -0,0 +1,580 @@ + + + + + + + + + + داشبورد دانش آموز - سیستم گروه‌بندی + + + + + + + + + + + + +
+ +
+

+

لطفاً تمام تست‌های شخصیتی خود را تکمیل کنید و اطلاعات را ذخیره نمایید.

+
+

💡 چرا این اطلاعات مهم است؟ سیستم گروه‌بندی هوشمند از روش‌های علمی مانند ZPD، تکمیل شخصیتی MBTI، و تنوع سبک یادگیری VARK برای ساخت بهترین تیم‌ها استفاده می‌کند. هر چه اطلاعات کامل‌تری وارد کنید، گروه‌بندی دقیق‌تری خواهید داشت!

+
+
+ + +
+
+
+
+

شماره دانش‌آموزی

+

-

+
+
+ + + +
+
+
+ +
+
+
+

تست‌های تکمیل شده

+

0 / 4

+
+
+ + + +
+
+
+ +
+
+
+

معدل

+

-

+
+
+ + + +
+
+
+
+ + +
+ +
+
+
+ + + +
+
+

MBTI - تیپ شخصیتی

+

شناخت تیپ شخصیتی برای ایجاد تعادل در گروه و درک نقاط قوت شما

+
+
+
+ +
+ + +
+
+
+ + +
+
+
+ + + + +
+
+

VARK - سبک یادگیری

+

شناسایی سبک یادگیری شما (دیداری، شنیداری، خواندن/نوشتن، حرکتی)

+
+
+
+ +
+ + +
+
+
+ + +
+
+
+ + + +
+
+

AMS - انگیزش تحصیلی

+

سنجش انگیزه درونی و بیرونی برای یادگیری و پیشرفت تحصیلی

+
+
+
+ +
+ + +
+
+
+ + +
+
+
+ + + +
+
+

توانایی همکاری

+

سنجش مهارت‌های کار گروهی، تعامل و همکاری با دیگران

+
+
+
+ +
+ + +
+
+
+
+ + +
+
+

+ + + + ترجیحات همگروهی +

+

می‌توانید حداکثر 4 نفر از همکلاسی‌های خود را که دوست دارید در یک گروه باشید انتخاب کنید (اختیاری)

+
+
+ +
+
+

انتخاب شده: 0/4

+
+
+ + +
+ + +
+
+ + + + + +
+
+
+

نسخه 1.0.0 | آبان 1404

+

© ۲۰۲۵ TalimBot - سیستم گروه‌بندی هوشمند دانش آموزان

+
+
+
+ + + + + diff --git a/backend/static/pages/student-data.html b/backend/static/pages/student-data.html new file mode 100644 index 0000000000000000000000000000000000000000..e69f90edf49e85ea49ab49029fc8645c3bf72245 --- /dev/null +++ b/backend/static/pages/student-data.html @@ -0,0 +1,388 @@ + + + + + + ورود اطلاعات دانش‌آموزان - TalimBot + + + + + + + + + + + +
+ +
+

+ + + + ورود اطلاعات دانش‌آموزان +

+

اطلاعات دانش‌آموزان را وارد کنید تا در سیستم گروه‌بندی ثبت شود

+
+ + +
+
+ + + +
+

راهنما

+

نام، شماره دانش‌آموزی و نمره را وارد کنید. پس از هر ورودی روی "ذخیره" کلیک کنید تا فرم خالی شود و آماده ورود دانش‌آموز بعدی باشد.

+
+
+
+ + +
+
+
+
+

تعداد دانش‌آموزان ثبت شده

+

0

+
+
+ + + +
+
+
+ +
+
+
+

آخرین دانش‌آموز ثبت شده

+

-

+
+
+ + + +
+
+
+
+ + +
+
+

+ + + + افزودن دانش‌آموز +

+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+
+
+ + +
+
+

+ + + + لیست دانش‌آموزان ذخیره شده +

+
+ +
+ + + + + + + + + + + + + + +
ردیفشماره دانش‌آموزنام دانش‌آموزنمره (از 20)
+ + + +

هنوز دانش‌آموزی ثبت نشده است

+

از فرم بالا برای افزودن دانش‌آموز استفاده کنید

+
+
+
+ + + + + +
+
+
+

نسخه 1.0.0 | آبان 1404

+

© ۲۰۲۵ TalimBot - سیستم گروه‌بندی هوشمند دانش آموزان

+
+
+
+
+ + + + + + + diff --git a/backend/static/pages/teacher-dashboard.html b/backend/static/pages/teacher-dashboard.html new file mode 100644 index 0000000000000000000000000000000000000000..4025d009e959b9a964cce03f3e3a9d41d373fbfc --- /dev/null +++ b/backend/static/pages/teacher-dashboard.html @@ -0,0 +1,1017 @@ + + + + + + + + + + داشبورد معلم - سیستم گروه‌بندی + + + + + + + + +
+ + + + +
+
+

+ + + + + پنل مدیریت معلم +

+

مدیریت گروه‌بندی دانش آموزان

+
+
+ +
+ +
+
+
+
+

تعداد کل دانش آموزان

+

30

+
+
+ + + +
+
+
+ +
+
+
+

پروفایل‌های تکمیل شده

+

0

+
+
+ + + +
+
+
+ +
+
+
+

دانش آموزان گروه‌بندی شده

+

0

+
+
+ + + +
+
+
+
+ + +
+ +
+

+ + + + اطلاعات درس +

+
+
+ + +
+
+ +
+
+
+ + +
+

+ + + + وضعیت گروه‌بندی +

+
+ +
+
+
+ + +
+

+ ⚙️ + عملیات گروه‌بندی +

+
+
+

+ + + + آماده گروه‌بندی +

+

+ 0 + دانش آموز آماده گروه‌بندی هستند. +

+
+

🎯 معیارهای گروه‌بندی علمی (سنین 15-16 سال، به ترتیب اولویت):

+
+

1. بهینه‌سازی ZPD ناحیه رشد نزدیک (30%): ترکیب نمرات بالا با متوسط برای یادگیری همیاری و آموزش همتا-به-همتا

+

2. تکمیل MBTI (25%): ترکیب شخصیت‌های مکمل (نه مشابه) - مثال: ENFP+INTJ، ENTP+INFJ - تعادل درون‌گرا/برون‌گرا

+

3. تنوع VARK (20%): ترکیب سبک‌های یادگیری دیداری، شنیداری، خواندن/نوشتن و حرکتی در هر گروه

+

4. انگیزش تحصیلی - AMS (15%): توزیع دانش‌آموزان با انگیزه بالا (>140) در گروه‌ها برای الهام‌بخشی

+

5. مهارت همکاری (10%): دانش‌آموزان با مهارت همکاری بالا (>88) به عنوان تسهیل‌گر اجتماعی

+

6. ویژگی‌های درس: تطبیق با نیازهای درس (ریاضی: تفکر منطقی، ادبیات: احساسی)

+

7. ترجیحات دانش‌آموزان (5%): فقط اگر با معیارهای بالا تضاد نداشته باشد - نوجوانان از کار با افراد جدید رشد می‌کنند

+
+
+
+

+ ⚡ نکته مهم: فقط دانش‌آموزانی که MBTI و سبک یادگیری خود را تکمیل کرده‌اند در گروه‌بندی شرکت می‌کنند. + اگر دانش‌آموزی دوستان خود را انتخاب کرده ولی آن دوستان فرم را تکمیل نکرده‌اند، این انتخاب در نظر گرفته نمی‌شود. +

+
+
+ +
+ + + + +
+ + + + +
+
+ + +
+

+ 📋 + لیست تمام دانش آموزان +

+ +
+ + +
+ +
+ + +
+
+
+ + + +
+
+

بازنشانی داده‌ها

+

+ این عملیات تمام اطلاعات سیستم را به حالت اولیه بازمی‌گرداند. همه 30 دانش‌آموز (S001 تا S030) بازگردانده می‌شوند، + تمام تغییرات و گروه‌بندی‌ها حذف می‌شوند و سیستم به حالت پیش‌فرض برمی‌گردد. +

+ +
+
+
+ + +
+
+
+ + + +
+
+

پر کردن داده‌های تست

+

+ برای تست سیستم گروه‌بندی، 10 دانش‌آموز اول (S001 تا S010) را با داده‌های کامل و تصادفی پر کنید. + این شامل MBTI، VARK، نمرات AMS و Cooperative و ترجیحات دانش‌آموزی می‌شود. +

+
+ + +
+
+
+
+ + +
+
+
+ + + +
+
+

وارد کردن داده‌ها از JSON

+

+ یک فایل JSON حاوی اطلاعات دانش‌آموزان را انتخاب کنید. فایل باید شامل آرایه‌ای از دانش‌آموزان با فیلدهای + mbti، learningStyle، ams، cooperative و preferredStudents باشد. +

+
+ + + + +
+
+
+
+ + + + + +
+
+
+

نسخه 1.0.0 | آبان 1404

+

© ۲۰۲۵ TalimBot - سیستم گروه‌بندی هوشمند دانش آموزان

+
+
+
+
+
+ + + + + + + + diff --git a/render.yaml b/render.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4cd0aa17f74410eb9103a646596855b085321737 --- /dev/null +++ b/render.yaml @@ -0,0 +1,7 @@ +# Render.com Deployment Configuration + +## Build Command: +pip install -r backend/requirements.txt + +## Start Command: +cd backend && uvicorn main:app --host 0.0.0.0 --port $PORT diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..0549d5212e0b5d34cb0cd50a46451a3a4225070b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +fastapi==0.104.1 +uvicorn==0.24.0 +pydantic==2.5.0 +requests==2.31.0 +python-multipart==0.0.6 +python-dotenv==1.0.0 diff --git a/resources_references/DEPLOYMENT_CHECKLIST.md b/resources_references/DEPLOYMENT_CHECKLIST.md new file mode 100644 index 0000000000000000000000000000000000000000..beb2d3904ada7251206b2a4124d632927f395f98 --- /dev/null +++ b/resources_references/DEPLOYMENT_CHECKLIST.md @@ -0,0 +1,207 @@ +# ✅ Railway Deployment Checklist + +## Pre-Deployment Verification + +### ✅ Local Testing Complete +- [x] Project restructured with `backend/static/` folder +- [x] All frontend files copied to `backend/static/` +- [x] `main.py` updated to serve static files +- [x] `data.js` updated for Railway deployment +- [x] Local server tested and working (`http://localhost:8000`) +- [x] All pages accessible (index, login, dashboards, questionnaires) +- [x] API endpoints working (grouping, data saving) + +### ✅ Git Repository Status +- [x] `.env` file in `.gitignore` (API key NOT committed) +- [x] All changes committed to Git +- [x] Changes pushed to GitHub main branch +- [x] Repository: `https://github.com/talimbot/talimbot` + +--- + +## 🚂 Railway Deployment Steps + +### Step 1: Create Railway Account +- [ ] Go to https://railway.app +- [ ] Click "Start a New Project" +- [ ] Sign in with GitHub account +- [ ] Authorize Railway to access your repositories + +### Step 2: Deploy from GitHub +- [ ] Click "Deploy from GitHub repo" +- [ ] Select `talimbot/talimbot` repository +- [ ] Railway auto-detects Python project +- [ ] Deployment starts automatically + +### Step 3: Configure Environment Variables +- [ ] Go to project dashboard +- [ ] Click "Variables" tab +- [ ] Click "+ New Variable" +- [ ] Add variable: + - **Key**: `OPENROUTER_API_KEY` + - **Value**: `sk-or-v1-your-actual-key-here` +- [ ] Click "Add" + +### Step 4: Generate Public URL +- [ ] Go to "Settings" tab +- [ ] Scroll to "Networking" section +- [ ] Click "Generate Domain" +- [ ] Copy your URL: `https://talimbot-production-xxxx.up.railway.app` + +### Step 5: Wait for Deployment +- [ ] Monitor deployment in "Deployments" tab +- [ ] Wait 2-3 minutes for build to complete +- [ ] Look for "✅ Deployment successful" message + +--- + +## 🧪 Post-Deployment Testing + +### Test All Features +Visit your Railway URL and verify: + +#### Frontend Pages +- [ ] Main page loads (`/`) +- [ ] Login page works (`/pages/login.html`) +- [ ] Student dashboard loads and functions +- [ ] Teacher dashboard loads and functions +- [ ] AMS questionnaire page works +- [ ] Cooperative questionnaire page works +- [ ] Group view page displays correctly + +#### Authentication & Navigation +- [ ] Can login as student (use student number from data) +- [ ] Can login as teacher (password: `teacher123`) +- [ ] Navigation between pages works +- [ ] Back buttons work correctly +- [ ] Logout functionality works + +#### Student Features +- [ ] MBTI test can be filled and saved +- [ ] VARK test can be filled and saved +- [ ] AMS questionnaire completes and auto-saves score +- [ ] Cooperative questionnaire completes and auto-saves score +- [ ] Preferred students selection works (max 4) +- [ ] "Save All Data" button saves successfully +- [ ] Data persists after page refresh +- [ ] Can view assigned group + +#### Teacher Features +- [ ] Can view all students in table +- [ ] Can edit student information +- [ ] Can add new students +- [ ] Can delete students +- [ ] Download data button works +- [ ] Toggle visibility for students works +- [ ] AI Grouping functionality works + - [ ] Can select course (Riazi, Faravari, etc.) + - [ ] Grouping completes successfully + - [ ] Groups are created and saved + - [ ] Students can see their groups +- [ ] AMS and Cooperation columns display correctly + +#### API & Data Persistence +- [ ] API calls complete without errors (check browser console) +- [ ] Student data saves to backend +- [ ] Data persists between sessions +- [ ] Groups persist after creation +- [ ] No 404 errors for API endpoints +- [ ] No CORS errors in console + +--- + +## 🔍 Troubleshooting Guide + +### If pages don't load: +1. Check Railway deployment logs for errors +2. Verify `backend/static/` folder contains all files +3. Check browser console for 404 errors + +### If API calls fail: +1. Verify environment variable `OPENROUTER_API_KEY` is set +2. Check Railway logs for API errors +3. Test API endpoints directly: `https://your-url.railway.app/api/students` + +### If grouping doesn't work: +1. Verify OpenRouter API key is correct +2. Check you have credits in OpenRouter account +3. View Railway logs during grouping attempt +4. Ensure request is reaching `/api/grouping` endpoint + +### If static files are missing: +1. Verify files exist in `backend/static/` folder locally +2. Ensure git committed and pushed all files +3. Trigger manual redeploy in Railway +4. Check file paths in HTML are correct (relative paths) + +--- + +## 📊 Monitoring + +### Check Deployment Status +- [ ] Railway dashboard shows "Active" status +- [ ] Latest deployment timestamp is recent +- [ ] No error messages in deployment logs + +### Monitor Usage +- [ ] View usage in Railway "Usage" tab +- [ ] Verify you're within free tier ($5/month) +- [ ] Estimated usage: ~$2-3/month + +### View Logs +- [ ] Real-time logs available in "Deployments" tab +- [ ] Check logs for any errors or warnings +- [ ] Monitor API request logs + +--- + +## 🎯 Final Verification + +### Complete Success Criteria +- [ ] Application is accessible from any device with internet +- [ ] All features work identically to localhost version +- [ ] No errors in browser console +- [ ] No errors in Railway logs +- [ ] Data persistence works correctly +- [ ] AI grouping creates groups successfully +- [ ] Teachers can manage students +- [ ] Students can complete questionnaires + +### Share with Users +- [ ] Copy Railway URL +- [ ] Share URL with teacher +- [ ] Provide login instructions: + - Teacher: Password is `teacher123` + - Students: Use their student number +- [ ] Explain how to access from any device + +--- + +## 🚀 You're Done! + +Once all checkboxes are checked, your TalimBot is: +- ✅ **Fully deployed** on Railway +- ✅ **Accessible from anywhere** via the internet +- ✅ **Secure** (API key in environment variables) +- ✅ **Free** (within Railway's $5/month credit) +- ✅ **Professional** (real production website) + +**Your Live URL**: `https://your-project.up.railway.app` + +--- + +## 📝 Next Steps + +1. **Bookmark your Railway dashboard** for monitoring +2. **Keep your OpenRouter API key safe** (never share it) +3. **Monitor Railway usage** to stay within free tier +4. **Test from multiple devices** (phone, tablet, different computers) +5. **Collect feedback** from teachers and students + +--- + +## 🆘 Need Help? + +- **Railway Docs**: https://docs.railway.app +- **Railway Discord**: https://discord.gg/railway +- **Review**: `RAILWAY_SETUP_GUIDE.md` for detailed instructions diff --git a/resources_references/Icons/Additional/teacherIcon.png b/resources_references/Icons/Additional/teacherIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..11ea63b048234b9661ca53b6dcd970607a768d2e Binary files /dev/null and b/resources_references/Icons/Additional/teacherIcon.png differ diff --git a/resources_references/Icons/Additional/teacherIcon2.jpg b/resources_references/Icons/Additional/teacherIcon2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..00ba55f45c28c016de9dd78f3a9b0c622fdb2c99 Binary files /dev/null and b/resources_references/Icons/Additional/teacherIcon2.jpg differ diff --git a/resources_references/Icons/logo/Logo_blueBackground.jpg b/resources_references/Icons/logo/Logo_blueBackground.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e8c775a86dbb733ff780963bb10ea73e203966b6 Binary files /dev/null and b/resources_references/Icons/logo/Logo_blueBackground.jpg differ diff --git a/resources_references/Icons/logo/Logo_noBackground.jpg b/resources_references/Icons/logo/Logo_noBackground.jpg new file mode 100644 index 0000000000000000000000000000000000000000..317d414f287b697d891103c98647a6c043f1c671 Binary files /dev/null and b/resources_references/Icons/logo/Logo_noBackground.jpg differ diff --git a/resources_references/Icons/logo/logo.png b/resources_references/Icons/logo/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d9dcbecafe8f1fa3138980865b9d392967f6940d Binary files /dev/null and b/resources_references/Icons/logo/logo.png differ diff --git a/resources_references/Icons/studentIcon.png b/resources_references/Icons/studentIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..1b3b4f96bca1bfada5b15c165a4469773a630aab Binary files /dev/null and b/resources_references/Icons/studentIcon.png differ diff --git a/resources_references/Icons/teacherIcon3.png b/resources_references/Icons/teacherIcon3.png new file mode 100644 index 0000000000000000000000000000000000000000..d19f6da898b38cb3391cb2705d7c09fdfdf44bf3 Binary files /dev/null and b/resources_references/Icons/teacherIcon3.png differ diff --git a/resources_references/RAILWAY_DEPLOYMENT.md b/resources_references/RAILWAY_DEPLOYMENT.md new file mode 100644 index 0000000000000000000000000000000000000000..907f84e1f3eca544fded6bc6dcbe703d7a643dba --- /dev/null +++ b/resources_references/RAILWAY_DEPLOYMENT.md @@ -0,0 +1,126 @@ +# 🚂 Railway Deployment Guide for TalimBot + +## Quick Deploy to Railway.app + +### Step 1: Create Railway Account +1. Go to: **https://railway.app** +2. Click **"Login"** → **"Login with GitHub"** +3. Authorize Railway to access your repositories + +### Step 2: Deploy from GitHub +1. Click **"New Project"** +2. Select **"Deploy from GitHub repo"** +3. Choose your **"talimbot"** repository +4. Railway will automatically detect it's a Python project! + +### Step 3: Add Environment Variable (IMPORTANT!) +1. In your Railway project, click on your service +2. Go to **"Variables"** tab +3. Click **"New Variable"** +4. Add: + - **Name:** `OPENROUTER_API_KEY` + - **Value:** `sk-or-v1-your-actual-key-here` (get from https://openrouter.ai) +5. Click **"Add"** + +### Step 4: Configure Build (if needed) +Railway usually auto-detects, but if it doesn't: +1. Go to **"Settings"** tab +2. **Build Command:** `pip install -r backend/requirements.txt` +3. **Start Command:** `cd backend && uvicorn main:app --host 0.0.0.0 --port $PORT` + +### Step 5: Deploy! +1. Railway deploys automatically +2. Wait 2-3 minutes for build to complete +3. You'll see "Active" when ready + +### Step 6: Get Your URL +1. Click **"Settings"** tab +2. Scroll to **"Networking"** +3. Click **"Generate Domain"** +4. You'll get: `https://talimbot-production-xxxx.up.railway.app` +5. **Copy this URL!** + +### Step 7: Update Frontend +Edit `assets/js/data.js` line 11: +```javascript +return 'https://talimbot-production-xxxx.up.railway.app/api'; +``` + +### Step 8: Push to GitHub +```bash +git add assets/js/data.js +git commit -m "Configure Railway backend URL" +git push origin main +``` + +### Step 9: Test! +Visit: `https://talimbot.github.io/talimbot/` + +--- + +## Environment Variables Explained + +### Local Development (.env file) +``` +OPENROUTER_API_KEY=sk-or-v1-your-key-here +``` + +### Railway (Variables tab in dashboard) +- **Variable Name:** OPENROUTER_API_KEY +- **Variable Value:** sk-or-v1-your-key-here + +**Important:** NEVER commit your .env file to GitHub! It's already in .gitignore. + +--- + +## Cost + +Railway offers: +- **$5 free credit per month** (renews monthly) +- **$0.000463 per GB-hour** of RAM +- Usually enough for small projects like this +- Can upgrade to Developer plan ($5/month) for more resources + +--- + +## Advantages over Render + +✅ **No sleep** - stays online 24/7 +✅ **Faster deployments** - usually 1-2 minutes +✅ **Better free tier** - $5 credit (vs Render's sleep after 15min) +✅ **Auto-deploy** from GitHub on push +✅ **Easy environment variables** - simple UI + +--- + +## Troubleshooting + +### Build Fails +- Check Logs tab for error messages +- Verify `requirements.txt` has all dependencies +- Make sure `python-dotenv` is included + +### "API key not configured" Error +- Go to Variables tab +- Verify OPENROUTER_API_KEY is set +- Make sure there are no extra spaces +- Redeploy after adding variable + +### Can't Access Backend +- Check service is "Active" (not deploying/failed) +- Verify you generated a domain in Settings → Networking +- Try accessing: `https://your-url.up.railway.app/api/students` + +--- + +## Next Steps + +After deployment: +1. Test login from multiple devices +2. Test grouping functionality +3. Download JSON backup +4. Share link with students! + +--- + +**Your website is now live and independent!** 🎉 diff --git a/resources_references/RAILWAY_SETUP_GUIDE.md b/resources_references/RAILWAY_SETUP_GUIDE.md new file mode 100644 index 0000000000000000000000000000000000000000..a9b8749df31da9d24bd80f17185ec70108a137f0 --- /dev/null +++ b/resources_references/RAILWAY_SETUP_GUIDE.md @@ -0,0 +1,300 @@ +# 🚂 Railway Deployment Guide for TalimBot + +## 📋 Overview +This guide will help you deploy your complete TalimBot application (Frontend + Backend) to Railway for free, making it accessible from anywhere. + +## ✅ What We've Done (Preparation Complete!) + +### 1. **Restructured the Project** +- ✅ Created `backend/static/` folder +- ✅ Moved all frontend files (HTML, CSS, JS, Icons) to `backend/static/` +- ✅ Updated `main.py` to serve static files +- ✅ Updated `data.js` to use relative API paths for Railway + +### 2. **Updated FastAPI Configuration** +Your `backend/main.py` now: +- Serves static files from `backend/static/` folder +- Uses environment variable for OpenRouter API key +- Handles both frontend (HTML pages) and backend (API endpoints) from the same server +- Works seamlessly on Railway's deployment platform + +### 3. **Project Structure** +``` +talimbot/ +├── .env # LOCAL ONLY - Contains your API key (NEVER commit this!) +├── .env.example # Template for environment variables +├── .gitignore # Ensures .env is not committed +├── Procfile # Tells Railway how to start the server +├── runtime.txt # Specifies Python version +├── README.md +├── RAILWAY_DEPLOYMENT.md +├── backend/ +│ ├── main.py # ✅ UPDATED - Serves static files + API endpoints +│ ├── grouping_logic.py +│ ├── requirements.txt +│ ├── data/ +│ │ └── students.json +│ └── static/ # ✅ NEW - All frontend files +│ ├── index.html +│ ├── assets/ # CSS and JS files +│ │ ├── css/ +│ │ │ └── styles.css +│ │ └── js/ +│ │ ├── data.js # ✅ UPDATED - Uses relative API paths +│ │ └── grouping.js +│ ├── pages/ # All HTML pages +│ │ ├── login.html +│ │ ├── student-dashboard.html +│ │ ├── teacher-dashboard.html +│ │ ├── ams-questionnaire.html +│ │ ├── cooperative-questionnaire.html +│ │ └── group-view.html +│ └── Icons/ # Logo and icons +``` + +--- + +## 🚀 Deployment Steps + +### **Step 1: Verify Local Setup** + +1. **Create `.env` file** (if you haven't already): + ```bash + # In the project root (talimbot/) folder + echo OPENROUTER_API_KEY=sk-or-v1-your-actual-key-here > .env + ``` + +2. **Test locally**: + ```bash + cd backend + python main.py + ``` + +3. **Open browser** to `http://localhost:8000` + - You should see the index.html page + - All pages should work (login, dashboards, questionnaires) + - API calls should work (grouping, data saving) + +--- + +### **Step 2: Commit Changes to GitHub** + +⚠️ **IMPORTANT**: Make sure `.env` is in `.gitignore` (it already is!) + +```bash +# From the talimbot/ directory +git add . +git status # Verify .env is NOT listed (should only see modified files) + +git commit -m "Restructure project for Railway deployment - serve frontend from backend" +git push origin main +``` + +--- + +### **Step 3: Deploy to Railway** + +#### A. **Sign Up / Log In** +1. Go to [railway.app](https://railway.app) +2. Click **"Start a New Project"** +3. Sign in with your GitHub account + +#### B. **Create New Project** +1. Click **"Deploy from GitHub repo"** +2. Select your `talimbot` repository +3. Railway will automatically detect it's a Python project + +#### C. **Configure Environment Variables** +1. In the Railway dashboard, go to your project +2. Click on the **"Variables"** tab +3. Click **"+ New Variable"** +4. Add: + - **Key**: `OPENROUTER_API_KEY` + - **Value**: `sk-or-v1-your-actual-openrouter-api-key` +5. Click **"Add"** + +#### D. **Verify Deployment Settings** +Railway auto-detects settings from your files: +- ✅ **Build Command**: None needed (Python dependencies auto-installed) +- ✅ **Start Command**: From `Procfile` → `cd backend && uvicorn main:app --host 0.0.0.0 --port $PORT` +- ✅ **Python Version**: From `runtime.txt` → `python-3.11.0` + +#### E. **Deploy!** +1. Click **"Deploy"** +2. Wait 2-3 minutes for deployment +3. Railway will show deployment logs +4. When complete, you'll see: ✅ **"Deployment successful"** + +#### F. **Get Your URL** +1. In Railway dashboard, click **"Settings"** tab +2. Scroll to **"Networking"** section +3. Click **"Generate Domain"** +4. Copy your URL (e.g., `https://talimbot-production-abc123.up.railway.app`) + +--- + +### **Step 4: Test Your Deployed Application** + +1. **Open your Railway URL** in a browser +2. **Test all features**: + - ✅ Main page loads (`index.html`) + - ✅ Login page works (`/pages/login.html`) + - ✅ Student dashboard loads + - ✅ Teacher dashboard loads + - ✅ Questionnaires work (AMS, Cooperative) + - ✅ Grouping functionality works + - ✅ Data saves correctly + +--- + +## 🔧 How It Works + +### **Single Server Architecture** +Railway runs ONE server that handles BOTH: + +1. **Frontend (Static Files)**: + - `GET /` → Serves `index.html` + - `GET /pages/login.html` → Serves login page + - `GET /assets/css/styles.css` → Serves CSS + - `GET /assets/js/data.js` → Serves JavaScript + +2. **Backend (API Endpoints)**: + - `POST /api/grouping` → AI grouping logic + - `GET /api/students` → Get all students + - `PUT /api/students/{id}` → Update student + - All other API routes in `main.py` + +### **How Requests Are Routed** + +``` +User Browser → Railway URL + ↓ + FastAPI Server (main.py) + ↓ + ┌─────────┴─────────┐ + ↓ ↓ + /api/* Everything else +(API Endpoints) (Static Files) + ↓ ↓ +grouping_logic.py backend/static/ +OpenRouter API (HTML/CSS/JS) +``` + +### **Environment Variable Flow** + +``` +Local Development: +.env file → load_dotenv() → os.getenv("OPENROUTER_API_KEY") + +Railway Production: +Railway Variables → os.getenv("OPENROUTER_API_KEY") +``` + +--- + +## 📊 Monitoring & Management + +### **View Logs** +1. Go to Railway dashboard +2. Click on your project +3. Click **"Deployments"** tab +4. Click on the latest deployment +5. View real-time logs + +### **Check Usage** +- Railway free tier: **$5 credit/month** +- Your app should use: **~$2-3/month** +- Monitor usage in **"Usage"** tab + +### **Redeploy (After Code Changes)** +1. Make changes locally +2. Test locally (`python main.py`) +3. Commit and push to GitHub: + ```bash + git add . + git commit -m "Your changes" + git push origin main + ``` +4. Railway **auto-deploys** within 1-2 minutes! + +--- + +## 🆘 Troubleshooting + +### **Problem: API calls fail (404 errors)** +**Solution**: API routes must start with `/api/` +- ✅ Correct: `POST /api/grouping` +- ❌ Wrong: `POST /grouping` + +### **Problem: Static files not loading (CSS/JS missing)** +**Solution**: +1. Verify files are in `backend/static/` folder +2. Check browser console for 404 errors +3. Ensure paths in HTML are relative (e.g., `/assets/css/styles.css`) + +### **Problem: OpenRouter API errors** +**Solution**: +1. Verify API key is correct in Railway Variables +2. Check you have credits in your OpenRouter account +3. View logs in Railway to see exact error message + +### **Problem: Server won't start** +**Solution**: +1. Check Railway logs for error messages +2. Verify `requirements.txt` has all dependencies +3. Ensure `Procfile` command is correct + +--- + +## 🎯 Success Checklist + +After deployment, verify: + +- [ ] Railway URL loads the main page +- [ ] All navigation links work +- [ ] Login system works (student/teacher) +- [ ] Student dashboard loads +- [ ] Teacher dashboard loads +- [ ] AMS questionnaire works and saves +- [ ] Cooperative questionnaire works and saves +- [ ] AI grouping creates groups successfully +- [ ] Student data persists after refresh +- [ ] API calls complete without errors +- [ ] No console errors in browser DevTools + +--- + +## 💡 Next Steps + +Once deployed successfully: + +1. **Share the Railway URL** with your teacher +2. **Test from different devices** (phone, tablet) +3. **Monitor Railway dashboard** for any errors +4. **Keep your OpenRouter API key secure** +5. **Consider upgrading Railway plan** if you exceed free tier + +--- + +## 📞 Support Resources + +- **Railway Docs**: https://docs.railway.app +- **OpenRouter Docs**: https://openrouter.ai/docs +- **FastAPI Docs**: https://fastapi.tiangolo.com +- **Your Deployment Guide**: `RAILWAY_DEPLOYMENT.md` + +--- + +## 🎉 Congratulations! + +Your TalimBot is now a **real, independent website** accessible from anywhere! 🚀 + +**Your app URL**: `https://your-project.up.railway.app` + +Teachers and students can access it from: +- ✅ Home computers +- ✅ School computers +- ✅ Phones (any device with internet) +- ✅ Tablets + +No need for localhost, no need for running Python locally - it's fully online! 🌐 diff --git a/resources_references/README.md b/resources_references/README.md new file mode 100644 index 0000000000000000000000000000000000000000..35b35181c2d2d1696288d6892360034594c4124d --- /dev/null +++ b/resources_references/README.md @@ -0,0 +1,407 @@ +# TalimBot - سیستم گروه‌بندی هوشمند دانش‌آموزان + +A full-stack intelligent student grouping system using AI-powered analysis with FastAPI backend and GitHub Pages frontend. + +## 🎯 Overview + +TalimBot groups 30 real students based on MBTI personality types, learning styles, math grades, and peer preferences using AI (GPT-4o-mini via OpenRouter). Teachers control when students can view their group assignments. + +### Real Student Data +- **30 Students** from class 1061 (دهم تجربی) +- **Persian Names**: Full Persian names from actual class roster +- **National Code Authentication**: Students login with their national ID +- **Math Grades**: Real grades from student records + +--- + +## 🚀 Quick Start (Local Development) + +### Prerequisites +- Python 3.8+ installed +- Modern web browser +- Internet connection (for AI grouping) + +### Step 1: Start Backend (30 seconds) + +```powershell +# Navigate to backend folder +cd backend + +# Install dependencies (first time only) +pip install -r requirements.txt + +# Start the server +python main.py +``` + +**Server runs on:** `http://localhost:8000` + +**Keep this terminal running!** + +### Step 2: Open Frontend (10 seconds) + +**Option A - Direct Open (Easiest):** +- Double-click `index.html` in File Explorer + +**Option B - Local Server (Recommended):** +```powershell +# In a NEW terminal window +python -m http.server 3000 +``` +Then visit: `http://localhost:3000` + +--- + +## 👥 Login Credentials + +### Students (30 students available) +Login with: +- **Student Number:** S001, S002, ..., S030 +- **National Code:** Each student's 10-digit national ID + +**Example:** +- Student Number: `S001` +- National Code: `929986644` +- Name: آدینه پور - یاسمن + +### Teacher +- **Password:** `teacher123` + +--- + +## 📋 How It Works + +### For Students: + +1. **Login** + - Enter student number (S001-S030) + - Enter your 10-digit national code + - System displays your Persian name + +2. **Complete Profile** + - Take MBTI test (link provided) + - Take VARK learning style test (link provided) + - Fill out AMS and Cooperative preferences + - Select up to 4 preferred groupmates + - Save your information + +3. **View Group** (after teacher enables) + - See your group number + - View all group members + - Read AI reasoning in Persian + +### For Teachers: + +1. **Monitor Progress** + - View dashboard with statistics + - See which students completed profiles + - Check readiness for grouping + +2. **Perform Grouping** + - Enter course name + - Click "Start Grouping" + - AI analyzes all data (10-30 seconds) + - Review generated groups with Persian reasoning + +3. **Control Visibility** + - Groups are hidden by default + - Click "Show Results to Students" + - Toggle visibility on/off anytime + +4. **Manage Data** + - View all student information + - Edit student data if needed + - Reset grouping when necessary + +--- + +## 🌐 Deployment to GitHub Pages + +### Part 1: Deploy Frontend to GitHub Pages + +1. **Create GitHub Repository** + ```bash + # Initialize git (if not already done) + git init + git add . + git commit -m "Initial commit with real student data" + + # Create repo on GitHub and push + git remote add origin https://github.com/talimbot/talimbot.git + git branch -M main + git push -u origin main + ``` + +2. **Enable GitHub Pages** + - Go to repository Settings → Pages + - Source: Deploy from branch `main` + - Folder: `/ (root)` + - Click Save + +3. **Your site will be live at:** + `https://talimbot.github.io/talimbot/` + +### Part 2: Deploy Backend to Render.com (Free) + +1. **Create Render Account** + - Visit [render.com](https://render.com) + - Sign up with GitHub + +2. **Create New Web Service** + - Click "New +" → "Web Service" + - Connect your GitHub repository + - Settings: + - **Name:** `talimbot-backend` + - **Environment:** `Python 3` + - **Build Command:** `pip install -r backend/requirements.txt` + - **Start Command:** `cd backend && uvicorn main:app --host 0.0.0.0 --port $PORT` + - **Plan:** Free + +3. **Add Environment Variable** + - In Render dashboard → Environment + - Add: `OPENROUTER_API_KEY` = `your-api-key` + - Optionally: `TEACHER_PASSWORD` = `your-password` + +4. **Get Your Backend URL** + - Copy the URL (e.g., `https://talimbot-backend.onrender.com`) + +### Part 3: Update Frontend to Use Deployed Backend + +Update `API_BASE_URL` in BOTH files: +- `assets/js/data.js` +- `assets/js/grouping.js` + +```javascript +// Change from: +const API_BASE_URL = 'http://localhost:8000/api'; + +// To: +const API_BASE_URL = 'https://talimbot-backend.onrender.com/api'; +``` + +Commit and push changes: +```bash +git add . +git commit -m "Update API URL for production" +git push +``` + +GitHub Pages will auto-deploy in ~1 minute. + +--- + +## 🔧 Configuration + +### API Key (OpenRouter) +Located in `backend/grouping_logic.py`: +```python +OPENROUTER_API_KEY = 'sk-or-v1-...' +``` + +### Teacher Password +Located in `backend/main.py` (SystemData model): +```python +teacherPassword: str = "teacher123" +``` + +Change as needed for security. + +### Student Data +Students are initialized in `backend/main.py` in the `load_data()` function. +All 30 real students with Persian names and national codes are pre-loaded. + +--- + +## 📁 Project Structure + +``` +talimbot/ +├── backend/ +│ ├── main.py # FastAPI server with real student data +│ ├── grouping_logic.py # AI grouping with OpenRouter +│ ├── requirements.txt # Python dependencies +│ ├── README.md # Backend documentation +│ └── data/ +│ └── students.json # Auto-created data storage +│ +├── assets/ +│ ├── css/ +│ │ └── styles.css +│ └── js/ +│ ├── data.js # API client for student data +│ └── grouping.js # API client for grouping +│ +├── pages/ +│ ├── login.html # Login with national code +│ ├── student-dashboard.html +│ ├── student-data.html +│ ├── teacher-dashboard.html +│ └── group-view.html +│ +├── Icons/ # UI icons +├── index.html # Redirects to login +├── README.md # This file +├── .gitignore # Git ignore rules +└── start-backend.ps1 # Windows startup script +``` + +--- + +## 🎯 Features + +### ✅ Implemented +- **Real Student Data**: 30 actual students with Persian names +- **National Code Authentication**: Secure login using national IDs +- **Persistent Storage**: JSON database on backend +- **AI-Powered Grouping**: OpenRouter GPT-4o-mini integration +- **Persian Language**: Full RTL support with Persian reasoning +- **Teacher Controls**: Show/hide results, reset grouping +- **Math Grades**: Real grades from class records +- **MBTI & Learning Styles**: Comprehensive personality analysis +- **Peer Preferences**: Students select preferred groupmates + +### 🎨 UI Features +- Modern, responsive design with Tailwind CSS +- Persian (RTL) interface +- Real-time validation +- Progress indicators + +--- + +## 🐛 Troubleshooting + +### "Failed to fetch" errors +**Problem:** Backend not running or wrong URL +**Solution:** +1. Check backend is running: Visit `http://localhost:8000` +2. Should see: `{"message":"TalimBot API is running"}` +3. Check `API_BASE_URL` in JS files matches backend + +### Students can't login +**Problem:** Wrong national code +**Solution:** +- National code must be exactly 10 digits +- Must match the code in backend database +- Check `backend/main.py` for correct codes + +### Students can't see groups +**Problem:** Teacher hasn't enabled visibility +**Solution:** +- Teacher must click "Show Results to Students" +- Button appears after grouping is complete + +### Grouping fails +**Problem:** API error or no internet +**Solution:** +1. Check internet connection +2. Verify OpenRouter API key is valid +3. Check backend logs for errors +4. System has fallback random grouping if AI fails + +--- + +## 📊 Student List (Sample) + +| # | Student Number | Name | National Code | Math Grade | +|---|----------------|------|---------------|------------| +| 1 | S001 | آدینه پور - یاسمن | 929986644 | 16.0 | +| 2 | S002 | احمدزاده - پریا | 980085330 | 12.0 | +| 3 | S003 | اکبرزاده - فاطمه | 970154550 | 11.0 | +| 4 | S004 | الهی مهر - آناهیتا | 26425955 | 17.0 | +| 5 | S005 | امیری - مریم | 980093341 | 18.0 | +| ... | ... | ... | ... | ... | +| 30 | S030 | وحدتی - باران | 929916913 | 12.0 | + +*Full list available in `backend/main.py`* + +--- + +## 🔐 Security Notes + +### Current (Demo/Development): +- ✅ Simple password authentication +- ✅ CORS allows all origins +- ✅ National codes as passwords + +### Recommended for Production: +- 🔒 Use environment variables for secrets +- 🔒 Implement JWT token authentication +- 🔒 Enable HTTPS only +- 🔒 Add rate limiting +- 🔒 Use PostgreSQL instead of JSON +- 🔒 Hash national codes +- 🔒 Implement proper session management + +--- + +## 📞 API Endpoints + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/` | GET | Health check | +| `/api/students` | GET | Get all students | +| `/api/student/{id}` | GET | Get one student | +| `/api/student/{id}` | PUT | Update student | +| `/api/auth/student` | POST | Authenticate student | +| `/api/auth/teacher` | POST | Authenticate teacher | +| `/api/grouping/perform` | POST | Run AI grouping | +| `/api/grouping/status` | GET | Get stats | +| `/api/grouping/toggle-visibility` | POST | Show/hide results | +| `/api/grouping/reset` | POST | Clear grouping | +| `/api/student/{id}/group` | GET | Get student's group | + +--- + +## 🎓 Technologies Used + +### Backend +- **FastAPI** - Modern Python web framework +- **Uvicorn** - ASGI server +- **Pydantic** - Data validation +- **aiohttp** - Async HTTP client +- **OpenRouter API** - AI integration + +### Frontend +- **HTML5 / CSS3** - Structure and styling +- **Tailwind CSS** - Utility-first CSS +- **JavaScript (Async/Await)** - Modern JS +- **Fetch API** - HTTP requests + +### Deployment +- **GitHub Pages** - Frontend hosting (free) +- **Render.com** - Backend hosting (free) + +--- + +## 📝 License + +This project is for educational purposes. + +--- + +## 🙏 Credits + +- Student data from class 1061 (دهم تجربی) +- AI grouping powered by OpenRouter (GPT-4o-mini) + +--- + +## ✅ Pre-Launch Checklist + +- [ ] Backend running locally +- [ ] Students can login with national codes +- [ ] Teacher can login with password +- [ ] Grouping works with AI +- [ ] Visibility toggle works +- [ ] All 30 students load correctly +- [ ] Persian names display properly +- [ ] Math grades show correctly +- [ ] Backend deployed to Render +- [ ] Frontend deployed to GitHub Pages +- [ ] API URLs updated for production + +--- + +**Live URL:** `https://talimbot.github.io/talimbot/pages/login.html` + +**Last Updated:** December 2025 diff --git a/resources_references/TEST_RESULTS_AND_SOLUTION.md b/resources_references/TEST_RESULTS_AND_SOLUTION.md new file mode 100644 index 0000000000000000000000000000000000000000..486637eb8f2d10c6d1273878203a293979ec2e78 --- /dev/null +++ b/resources_references/TEST_RESULTS_AND_SOLUTION.md @@ -0,0 +1,225 @@ +# ✅ SERVER ISSUE - FULLY DIAGNOSED & READY TO FIX + +## 🎯 Test Results (Just Ran) + +✅ **Server Status:** Running on port 8000 +✅ **API Response:** Returns data successfully +✅ **Firewall Rules:** Properly configured for Python +✅ **Local Network:** Server IS accessible from 192.168.114.1 +✅ **Deployment Files:** All created and ready + +--- + +## 🔍 Problem Diagnosis + +**Why GitHub Pages doesn't work:** +``` +https://talimbot.github.io/talimbot/ + ↓ tries to connect to +http://localhost:8000 ← This doesn't exist on the internet! +``` + +**Why phone doesn't work:** +Your phone tries to connect to "localhost" which means "this phone" not "your laptop" + +--- + +## 🎯 TWO SOLUTIONS - Choose One + +### 📱 Solution A: Test on Phone NOW (Temporary) + +Your server **IS** accessible from local network! + +**On Your Phone:** +1. Connect to **same Wi-Fi** as laptop +2. Open browser and visit: + ``` + http://192.168.114.1:8000/index.html + ``` + Or just test API: + ``` + http://192.168.114.1:8000/api/students + ``` + +3. Should work! ✅ + +**To make GitHub Pages work with your laptop server:** +1. Edit `assets/js/data.js` +2. Change: + ```javascript + const API_BASE_URL = 'http://localhost:8000/api'; + ``` + To: + ```javascript + const API_BASE_URL = 'http://192.168.114.1:8000/api'; + ``` +3. Push to GitHub +4. Now GitHub Pages will try to connect to your laptop +5. **Only works when laptop is on and server is running!** + +⚠️ **Limitations:** +- Only works on your Wi-Fi network +- Laptop must be on with server running +- If laptop IP changes, you need to update code +- Not suitable for real deployment + +--- + +### 🌐 Solution B: Deploy to Cloud (PERMANENT) ⭐ RECOMMENDED + +Make your backend accessible from anywhere! + +**5-Minute Render.com Deployment:** + +1. **Sign up:** https://render.com (use GitHub login) + +2. **New Web Service:** + - Click "New +" → "Web Service" + - Connect your `talimbot` repository + - Select the repo + +3. **Configure:** + ``` + Name: talimbot-api + Build Command: pip install -r backend/requirements.txt + Start Command: cd backend && uvicorn main:app --host 0.0.0.0 --port $PORT + Instance Type: Free + ``` + +4. **Deploy:** Click "Create Web Service" + +5. **Get URL:** Copy your URL (e.g., `https://talimbot-api.onrender.com`) + +6. **Update Frontend:** + Edit `assets/js/data.js`: + ```javascript + const API_BASE_URL = 'https://talimbot-api.onrender.com/api'; + ``` + +7. **Push to GitHub:** + ```bash + git add . + git commit -m "Update API URL for production" + git push + ``` + +8. **Done!** Your site works from anywhere! 🎉 + +✅ **Benefits:** +- Works from anywhere in the world +- Works on any device +- No laptop needed +- Professional setup +- FREE tier available + +--- + +## 📋 Files I Created for You + +| File | Purpose | +|------|---------| +| `DEPLOYMENT_GUIDE.md` | Complete deployment tutorial (Render/Railway/PythonAnywhere) | +| `FIXING_SERVER_ERROR.md` | Troubleshooting guide | +| `Procfile` | Tells Render how to start server | +| `runtime.txt` | Specifies Python version | +| `render.yaml` | Render configuration | +| `setup-firewall.ps1` | Windows firewall setup (if needed) | +| `THIS FILE` | Summary of everything | + +--- + +## 🚀 WHAT TO DO NOW + +### For Quick Test (Next 5 Minutes): +1. **On phone**, visit: `http://192.168.114.1:8000/api/students` +2. Should see JSON data ✅ +3. This proves your server works! + +### For Real Deployment (Next 15 Minutes): +1. **Go to:** https://render.com +2. **Follow steps** in Solution B above +3. **Read full guide:** `DEPLOYMENT_GUIDE.md` +4. **Update code** with Render URL +5. **Push to GitHub** +6. **Your app works worldwide!** 🌍 + +--- + +## 📊 Current vs After Deployment + +**Current (localhost):** +``` +✅ Laptop browser → Works +❌ Phone → Doesn't work +❌ GitHub Pages → Doesn't work +❌ Other people → Can't access +``` + +**After Deployment:** +``` +✅ Laptop browser → Works +✅ Phone → Works +✅ GitHub Pages → Works +✅ Other people → Can access +✅ From anywhere → Works +``` + +--- + +## 💡 Quick Comparison + +| Method | Time | Cost | Works Everywhere | Reliable | +|--------|------|------|------------------|----------| +| **Localhost** | 0 min | Free | ❌ No | - | +| **Local IP (192.168.x.x)** | 0 min | Free | ❌ Wi-Fi only | ❌ | +| **Render.com** | 5 min | Free | ✅ Yes | ✅ | +| **Railway.app** | 5 min | $5 credit | ✅ Yes | ✅ | +| **PythonAnywhere** | 15 min | Free | ✅ Yes | ✅ | + +**Recommendation:** Render.com (easiest, fastest, free) + +--- + +## 🎓 What You Learned + +1. **localhost = your computer only** (not accessible from internet) +2. **192.168.x.x = local network only** (same Wi-Fi) +3. **https://your-app.onrender.com = anywhere** (real internet URL) +4. **GitHub Pages = frontend only** (needs backend somewhere else) +5. **Backend must be deployed separately** from frontend + +--- + +## ❓ Common Questions + +**Q: Can't I just use localhost?** +A: No, localhost doesn't exist on the internet. + +**Q: Why does it work on my laptop?** +A: Your laptop's browser can reach localhost because the server is running ON your laptop. + +**Q: Will phone work if I deploy?** +A: Yes! After deployment, phone/laptop/anyone can access it. + +**Q: Is Render really free?** +A: Yes, free tier available. Service sleeps after 15 min inactivity (wakes up in 30s on first request). + +**Q: What if Render is too slow?** +A: Try Railway ($5 free credit) or upgrade Render later. + +--- + +## 🎯 Bottom Line + +**Your server works perfectly!** ✅ +It's just not on the internet yet. + +**Two choices:** +1. Quick test on phone: Use `http://192.168.114.1:8000` (same Wi-Fi) +2. Real deployment: Deploy to Render (5 minutes, works everywhere) + +**I recommend:** Deploy to Render - then your project is truly online! 🚀 + +--- + +**All files are ready. All tests passed. Ready to deploy!** ✅ diff --git a/resources_references/angizesh_tahsili.txt b/resources_references/angizesh_tahsili.txt new file mode 100644 index 0000000000000000000000000000000000000000..cb97f9b7b3f92510e5fc5537c8198ddafcb66817 --- /dev/null +++ b/resources_references/angizesh_tahsili.txt @@ -0,0 +1,67 @@ +پرسشنامه انگیزش تحصیلی (نسخه محقق‌ساخته هم‌ساخت با AMS – مناسب دانش‌آموزان) +۲۸ گویه – ۷ زیرمقیاس – مقیاس ۷ گزینه‌ای +راهنمای پاسخ‌دهی +لطفاً مشخص کنید هر جمله تا چه حد درباره شما درست است: +1. اصلاً درست نیست +2. خیلی کم درست است +3. کمی درست است +4. تا حدی درست است +5. نسبتاً درست است +6. خیلی درست است +7. کاملاً درست است +________________________________________ +A) بی‌انگیزشی (Amotivation) +1. گاهی نمی‌دانم چرا باید برای درس خواندن تلاش کنم. +2. احساس می‌کنم درس خواندن برایم فایده‌ای ندارد. +3. بعضی وقت‌ها فکر می‌کنم ادامه دادن درس هیچ هدف مشخصی برایم ندارد. +4. وقتی درس می‌خوانم، نمی‌دانم برای چه چیزی زحمت می‌کشم. +________________________________________ +B) انگیزش بیرونی – تنظیم بیرونی (External Regulation) +5. برای اینکه والدین یا معلمانم از من ناراحت نشوند، درس می‌خوانم. +6. بیشتر برای گرفتن نمره خوب تلاش می‌کنم، نه چیز دیگر. +7. درس می‌خوانم چون از پیامدهای منفی نمره پایین می‌ترسم. +8. پیشرفت تحصیلی‌ام برایم مهم است چون دیگران از من انتظار دارند. +________________________________________ +C) انگیزش بیرونی – درون‌فکنی‌شده (Introjected Regulation) +9. وقتی درس نمی‌خوانم احساس گناه می‌کنم. +10. درس خواندن باعث می‌شود حس بهتری نسبت به خودم داشته باشم. +11. اگر برای امتحان آماده نباشم، از خودم ناراحت می‌شوم. +12. بعضی وقت‌ها برای اینکه احساس ارزشمندی کنم، بیشتر درس می‌خوانم. +________________________________________ +D) انگیزش بیرونی – همانندسازی‌شده (Identified Regulation) +13. درس خواندن برایم مهم است چون می‌دانم در آینده به من کمک می‌کند. +14. یادگیری مطالب مدرسه را ارزشمند می‌دانم. +15. موفقیت در درس را بخشی از پیشرفت شخصی خودم می‌دانم. +16. برای این درس می‌خوانم که به هدف‌های آینده‌ام نزدیک‌تر شوم. +________________________________________ +E) انگیزش درونی برای کسب دانش (Intrinsic – To Know) +17. از یادگرفتن مطالب جدید لذت می‌برم. +18. وقتی چیزی را واقعاً یاد می‌گیرم، احساس رضایت می‌کنم. +19. فهمیدن موضوعات سخت برایم هیجان‌انگیز است. +20. دوست دارم درباره درس‌ها بیشتر بدانم، حتی خارج از کلاس. +________________________________________ +F) انگیزش درونی برای کسب موفقیت (Intrinsic – Toward Accomplishment) +21. حل کردن یک مسئله سخت به من حس موفقیت می‌دهد. +22. وقتی یک تکلیف را عالی انجام می‌دهم، احساس افتخار می‌کنم. +23. دوست دارم در درس‌ها عملکردی بالاتر از حد معمول داشته باشم. +24. تلاش می‌کنم چون می‌خواهم توانایی‌هایم را نشان بدهم. +________________________________________ +G) انگیزش درونی برای تجربه تحریک/هیجان (Intrinsic – Stimulation) +25. بعضی درس‌ها برایم هیجان‌انگیز و جذاب هستند. +26. یادگیری برایم لذت‌بخش است، حتی وقتی سخت باشد. +27. وقتی در فعالیت‌های آموزشی مشارکت می‌کنم، احساس انرژی و شادی می‌کنم. +28. تجربه کردن روش‌های جدید یادگیری برایم جالب و هیجان‌آور است. +________________________________________ +نمره‌گذاری +• هر سؤال از ۱ تا ۷ نمره دارد. +• نمره هر زیرمقیاس = مجموع ۴ گویه آن (دامنه: ۴ تا ۲۸). +• نمره‌گذاری +• هر یک از ۷ زیرمقیاس شامل ۴ سؤال است. +• • بی‌انگیزشی = میانگین/جمع سؤالات 1–4 +• • تنظیم بیرونی = سؤالات 5–8 +• • درون‌فکنی شده = 9–12 +• • همانندسازی شده = 13–16 +• • انگیزش درونی برای دانستن = 17–20 +• • انگیزش درونی برای موفقیت = 21–24 +• • انگیزش درونی برای تحریک/هیجان = 25–28 + diff --git a/resources_references/cooperative.txt b/resources_references/cooperative.txt new file mode 100644 index 0000000000000000000000000000000000000000..27b15365feea47e8999b4baad9e74f05dd5a6054 --- /dev/null +++ b/resources_references/cooperative.txt @@ -0,0 +1,64 @@ +پرسشنامه یادگیری مشارکتی (بر اساس مدل جانسون و جانسون، ۱۹۹۴) +مناسب برای دانشجویان / دانش‌آموزان / معلمان +نسخه فارسی – محقق‌ساخته مبتنی بر مدل پنج‌عنصری +مقیاس پاسخ‌دهی +لطفاً میزان موافقت خود را با هر جمله مشخص کنید: +1. کاملاً مخالفم +2. مخالفم +3. نظری ندارم +4. موافقم +5. کاملاً موافقم +________________________________________ +بخش اول: وابستگی متقابل مثبت +(Positive Interdependence) +1. موفقیت من در فعالیت‌های این درس به همکاری اعضای گروه بستگی دارد. +2. اگر یکی از اعضای گروه کار خود را انجام ندهد، عملکرد کل گروه تحت تأثیر قرار می‌گیرد. +3. اعضای گروه برای رسیدن به هدف مشترک احساس مسئولیت مشترک دارند. +4. در فعالیت‌های گروهی، پیشرفت هر عضو به پیشرفت گروه کمک می‌کند. +5. هدف‌های گروه طوری طراحی شده‌اند که بدون همکاری، دستیابی به آن‌ها دشوار است. +________________________________________ +بخش دوم: پاسخگویی فردی +(Individual Accountability) +6. هر عضو گروه مسئول بخش مشخصی از کار است. +7. عملکرد من در گروه به‌طور جداگانه نیز مورد ارزیابی قرار می‌گیرد. +8. من باید وظیفه خودم را به‌خوبی انجام دهم تا گروه موفق شود. +9. همه اعضا می‌دانند که سهم هر فرد چه تأثیری بر نتیجه نهایی دارد. +10. هر فرد در گروه باید بتواند بخش مربوط به خود را توضیح دهد. +________________________________________ +بخش سوم: تعامل چهره‌به‌چهره / ارتقای متقابل +(Face-to-Face Promotive Interaction) +11. اعضای گروه در کلاس به‌طور مستقیم با یکدیگر گفت‌وگو می‌کنند. +12. هنگام انجام فعالیت‌های گروهی، اعضا به یکدیگر کمک می‌کنند تا بهتر یاد بگیرند. +13. در گروه، توضیح دادن مطالب به یکدیگر امری معمول است. +14. اعضای گروه به یکدیگر بازخورد سازنده می‌دهند. +15. در فعالیت‌های گروهی، فرصت کافی برای تبادل نظر وجود دارد. +________________________________________ +بخش چهارم: مهارت‌های اجتماعی +(Social Skills) +16. اعضای گروه با احترام به نوبت یکدیگر صحبت می‌کنند. +17. اعضا برای حل اختلاف‌ها از گفت‌وگو و مذاکره استفاده می‌کنند. +18. اعضای گروه به یکدیگر گوش می‌دهند و صحبت‌های هم‌گروهی‌ها را قطع نمی‌کنند. +19. مدیریت زمان و تقسیم وظایف به شکل مؤثر انجام می‌شود. +20. اعضای گروه در تعاملات خود ادب و احترام را رعایت می‌کنند. +________________________________________ +بخش پنجم: پردازش گروهی / بازاندیشی در عملکرد گروه +(Group Processing) +21. در پایان فعالیت‌ها، گروه درباره نقاط قوت خود بحث می‌کند. +22. گروه به‌طور منظم درباره این‌که چگونه می‌توانند همکاری بهتری داشته باشند، بازاندیشی می‌کند. +23. اعضا درباره مشکلات گروه و راه‌حل‌های ممکن با هم صحبت می‌کنند. +24. گروه پس از هر فعالیت، ارزیابی کوتاهی از نحوه عملکرد خود انجام می‌دهد. +25. اعضای گروه در مورد بهبود عملکرد آینده تصمیم‌گیری مشترک می‌کنند. +________________________________________ +نحوه نمره‌گذاری +• مقیاس: 1 تا 5(نمره بالاتر = سطح بالاتر یادگیری مشارکتی) +نمره هر زیرمقیاس +• هر مؤلفه = مجموع نمرات ۵ گویه +• دامنه نمره هر زیرمقیاس: ۵ تا ۲۵ +نمره کل یادگیری مشارکتی +• مجموع گویه‌ها +• دامنه: ۲۵ تا ۱۲۵ +تفسیر پیشنهادی نمره کل +• ۲۵–۵۰: یادگیری مشارکتی کم +• ۵۱–۸۸: یادگیری مشارکتی متوسط +• ۸۹–۱۲۵: یادگیری مشارکتی زیاد + diff --git a/resources_references/sample-student-data.json b/resources_references/sample-student-data.json new file mode 100644 index 0000000000000000000000000000000000000000..9a49c4c57d6c45981c0d216cee3789342c2136d8 --- /dev/null +++ b/resources_references/sample-student-data.json @@ -0,0 +1,26 @@ +[ + { + "studentNumber": "S001", + "mbti": "INTJ", + "learningStyle": "Visual", + "ams": "150", + "cooperative": "95", + "preferredStudents": ["S002", "S003"] + }, + { + "studentNumber": "S002", + "mbti": "ENFP", + "learningStyle": "Aural", + "ams": "130", + "cooperative": "110", + "preferredStudents": ["S001", "S004", "S005"] + }, + { + "studentNumber": "S003", + "mbti": "ISFJ", + "learningStyle": "Read/Write", + "ams": "165", + "cooperative": "88", + "preferredStudents": ["S001", "S002"] + } +] diff --git a/resources_references/setup-firewall.ps1 b/resources_references/setup-firewall.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..d49c209f936797d74d4c12c562d2b1b0a4b4e956 --- /dev/null +++ b/resources_references/setup-firewall.ps1 @@ -0,0 +1,43 @@ +# 🔥 Allow Backend Server Through Firewall (Windows) + +# This script allows Python to accept connections from your local network +# Run this in PowerShell as Administrator + +Write-Host "Checking firewall rules for Python..." -ForegroundColor Cyan + +# Find Python executable +$pythonPath = (Get-Command python).Source + +Write-Host "Python location: $pythonPath" -ForegroundColor Yellow + +# Create firewall rule +$ruleName = "TalimBot Backend Server" + +# Check if rule already exists +$existingRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue + +if ($existingRule) { + Write-Host "Firewall rule already exists. Removing old rule..." -ForegroundColor Yellow + Remove-NetFirewallRule -DisplayName $ruleName +} + +# Create new rule +Write-Host "Creating firewall rule to allow Python server..." -ForegroundColor Cyan + +New-NetFirewallRule -DisplayName $ruleName ` + -Direction Inbound ` + -Program $pythonPath ` + -Action Allow ` + -Protocol TCP ` + -LocalPort 8000 ` + -Profile Any ` + -Description "Allows TalimBot backend server to accept connections on port 8000" + +Write-Host "✅ Firewall rule created successfully!" -ForegroundColor Green +Write-Host "" +Write-Host "Now your server at http://192.168.114.1:8000 should be accessible from your phone!" -ForegroundColor Green +Write-Host "" +Write-Host "Next steps:" -ForegroundColor Cyan +Write-Host "1. Make sure your laptop and phone are on the same Wi-Fi" +Write-Host "2. Server must be running: python main.py" +Write-Host "3. On phone, visit: http://192.168.114.1:8000/api/students" diff --git a/resources_references/start-backend.ps1 b/resources_references/start-backend.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..b4ba3c51a7978a6bf79f154ad52c88a747291013 --- /dev/null +++ b/resources_references/start-backend.ps1 @@ -0,0 +1,30 @@ +# Start Backend Server +Write-Host "Starting TalimBot Backend..." -ForegroundColor Green + +# Check if Python is installed +try { + $pythonVersion = python --version 2>&1 + Write-Host "Python found: $pythonVersion" -ForegroundColor Cyan +} catch { + Write-Host "ERROR: Python not found. Please install Python 3.8 or higher." -ForegroundColor Red + exit 1 +} + +# Navigate to backend directory +$backendPath = Join-Path $PSScriptRoot "backend" +Set-Location $backendPath + +# Check if requirements are installed +Write-Host "Checking dependencies..." -ForegroundColor Yellow +$requirementsExist = Test-Path "requirements.txt" + +if ($requirementsExist) { + Write-Host "Installing/updating dependencies..." -ForegroundColor Yellow + pip install -r requirements.txt +} + +# Start the server +Write-Host "`nStarting FastAPI server on http://localhost:8000" -ForegroundColor Green +Write-Host "Press Ctrl+C to stop the server`n" -ForegroundColor Yellow + +python main.py diff --git a/resources_references/students_class_notebook.txt b/resources_references/students_class_notebook.txt new file mode 100644 index 0000000000000000000000000000000000000000..c47b686f3fda90b7c0994497983030edaccb18c3 --- /dev/null +++ b/resources_references/students_class_notebook.txt @@ -0,0 +1,35 @@ +کارنامه مستمر پاییز,,,,,,,,,,,,,,,,,,,, +پایه: دهم تجربی,,,کلاس: 1061,,,,,,,,,,,,,,سال تحصیلی: 1405-1404,,, +رديف,نام خانوادگی - نام,کد ملی,کلاس,معــدل,اختصاصی,رتبه,,تعلیمات دینی (دینی، اخلاق و قرآن) 1 ,عربی، زبان قرآن1 ,فارسی 1 ,نگارش 1 ,زبان خارجی1 ,جغرافیای عمومی و استان شناسی ,تفکر و سواد رسانه‌ای ,آمادگی دفاعی ,ریاضی1 ,فیزیک1 ,زیست شناسی 1 ,شیمی 1 ,انضباط +,,,,,,کلاس,پایه,,,,,,,,,,,,, +-,میانگین,-,-,18.25,17.2,-,-,19.38,17.52,18.47,20,17.43,19.5,20,20,15.7,17.18,18.23,18.2,19.88 +1,آدینه پور - یاسمن,929986644,1061,18.77,18.19,15,24,20,18,18,20,18.5,19,20,20,16,19.5,19,19,20 +2,احمدزاده - پریا,980085330,1061,17.28,15.58,24,48,19.5,13.5,18,20,17.75,19,20,20,12,16.5,16.5,18.5,20 +3,اکبرزاده - فاطمه,970154550,1061,16.71,14.92,27,52,19.25,16,18,20,13.5,18.5,20,20,11,18.5,14.5,17,20 +4,الهی مهر - آناهیتا,26425955,1061,19.05,18.73,11,18,19,19,20,20,17,20,20,20,17,19,20,19.5,20 +5,امیری - مریم,980093341,1061,18.87,17.94,12,21,20,18.5,20,20,18.25,20,20,20,18,16.75,19.5,17.5,20 +6,برادران رحیمی - باران,960043985,1061,19.07,18.4,9,16,18.25,19,20,20,19.5,19.5,20,20,18,18.75,17,20,20 +7,بصیری امین - مایسا,960089446,1061,19.33,19.48,5,9,18.25,18,19,20,18.5,20,20,20,20,18.75,20,19,20 +8,ثابت عهد - دلارام,960125620,1061,19.55,19.54,1,3,20,19,20,20,18,20,20,20,18.5,20,20,20,20 +9,جان محمدی - شاینا,960068041,1061,19.47,19.23,3,5,20,18.5,19,20,19.5,20,20,20,19,19.5,19,19.5,19.5 +10,جوان - آیدا,95112313,1061,16.77,15.06,26,51,18.5,16.75,18,20,13.5,18.5,20,20,9,16.75,18,18.5,20 +11,حاجی آبادی - سارینا,999216751,1061,16.08,12.69,28,55,19.5,18.5,16,20,16.5,18,20,20,12,7,15,17,20 +12,حسن پور جوان - هستی,960074198,1061,19.55,19.15,1,3,20,20,20,20,19,20,20,20,18,20,19.5,19.5,20 +13,حسینی - فاطمه,2400410259,1061,19.07,18.4,9,16,20,19,20,20,18,20,20,20,18,17.75,18,20,20 +14,خسروی - غزل,929995767,1061,15.05,10.69,29,56,20,19.75,14,20,14,19,20,20,7,9,17,11,20 +15,ذباح - غزل,960110186,1061,19.25,19.1,8,13,20,18.5,18,20,18.5,20,20,20,19.5,18.75,18,20,19.5 +16,راشکی - نازنین زهرا,3661516087,1061,17.02,15.71,25,50,20,13,18,20,15.5,17.5,20,20,13,16.25,18,16.5,20 +17,روح نواز - ویونا,314458344,1061,18.7,17.6,16,25,18,19,20,20,19,20,20,20,16.5,18.75,17.5,18,20 +18,سعادتی - روژینا,960051023,1061,18.2,17.52,20,35,18,16,17,20,19.5,18,20,20,15.5,18.25,18.5,18.5,20 +19,شعبانی - ترنم,950083100,1061,19.37,19.35,4,8,20,19.5,18,20,18,20,20,20,19,20,20,18.5,20 +20,شفابخش - ستایش,960126899,1061,18.36,17.73,19,30,20,15.5,15,20,19.25,20,20,20,17.5,16,17.5,20,20 +21,شیرزادخان - فاطمه,980120756,1061,19.33,19.23,5,9,19.5,18.5,17,20,19.75,20,20,20,19,19,19,20,20 +22,علی جوی - آرزو,960054316,1061,17.98,15.65,21,40,20,18.5,20,20,19,20,20,20,13,16,17,17.5,20 +23,قنادزاده - آناهیتا,960089836,1061,18.84,17.58,13,22,20,18.5,20,20,19.5,20,20,20,14,20,19,18.5,19.5 +24,کارگر - نیایش,929956052,1061,17.74,16.45,22,44,18.5,15,19,20,17,20,20,20,12,18.8,19,17.5,19.5 +25,کبریایی نسب - باران,980119588,1061,18.82,17.83,14,23,20,19.5,20,20,17.5,20,20,20,16.5,18.75,19.5,17,20 +26,کیانوش - زینب,970072678,1061,18.58,17.54,17,27,18.25,19,19,20,18.5,20,20,20,16.5,16,20,18,19.5 +27,محمودی - ستایش,929904656,1061,19.33,19.38,5,9,19.75,17,18,20,19.25,20,20,20,19.5,19.5,20,18.5,20 +28,مشتاقی - ستایش,361282217,1061,17.67,17.02,23,46,18.5,15.25,17,20,16,18.5,20,20,16.5,15.75,18,18,20 +29,معلمی - مهتاب,960070265,1061,18.56,17.79,18,29,20,18,20,20,16,20,20,20,17.5,17.75,18,18,20 +30,وحدتی - باران,929916913,1061,15.02,12.58,30,57,18.5,11.5,18,20,9,19.5,20,20,12,8,15,15.5,19 diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 0000000000000000000000000000000000000000..f2dbea3000ec972d40ca1afea09300a86fdb5ee1 --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-3.11.0