Skip to content

Latest commit

Β 

History

History
646 lines (505 loc) Β· 16.1 KB

File metadata and controls

646 lines (505 loc) Β· 16.1 KB

πŸ”„ Migration Guide: Python β†’ TypeScript

This guide helps you migrate from the Python azubiheft library to the TypeScript/Node.js version.

πŸ“š Table of Contents


🎯 Overview

Why Migrate?

The TypeScript version offers several advantages:

Feature Python Version TypeScript Version
Type Safety Runtime errors Compile-time checking
IDE Support Basic Full IntelliSense, autocomplete
Performance Good Better (V8 engine)
Ecosystem Python packages npm ecosystem
Async/Await Limited Native, first-class
Web Integration Complex Seamless
Documentation Basic Comprehensive with examples

Compatibility

The TypeScript version maintains 100% feature parity with the Python version while adding new capabilities:

  • βœ… All Python functionality preserved
  • βœ… Same API patterns and methods
  • βœ… Enhanced with TypeScript benefits
  • βœ… Additional features and utilities

πŸš€ Quick Migration

1. Installation

Python:

pip install azubiheft-python

TypeScript/Node.js:

npm install azubiheft-api

2. Basic Session Usage

Python:

from azubiheft import Session, Entry

session = Session()
session.login('username', 'password')

entry = Entry(
    date=datetime.now(),
    message='Daily work',
    time_spent='08:00',
    entry_type=1
)

session.writeReports([entry])
session.logout()

TypeScript:

import { Session, Entry, EntryType } from 'azubiheft-api';

const session = new Session();
await session.login({
  username: 'username',
  password: 'password'
});

const entry = new Entry(
  new Date(),
  'Daily work',
  '08:00',
  EntryType.BETRIEB
);

await session.writeReports([entry]);
await session.logout();

3. Key Changes Summary

  1. Async/Await: All operations are now asynchronous
  2. Object Parameters: Login uses object instead of positional parameters
  3. TypeScript Types: Strong typing with interfaces and enums
  4. Error Handling: Specific error types with type guards

πŸ“‹ API Mapping

Core Classes

Python TypeScript Notes
Session() Session() Same constructor
Entry() Entry() Same constructor pattern
TimeHelper TimeHelper Static methods, same functionality

Session Methods

Python Method TypeScript Method Changes
login(username, password) login({ username, password }) Object parameter
logout() logout() Now async
isLoggedIn() isLoggedIn() Now async
writeReport(...) writeReport(...) Now async
writeReports(entries) writeReports(entries) Now async
getReport(date) getReport(date) Now async
deleteReport(date, entry_num) deleteReport(date, entryNumber) Now async
getSubjects() getSubjects() Now async
addSubject(name) addSubject(name) Now async
deleteSubject(id) deleteSubject(id) Now async
getReportWeekId(date) getReportWeekId(date) Now async
get_art_id_from_text(name) getArtIdFromText(name) Now async, camelCase

Entry Class

Python TypeScript Changes
Entry(date, message, time_spent, type) Entry(date, message, timeSpent, type) camelCase parameters
entry.date entry.date Same
entry.message entry.message Same
entry.time_spent entry.timeSpent camelCase
entry.type entry.type Same
N/A entry.validate() New method
N/A Entry.fromObject(data) New static method
N/A entry.with(updates) New method

TimeHelper Methods

Python TypeScript Changes
TimeHelper.dateTimeToString(date) TimeHelper.dateTimeToString(date) Same
TimeHelper.getActualTimestamp() TimeHelper.getActualTimestamp() Same
TimeHelper.timeDeltaToString(delta) TimeHelper.millisecondsToTimeString(ms) Different input type
N/A TimeHelper.addTimeStrings(t1, t2) New method
N/A TimeHelper.subtractTimeStrings(t1, t2) New method
N/A TimeHelper.timeStringToMinutes(time) New method
N/A TimeHelper.minutesToTimeString(mins) New method
N/A TimeHelper.getISOWeek(date) New method

Error Handling

Python TypeScript Changes
AuthError AuthError Same
NotLoggedInError NotLoggedInError Same
ValueTooLargeError ValueTooLargeError Same
N/A ApiError New
N/A ParseError New
N/A NetworkError New
N/A isAuthError() New type guard

πŸ”„ Code Examples

Example 1: Basic Authentication

Python:

from azubiheft import Session, AuthError

session = Session()

try:
    session.login('myuser', 'mypass')
    print("Login successful!")
except AuthError as e:
    print(f"Login failed: {e}")

TypeScript:

import { Session, isAuthError } from 'azubiheft-api';

const session = new Session();

try {
  await session.login({
    username: 'myuser',
    password: 'mypass'
  });
  console.log('Login successful!');
} catch (error) {
  if (isAuthError(error)) {
    console.log(`Login failed: ${error.message}`);
  }
}

Example 2: Creating Multiple Reports

Python:

from datetime import datetime, timedelta
from azubiheft import Session, Entry

session = Session()
session.login('user', 'pass')

entries = []
for i in range(5):
    date = datetime.now() - timedelta(days=i)
    entry = Entry(
        date=date,
        message=f'Work day {i+1}',
        time_spent='08:00',
        entry_type=1
    )
    entries.append(entry)

session.writeReports(entries)
session.logout()

TypeScript:

import { Session, Entry, EntryType } from 'azubiheft-api';

const session = new Session();
await session.login({ username: 'user', password: 'pass' });

const entries = [];
for (let i = 0; i < 5; i++) {
  const date = new Date();
  date.setDate(date.getDate() - i);

  const entry = new Entry(
    date,
    `Work day ${i + 1}`,
    '08:00',
    EntryType.BETRIEB
  );
  entries.push(entry);
}

await session.writeReports(entries);
await session.logout();

Example 3: Time Calculations

Python:

from datetime import timedelta
from azubiheft import TimeHelper

# Time arithmetic
delta = timedelta(hours=8, minutes=30)
time_str = TimeHelper.timeDeltaToString(delta)
print(f"Time: {time_str}")

TypeScript:

import { TimeHelper } from 'azubiheft-api';

// Time arithmetic
const totalTime = TimeHelper.addTimeStrings('04:30', '04:00');
console.log(`Time: ${totalTime}`); // "08:30"

// Convert between formats
const minutes = TimeHelper.timeStringToMinutes('08:30');
const backToString = TimeHelper.minutesToTimeString(minutes);

Example 4: Error Handling with Retry

Python:

import time
from azubiheft import Session, AuthError, NotLoggedInError

def login_with_retry(session, username, password, max_retries=3):
    for attempt in range(max_retries):
        try:
            session.login(username, password)
            return True
        except AuthError:
            if attempt == max_retries - 1:
                raise
            time.sleep(2 ** attempt)
    return False

TypeScript:

import { Session, isAuthError, isNotLoggedInError } from 'azubiheft-api';

async function loginWithRetry(
  session: Session,
  credentials: { username: string; password: string },
  maxRetries = 3
): Promise<boolean> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      await session.login(credentials);
      return true;
    } catch (error) {
      if (isAuthError(error)) {
        if (attempt === maxRetries - 1) throw error;
        await new Promise(resolve => setTimeout(resolve, 2 ** attempt * 1000));
      } else {
        throw error;
      }
    }
  }
  return false;
}

πŸ’‘ Key Differences

1. Asynchronous Operations

Python (Synchronous):

# All operations are synchronous
session.login('user', 'pass')
reports = session.getReport(datetime.now())
session.writeReports(entries)

TypeScript (Asynchronous):

// All operations are asynchronous
await session.login({ username: 'user', password: 'pass' });
const reports = await session.getReport(new Date());
await session.writeReports(entries);

2. Parameter Styles

Python (Positional/Keyword):

session.login('username', 'password')
session.deleteReport(date, entry_number=1)

TypeScript (Object/Named):

await session.login({ username: 'username', password: 'password' });
await session.deleteReport(date, 1);

3. Type Safety

Python (Duck Typing):

# Runtime type checking
entry = Entry(
    date="invalid-date",  # Runtime error
    message=123,          # Runtime error
    time_spent="25:00",   # Runtime error
    entry_type="invalid"  # Runtime error
)

TypeScript (Compile-time Checking):

// Compile-time type checking
const entry = new Entry(
  "invalid-date",  // ❌ TypeScript error
  123,             // ❌ TypeScript error
  "25:00",         // ❌ Runtime validation error
  "invalid"        // ❌ TypeScript error
);

4. Error Handling

Python (Exception Types):

try:
    session.login('user', 'pass')
except AuthError:
    # Handle auth error
except Exception as e:
    # Handle other errors

TypeScript (Type Guards):

try {
  await session.login({ username: 'user', password: 'pass' });
} catch (error) {
  if (isAuthError(error)) {
    // Handle auth error with full type safety
  } else if (isNotLoggedInError(error)) {
    // Handle login state error
  } else {
    // Handle other errors
  }
}

πŸ› οΈ Migration Strategies

Strategy 1: Direct Translation

Replace Python code line-by-line with TypeScript equivalents:

  1. Convert imports:

    from azubiheft import Session, Entry

    ↓

    import { Session, Entry } from 'azubiheft-api';
  2. Add async/await:

    session.login('user', 'pass')

    ↓

    await session.login({ username: 'user', password: 'pass' });
  3. Update parameter styles:

    Entry(date, message, time_spent, entry_type)

    ↓

    new Entry(date, message, timeSpent, entryType)

Strategy 2: Gradual Migration

For large codebases, migrate incrementally:

  1. Start with utilities:

    • Migrate TimeHelper usage first
    • Update date/time handling
    • Test thoroughly
  2. Migrate core logic:

    • Convert Session operations
    • Update error handling
    • Add type annotations
  3. Enhance with TypeScript features:

    • Add interfaces for data structures
    • Use enums for constants
    • Implement type guards

Strategy 3: Wrapper Approach

Create compatibility wrappers for smooth transition:

// Python-like wrapper for easier migration
class PythonCompatSession {
  private session = new Session();

  login(username: string, password: string): Promise<void> {
    return this.session.login({ username, password });
  }

  writeReport(date: Date, message: string, timeSpent: string, entryType: number): Promise<void> {
    const entry = new Entry(date, message, timeSpent, entryType);
    return this.session.writeReports([entry]);
  }
}

πŸ§ͺ Testing Your Migration

1. Create Test Scripts

test-migration.ts:

import { Session, Entry, EntryType, TimeHelper } from 'azubiheft-api';

async function testMigration() {
  console.log('Testing azubiheft-api migration...');

  // Test TimeHelper (synchronous)
  const dateStr = TimeHelper.dateTimeToString(new Date());
  console.log('βœ… TimeHelper working:', dateStr);

  // Test Entry creation
  const entry = new Entry(new Date(), 'Test entry', '01:00', EntryType.BETRIEB);
  entry.validate();
  console.log('βœ… Entry creation working');

  // Test Session (requires credentials)
  if (process.env.AZUBIHEFT_USERNAME && process.env.AZUBIHEFT_PASSWORD) {
    const session = new Session();
    try {
      await session.login({
        username: process.env.AZUBIHEFT_USERNAME,
        password: process.env.AZUBIHEFT_PASSWORD
      });
      console.log('βœ… Session login working');

      const subjects = await session.getSubjects();
      console.log('βœ… Subject fetching working:', subjects.length, 'subjects');

      await session.logout();
      console.log('βœ… Session logout working');
    } catch (error) {
      console.error('❌ Session test failed:', error.message);
    }
  }

  console.log('Migration test completed!');
}

testMigration().catch(console.error);

2. Compare Outputs

Create comparable scripts in both Python and TypeScript to verify identical behavior:

Python test:

from azubiheft import TimeHelper
from datetime import datetime

date = datetime(2024, 1, 15)
result = TimeHelper.dateTimeToString(date)
print(f"Python result: {result}")

TypeScript test:

import { TimeHelper } from 'azubiheft-api';

const date = new Date('2024-01-15');
const result = TimeHelper.dateTimeToString(date);
console.log(`TypeScript result: ${result}`);

3. Automated Testing

Create comprehensive tests covering all migrated functionality:

import { describe, test, expect } from '@jest/globals';
import { Session, Entry, EntryType, TimeHelper } from 'azubiheft-api';

describe('Migration Compatibility', () => {
  test('TimeHelper produces same results as Python', () => {
    const date = new Date('2024-01-15');
    const result = TimeHelper.dateTimeToString(date);
    expect(result).toBe('20240115'); // Same as Python version
  });

  test('Entry validation works correctly', () => {
    const entry = new Entry(new Date(), 'Test', '08:00', EntryType.BETRIEB);
    expect(() => entry.validate()).not.toThrow();
  });

  // Add more tests...
});

βœ… Migration Checklist

  • Environment Setup

    • Node.js 16+ installed
    • TypeScript configured
    • azubiheft-api package installed
  • Code Migration

    • Imports converted
    • Async/await added to all session operations
    • Parameter styles updated
    • Error handling converted to type guards
  • Type Safety

    • Added TypeScript types
    • Used enums instead of magic numbers
    • Implemented proper interfaces
  • Testing

    • Created test scripts
    • Verified identical behavior
    • Added unit tests
    • Tested error scenarios
  • Documentation

    • Updated code comments
    • Created migration notes
    • Updated team documentation

πŸŽ‰ Conclusion

The migration from Python to TypeScript provides significant benefits while maintaining full compatibility. The async/await pattern, enhanced type safety, and rich ecosystem make the TypeScript version ideal for modern development.

Key Benefits Achieved:

  • βœ… Type Safety: Catch errors at compile time
  • βœ… Better IDE Support: Full IntelliSense and autocomplete
  • βœ… Modern Async: Native Promise support
  • βœ… Rich Ecosystem: Access to npm packages
  • βœ… Enhanced Features: Additional utilities and helpers

Next Steps:

  1. Complete your migration using this guide
  2. Explore the advanced features
  3. Check out the comprehensive examples
  4. Join the community for support and tips