npm package: @lime1/esprit-ts
Description: TypeScript client for ESPRIT student portal - scrape grades, absences, credits, and schedules
Author: TheLime1
Latest version: 1.1.2

![NPM Version](https://img.shields.io/npm/v/%40lime1%2Fesprit-ts)
![NPM Downloads](https://img.shields.io/npm/dm/%40lime1%2Fesprit-ts)


# @lime1/esprit-ts

A TypeScript client library for the ESPRIT student portal. Scrape grades, absences, credits, schedules, and student information with full type safety.

## Features

- 🔐 **Authentication**: Secure login/logout with session management
- 📊 **Academic Data**:
  - Regular grades (incremental release during semester)
  - Principal session results (final verdict)
  - Rattrapage grades (retake session incremental)
  - Rattrapage session results (retake final verdict)
  - Historical ranking across multiple years
  - Language proficiency levels (French & English)
- 📅 **Attendance**: Absence tracking
- 🎓 **Credits**: Academic credit history
- 📋 **Schedules**: Class schedules with download links
- 👤 **Student Info**: Name and class information
- 🔄 **ASP.NET Support**: Full handling of complex ASP.NET postback mechanisms
- 📦 **Type Safety**: Complete TypeScript definitions

## Installation

```bash
npm install @lime1/esprit-ts
# or
pnpm add @lime1/esprit-ts
# or
yarn add @lime1/esprit-ts
```

## Usage

### Basic Example

```typescript
import { EspritClient } from '@lime1/esprit-ts';

async function main() {
  const client = new EspritClient({ debug: false });

  // Login
  const result = await client.login('your-student-id', 'your-password');
  if (!result.success) {
    console.error('Login failed:', result.message);
    return;
  }

  // Get student info
  const info = await client.getStudentInfo();
  console.log(`${info.name} - ${info.className}`);

  // Get regular grades (released during semester)
  const regularGrades = await client.getRegularGrades();
  if (regularGrades) {
    const [headers, ...rows] = regularGrades;
    console.log('Headers:', headers);
    console.log('Grades:', rows);
  }

  // Get principal session result (final verdict)
  const principalResult = await client.getPrincipalResult();
  if (principalResult) {
    console.log(`Average: ${principalResult.moyenneGeneral}`);
    console.log(`Decision: ${principalResult.decision}`);
  }

  await client.logout();
}

main().catch(console.error);
```

### Comprehensive Example

```typescript
import { EspritClient } from '@lime1/esprit-ts';

async function fetchAllData() {
  const client = new EspritClient({ debug: true });

  const login = await client.login('your-id', 'your-password');
  if (!login.success) return;

  console.log('=== Student Info ===');
  const info = await client.getStudentInfo();
  console.log(info);

  console.log('\n=== Regular Grades ===');
  const regularGrades = await client.getRegularGrades();
  console.log(regularGrades);

  console.log('\n=== Principal Result ===');
  const principalResult = await client.getPrincipalResult();
  console.log(principalResult);

  console.log('\n=== Rattrapage Grades ===');
  const rattrapageGrades = await client.getRattrapageGrades();
  console.log(rattrapageGrades);

  console.log('\n=== Rattrapage Result ===');
  const rattrapageResult = await client.getRattrapageResult();
  console.log(rattrapageResult);

  console.log('\n=== Language Levels ===');
  const languageLevels = await client.getLanguageLevels();
  console.log(languageLevels);

  console.log('\n=== Ranking ===');
  const ranking = await client.getRanking();
  console.log(ranking);

  console.log('\n=== Absences ===');
  const absences = await client.getAbsences();
  console.log(absences);

  console.log('\n=== Credits ===');
  const credits = await client.getCredits();
  console.log(credits);

  console.log('\n=== Schedules ===');
  const schedules = await client.getSchedules();
  console.log(schedules);

  await client.logout();
}

fetchAllData().catch(console.error);
```

### Legacy Grade Format with Averages

```typescript
// For backward compatibility, the old getGrades() method still works
// but returns parsed Grade objects instead of raw data
const grades = await client.getGrades(); // deprecated
const summary = await client.getGradesWithAverage();

if (summary) {
  console.log(`Total Average: ${summary.totalAverage?.toFixed(2)}`);
  summary.grades.forEach(grade => {
    console.log(`${grade.designation}: ${grade.moyenne?.toFixed(2)}`);
  });
}
```

## API Reference

### `EspritClient`

#### Constructor

```typescript
new EspritClient(config?: EspritClientConfig)
```

| Option      | Type      | Default   | Description              |
| ----------- | --------- | --------- | ------------------------ |
| `debug`     | `boolean` | `false`   | Enable debug logging     |
| `userAgent` | `string`  | Chrome UA | Custom user agent string |
| `timeout`   | `number`  | `30000`   | Request timeout in ms    |

#### Methods

##### Authentication
| Method                | Returns                | Description            |
| --------------------- | ---------------------- | ---------------------- |
| `login(id, password)` | `Promise<LoginResult>` | Login to the portal    |
| `logout()`            | `Promise<boolean>`     | Logout from the portal |
| `getCookies()`        | `Promise<Cookie[]>`    | Get session cookies    |

##### Grades & Results
| Method                   | Returns                             | Description                                              |
| ------------------------ | ----------------------------------- | -------------------------------------------------------- |
| `getRegularGrades()`     | `Promise<RegularGrade \| null>`     | Get grades released during semester (Session Principale) |
| `getPrincipalResult()`   | `Promise<PrincipalResult \| null>`  | Get final verdict of principal session                   |
| `getRattrapageGrades()`  | `Promise<RattrapageGrade \| null>`  | Get retake exam grades (incremental)                     |
| `getRattrapageResult()`  | `Promise<RattrapageResult \| null>` | Get final verdict of rattrapage session                  |
| `getLanguageLevels()`    | `Promise<LanguageLevel \| null>`    | Get French and English proficiency levels                |
| `getRanking()`           | `Promise<RankingEntry[] \| null>`   | Get historical academic ranking                          |
| `getGrades()`            | `Promise<Grade[] \| null>`          | **DEPRECATED:** Use `getRegularGrades()` instead         |
| `getGradesWithAverage()` | `Promise<GradeSummary \| null>`     | Get parsed grades with calculated averages               |

##### Other Data
| Method              | Returns                       | Description                 |
| ------------------- | ----------------------------- | --------------------------- |
| `getAbsences()`     | `Promise<Absence[] \| null>`  | Get student absences        |
| `getCredits()`      | `Promise<Credit[] \| null>`   | Get credit history          |
| `getSchedules()`    | `Promise<Schedule[] \| null>` | Get available schedules     |
| `getStudentName()`  | `Promise<string \| null>`     | Get student name            |
| `getStudentClass()` | `Promise<string \| null>`     | Get student class           |
| `getStudentInfo()`  | `Promise<StudentInfo>`        | Get name and class together |

### Types

```typescript
// Legacy parsed grade format (used by getGrades())
interface Grade {
  designation: string;
  coefficient: number | null;
  noteCC: number | null;
  noteTP: number | null;
  noteExam: number | null;
}

interface GradeSummary {
  grades: GradeWithAverage[];
  totalAverage: number | null;
  totalCoefficient: number;
}

// New grade formats (raw table data)
type RegularGrade = string[][]; // First row is headers
type RattrapageGrade = string[][]; // First row is headers

interface PrincipalResult {
  moyenneGeneral: string | null;
  decision: string | null;
}

interface RattrapageResult {
  moyenneGeneral: string | null;
  decision: string | null;
}

interface LanguageLevel {
  francais: string | null;
  anglais: string | null;
}

interface RankingEntry {
  anneeUniversitaire: string | null;
  classe: string | null;
  moyenne: string | null;
  rang: string | null;
}

interface LoginResult {
  success: boolean;
  cookies: Cookie[];
  message?: string;
}

interface StudentInfo {
  name: string | null;
  className: string | null;
}
```

## Development

```bash
# Install dependencies
npm install

# Build the package
npm run build

# Type check
npm run typecheck

# Watch mode
npm run dev
```

## License

MIT