Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 92 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,100 @@
# Direct Moutamadris
# Direct Moutamadris - Complete Student Portal

Nobody likes to use the clunky massar service website, not to mention when the servers are overloaded, this is a simple web app (scraper for legal reasons not) to fetch your grade data directly.
A comprehensive Next.js web application that provides access to all MoutaMadris platform functions. This app allows students and parents to access grades, attendance, schedules, announcements, homework, and student information through a modern, user-friendly interface.

# Demo
(Cloud hosted providers won't work due to the fact that you can only use the site in morocco)
- [Demo that may or may not be online](http://moutamadris.hopto.org/)

## 🌟 Features

# Run locally
```
### ✅ Currently Implemented Functions

- **Student Information** - View personal details, establishment info, and academic level
- **Academic Grades** - Access detailed grade reports with:
- Continuous assessment scores (contrôles continus)
- Exam results with coefficients
- Class averages and comparisons
- Interactive radar charts for visualization
- CSV export functionality
- **Attendance Tracking** - Monitor absence records with justified/unjustified status
- **Class Schedule** - View weekly timetable with teachers and room assignments
- **School Announcements** - Stay updated with official communications
- **Homework & Assignments** - Track assignments with due dates and completion status

### 🔧 Technical Features

- **Authentication System** - Secure login with MoutaMadris credentials
- **Tabbed Interface** - Easy navigation between different functions
- **Responsive Design** - Works on desktop and mobile devices
- **Error Handling** - Clear error messages and loading states
- **Data Parsing** - Intelligent parsing of HTML responses from MoutaMadris
- **Material-UI Components** - Modern, accessible user interface

## 📱 Screenshots

The application includes:
- Clean login interface with credential validation
- Comprehensive dashboard with 6 main sections
- Detailed grade tables with statistical analysis
- Visual charts for grade comparison
- Organized display of announcements and assignments

## 🚀 Quick Start

```bash
git clone https://github.com/Maxylium/DirectMoutamadris/
cd DirectMoutamadris
npm install
npm run dev
```

Open [http://localhost:3000](http://localhost:3000) in your browser.

## 📋 API Endpoints

The application includes the following API endpoints:

- `/api/fetch-grades` - Retrieve academic grades and scores
- `/api/fetch-student-info` - Get student profile information
- `/api/fetch-attendance` - Access attendance records
- `/api/fetch-schedule` - Retrieve class timetable
- `/api/fetch-announcements` - Get school announcements
- `/api/fetch-homework` - Access homework and assignments

## 🔒 Security & Privacy

- All authentication is handled securely through the official MoutaMadris platform
- No credentials are stored - authentication is session-based
- Data is only displayed to the authenticated user
- Network requests use proper headers and CSRF protection

## 🛠️ Development

### Project Structure
```
src/
├── components/ # React components for each feature
├── utils/ # Authentication and parsing utilities
├── types/ # TypeScript interfaces
└── app/ # Next.js app router pages

pages/api/ # API endpoints for MoutaMadris integration
```

### Key Files
- `MoutamadrisApp.tsx` - Main application with tabbed interface
- `moutamadrisAuth.ts` - Shared authentication utility
- `parseAdditionalData.ts` - HTML parsing for all data types
- `moutamadris.ts` - TypeScript type definitions

## ⚠️ Important Notes

- This application only works from Morocco due to MoutaMadris geo-restrictions
- Valid MoutaMadris credentials are required for authentication
- The app acts as a front-end client to the official MoutaMadris platform
- No data is stored locally - all information is fetched in real-time

## 🤝 Contributing

Contributions are welcome! Please feel free to submit issues or pull requests to improve the application.

## 📄 License

This project is intended for educational purposes and to improve access to student information.
36 changes: 36 additions & 0 deletions pages/api/fetch-announcements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { createAuthenticatedClient, getStandardHeaders } from '../../src/utils/moutamadrisAuth';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') return res.status(405).end();

const { username, password } = req.body;
if (!username || !password) {
return res.status(400).json({ error: 'Missing required fields' });
}

try {
const { client } = await createAuthenticatedClient(username, password);

// Try to get announcements
const announcementsRes = await client.get(
'https://massarservice.men.gov.ma/moutamadris/TuteurEleves/GetAnnonces',
{
headers: getStandardHeaders()
}
);

if (!announcementsRes.data) {
return res.status(500).json({ error: 'Could not fetch announcements' });
}

res.json({ rawHTML: announcementsRes.data });
} catch (error: any) {
console.error('API error:', error);
if (error.message === 'Login failed') {
res.status(401).json({ error: 'Login failed' });
} else {
res.status(500).json({ error: 'Something went wrong', details: error?.message });
}
}
}
41 changes: 41 additions & 0 deletions pages/api/fetch-attendance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { createAuthenticatedClient, getStandardHeaders } from '../../src/utils/moutamadrisAuth';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') return res.status(405).end();

const { username, password, year } = req.body;
if (!username || !password || !year) {
return res.status(400).json({ error: 'Missing required fields' });
}

try {
const { client } = await createAuthenticatedClient(username, password);

// Try to get attendance information
const attendancePayload = new URLSearchParams({
Annee: year.split('/')[0]
}).toString();

const attendanceRes = await client.post(
'https://massarservice.men.gov.ma/moutamadris/TuteurEleves/GetAbsences',
attendancePayload,
{
headers: getStandardHeaders()
}
);

if (!attendanceRes.data) {
return res.status(500).json({ error: 'Could not fetch attendance data' });
}

res.json({ rawHTML: attendanceRes.data });
} catch (error: any) {
console.error('API error:', error);
if (error.message === 'Login failed') {
res.status(401).json({ error: 'Login failed' });
} else {
res.status(500).json({ error: 'Something went wrong', details: error?.message });
}
}
}
63 changes: 8 additions & 55 deletions pages/api/fetch-grades.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import axios from 'axios';
import { load } from 'cheerio';
import { CookieJar } from 'tough-cookie';
import { wrapper } from 'axios-cookiejar-support';
import { createAuthenticatedClient, getStandardHeaders } from '../../src/utils/moutamadrisAuth';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') return res.status(405).end();
Expand All @@ -12,50 +9,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return res.status(400).json({ error: 'Missing required fields' });
}

const jar = new CookieJar();
// Use proxy only on Vercel (production)
const isVercel = !!process.env.VERCEL;
const axiosConfig: any = { jar, timeout: 15000 };
if (isVercel) {
axiosConfig.proxy = {
host: '196.115.252.173',
port: 3000,
// auth: { username: 'youruser', password: 'yourpass' }, // Uncomment if you set up auth
};
}
const client = wrapper(axios.create(axiosConfig));

try {
const tokenPage = await client.get('https://massarservice.men.gov.ma/moutamadris/Account');
const $ = load(tokenPage.data);
const token = $('input[name="__RequestVerificationToken"]').val();

if (!token) return res.status(500).json({ error: 'Failed to retrieve CSRF token' });

const loginPayload = new URLSearchParams({
UserName: username,
Password: password,
__RequestVerificationToken: token as string
}).toString();

const loginRes = await client.post(
'https://massarservice.men.gov.ma/moutamadris/Account',
loginPayload,
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Referer': 'https://massarservice.men.gov.ma/moutamadris/Account',
'Origin': 'https://massarservice.men.gov.ma',
'User-Agent': 'Mozilla/5.0'
}
}
);

if (!loginRes.data.includes('ChangePassword')) {
return res.status(401).json({ error: 'Login failed', details: loginRes.data });
}

await client.post('https://massarservice.men.gov.ma/moutamadris/General/SetCulture?culture=en', null);
const { client } = await createAuthenticatedClient(username, password);

const gradesPayload = new URLSearchParams({
Annee: year.split('/')[0],
Expand All @@ -66,13 +21,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
'https://massarservice.men.gov.ma/moutamadris/TuteurEleves/GetBulletins',
gradesPayload,
{
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded',
'Referer': 'https://massarservice.men.gov.ma/moutamadris/TuteurEleves/GetNotesEleve',
'Origin': 'https://massarservice.men.gov.ma',
'User-Agent': 'Mozilla/5.0'
}
headers: getStandardHeaders()
}
);

Expand All @@ -83,6 +32,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
res.json({ rawHTML: gradesRes.data });
} catch (error: any) {
console.error('API error:', error);
res.status(500).json({ error: 'Something went wrong', details: error?.message, stack: error?.stack });
if (error.message === 'Login failed') {
res.status(401).json({ error: 'Login failed' });
} else {
res.status(500).json({ error: 'Something went wrong', details: error?.message });
}
}
}
41 changes: 41 additions & 0 deletions pages/api/fetch-homework.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { createAuthenticatedClient, getStandardHeaders } from '../../src/utils/moutamadrisAuth';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') return res.status(405).end();

const { username, password, year } = req.body;
if (!username || !password || !year) {
return res.status(400).json({ error: 'Missing required fields' });
}

try {
const { client } = await createAuthenticatedClient(username, password);

// Try to get homework/assignments
const homeworkPayload = new URLSearchParams({
Annee: year.split('/')[0]
}).toString();

const homeworkRes = await client.post(
'https://massarservice.men.gov.ma/moutamadris/TuteurEleves/GetDevoirs',
homeworkPayload,
{
headers: getStandardHeaders()
}
);

if (!homeworkRes.data) {
return res.status(500).json({ error: 'Could not fetch homework data' });
}

res.json({ rawHTML: homeworkRes.data });
} catch (error: any) {
console.error('API error:', error);
if (error.message === 'Login failed') {
res.status(401).json({ error: 'Login failed' });
} else {
res.status(500).json({ error: 'Something went wrong', details: error?.message });
}
}
}
41 changes: 41 additions & 0 deletions pages/api/fetch-schedule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { createAuthenticatedClient, getStandardHeaders } from '../../src/utils/moutamadrisAuth';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') return res.status(405).end();

const { username, password, year } = req.body;
if (!username || !password || !year) {
return res.status(400).json({ error: 'Missing required fields' });
}

try {
const { client } = await createAuthenticatedClient(username, password);

// Try to get schedule/timetable information
const schedulePayload = new URLSearchParams({
Annee: year.split('/')[0]
}).toString();

const scheduleRes = await client.post(
'https://massarservice.men.gov.ma/moutamadris/TuteurEleves/GetEmploi',
schedulePayload,
{
headers: getStandardHeaders()
}
);

if (!scheduleRes.data) {
return res.status(500).json({ error: 'Could not fetch schedule data' });
}

res.json({ rawHTML: scheduleRes.data });
} catch (error: any) {
console.error('API error:', error);
if (error.message === 'Login failed') {
res.status(401).json({ error: 'Login failed' });
} else {
res.status(500).json({ error: 'Something went wrong', details: error?.message });
}
}
}
Loading