Metadata-Version: 2.4
Name: seatlock
Version: 0.2.0
Summary: A concurrency-safe seat allocation and booking engine
Author: Aditya
License: MIT
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Operating System :: OS Independent
Classifier: License :: OSI Approved :: MIT License
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: License
Dynamic: license-file

# Seat Allocation Engine – Working Cycle

This document explains **how the Seat Allocation Engine works internally**, step by step. It focuses on the **lifecycle of a seat**, **locking rules**, **bulk operations**, and **cleanup mechanics**. No UI, API, or framework concepts are involved here—this is purely the domain engine.

---

## 1. Core Purpose

The engine is designed to solve one problem **correctly**:

> Allocate seats to users in a safe, fair, and deterministic way.

It guarantees:
- No double booking
- No permanent locks
- No partial group bookings
- No lock stealing

---

## 2. Core Concepts

### 2.1 Seat as a Resource
Each seat is treated as an **independent resource**.

A seat has exactly one of three states:
- `AVAILABLE`
- `LOCKED`
- `BOOKED`

Once a seat is `BOOKED`, it is **terminal** and cannot change state.

---

### 2.2 Seat Ownership

When a seat is locked:
- It is owned by exactly **one user**
- Only that user can book it
- Other users are rejected

Ownership is enforced strictly at all times.

---

## 3. Seat Lifecycle

The lifecycle of a seat follows this strict state machine:

```
AVAILABLE → LOCKED → BOOKED
     ↑        |
     └────────┘  (lock expiry)
```

Invalid transitions are never allowed.

---

## 4. Locking Mechanism

### 4.1 Single Seat Locking

When a user requests to lock a seat:

1. The engine checks if the seat exists
2. If the seat is `BOOKED` → reject
3. If the seat is `LOCKED` → reject
4. If the seat is `AVAILABLE` → lock it

A successful lock records:
- `locked_by` (user id)
- `lock_time` (timestamp)

---

### 4.2 Bulk Seat Locking (Group Selection)

Bulk locking is **atomic**.

This means:
> Either all seats are locked, or none are.

#### Working cycle:

**Phase 1 – Validation (read-only)**
- All seats must exist
- All seats must be `AVAILABLE`
- If any seat fails → abort immediately

**Phase 2 – Commit (write)**
- All seats are locked together
- Same user
- Same lock timestamp

No partial locking is ever possible.

---

## 5. Booking Mechanism

### 5.1 Single Seat Booking

To book a seat:

1. Seat must exist
2. Seat must be `LOCKED`
3. Seat must be locked by the same user

If all checks pass:
- Seat transitions to `BOOKED`
- Lock metadata is cleared

---

### 5.2 Bulk Seat Booking

Bulk booking follows the same atomic principle as bulk locking.

**Validation phase:**
- All seats must exist
- All seats must be `LOCKED`
- All seats must be locked by the same user

**Commit phase:**
- All seats transition to `BOOKED`

If any seat fails validation → **no seat is booked**.

---

## 6. Lock Expiry (Auto Release)

Locks are **temporary by design**.

### 6.1 Timeout Rule

- Each lock has a maximum lifetime (`LOCK_TIMEOUT`)
- Default: 10 seconds

---

### 6.2 Cleanup Mechanism

Lock expiry is handled by a **system-level cleanup function**:

```
cleanup_expired_locks()
```

This function:
- Iterates over all seats
- Releases locks that exceeded the timeout
- Never depends on user actions

Important rule:

> A lock may expire, but it is never stolen by another user.

---

## 7. Concurrency Safety

All operations run inside a **global lock**.

This guarantees:
- Atomic operations
- No race conditions
- Deterministic behavior

The engine prioritizes **correctness over performance**.

---

## 8. Separation of Responsibilities

| Layer | Responsibility |
|-----|--------------|
| Seat | State + truth checks |
| SeatManager | Transitions + rules |
| Cleanup | Time-based expiry |
| UI / API | Input & presentation only |

The engine does **not** know about:
- Clicks
- HTTP
- UI state
- Databases

---

## 9. What This Engine Guarantees

✔ No double booking
✔ No permanent locks
✔ No partial group bookings
✔ Strong ownership enforcement
✔ Deterministic outcomes

---

## 10. What This Engine Intentionally Does NOT Do

- No UI rendering
- No HTTP / REST handling
- No persistence
- No background threads

These are integration concerns and belong outside the engine.

---

## 11. Intended Usage

This engine is designed to be:
- Packaged as a reusable library
- Called from event-driven systems
- Used under web, desktop, or CLI interfaces

The engine remains unchanged while integrations evolve.

---

## 12. Final Note

This is a **domain-correct seat allocation engine**.

All future work (UI, APIs, persistence, scaling) should be built **on top of this logic**, not mixed into it.

This separation is intentional and fundamental.



---

# Public API Reference

This section lists **all public methods exposed by the SeatLock engine**, with a one-line explanation of what each does. These are the only methods consumers should rely on.

---

## Importing the Engine

```python
from seatlock import Seat, SeatManager
```

---

## Core Classes

### `Seat`
Represents a single seat as an independent resource.

- `Seat(seat_id)` → Create a new seat with a unique identifier
- `is_available()` → Returns `True` if the seat is free
- `is_locked()` → Returns `True` if the seat is temporarily reserved
- `is_booked()` → Returns `True` if the seat is permanently booked

---

### `SeatManager`
Central engine that enforces all locking, booking, cancellation, and cleanup rules.

---

## Locking Methods

- `lock_seat(seat_id, user_id)`  
  Locks a single available seat for a user

- `lock_seats_bulk(seat_ids, user_id)`  
  Atomically locks multiple seats for a user (all-or-nothing)

---

## Booking Methods

- `book_a_seat(seat_id, user_id)`  
  Permanently books a single seat previously locked by the same user

- `book_seats_bulk(seat_ids, user_id)`  
  Atomically books multiple seats previously locked by the same user

---

## Cancellation Methods

- `cancel_lock(seat_id, user_id)`  
  Releases a lock held by the user on a single seat

- `cancel_locks_bulk(seat_ids, user_id)`  
  Atomically releases locks held by the user on multiple seats

---

## Cleanup / System Methods

- `cleanup_expired_locks()`  
  System-level method that releases all locks exceeding the configured timeout

---

## Code Example

```bash

#Base Case

from seatlock import SeatManager, Seat

# Create seats
seats = [Seat(i) for i in range(1, 6)]

# Initialize manager with custom timeout (in seconds)
manager = SeatManager(seats, lock_timeout=60)

# Lock a seat
print(manager.lock_seat(1, "user1"))

# Book the seat
print(manager.book_a_seat(1, "user1"))

#Bulk Use Case

from seatlock import SeatManager, Seat

seats = [Seat(i) for i in range(1, 6)]
manager = SeatManager(seats, lock_timeout=120)

# Lock multiple seats
print(manager.lock_seats_bulk([2, 3], "user2"))

# Book them
print(manager.book_seats_bulk([2, 3], "user2"))


#Handling Conflicts

# Another user tries to book without lock
print(manager.book_a_seat(4, "user3"))
# → Seat 4 is not locked

# Lock by one user
manager.lock_seat(4, "user1")

# Another user tries to lock same seat
print(manager.lock_seat(4, "user2"))
# → Seat 4 is currently locked by user1

```

## Update

In seatock v0.2.0, a new parameter has been added called lock_timeout. You can now provide the time out for a lock. 
This timeout will have base unit in seconds.

## Previous Version

In seatlock v0.1.0, lock_timeout was present but was set to 10 during developement and was constant and hence was creating a problem.
The bug is now fixed with proper parameter availibility. 

## Notes on Usage

- All methods are **thread-safe**
- All bulk operations are **atomic**
- Only the lock owner may book or cancel seats
- Booked seats are **final and immutable**
- Time-based lock expiry is handled **only** by `cleanup_expired_locks`

---

This API is intentionally minimal and stable. All UI, event handling, persistence, and networking should be built **on top of these methods**, not mixe
