Metadata-Version: 2.4
Name: ical-rental-sync
Version: 0.1.1
Summary: Sync iCal calendars from Airbnb, Booking.com and other rental platforms into a unified availability model.
License: MIT
Keywords: airbnb,booking,ical,rental,calendar,sync,django
Author: Brigli Bushati's Coding
Requires-Python: >=3.9,<4.0
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Dist: icalendar (>=5.0,<6.0)
Requires-Dist: requests (>=2.31,<3.0)
Project-URL: Documentation, https://brigli-the-coder.com/
Project-URL: Homepage, https://brigli-the-coder/
Project-URL: Repository, https://github.com/brigli-the-coder/ical-rental-sync
Description-Content-Type: text/markdown

# ical-rental-sync

> **Sync Airbnb, Booking.com and any iCal rental calendar into one unified Python API — in 5 lines of code.**

[![PyPI version](https://badge.fury.io/py/ical-rental-sync.svg)](https://badge.fury.io/py/ical-rental-sync)
[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)

---

## The Problem

Every rental developer writes the same boilerplate:

```python
# Without this library — the painful way
import requests
from icalendar import Calendar
import pytz
# ... 80 lines of parsing, error handling, date conversion, caching ...
```

**This library replaces all of that with one import and one class.**

---

## Installation

```bash
pip install ical-rental-sync
```

---

## Quick Start

```python
from ical_rental_sync import CalendarSync

sync = CalendarSync(
    urls={
        "airbnb":  "https://www.airbnb.com/calendar/ical/YOUR_ID.ics?s=SECRET",
        "booking": "https://ical.booking.com/v1/export?t=YOUR_TOKEN",
    }
)

# Check if a date range is free
if sync.is_available("2026-06-01", "2026-06-07"):
    print("Dates are free — take the booking!")
else:
    print("Sorry, already booked.")

# Get all booked dates (great for highlighting in a frontend calendar widget)
booked = sync.get_booked_dates()
# → [date(2026, 6, 1), date(2026, 6, 2), ...]
```

---

## You Choose What to Connect

The library is fully flexible — connect **one platform, two, or ten**. You are not forced to use Airbnb and Booking together.

```python
# ── Option A: Only Airbnb ──────────────────────────────────────────
sync = CalendarSync(urls={
    "airbnb": "https://www.airbnb.com/calendar/ical/..."
})

# ── Option B: Only Booking.com ─────────────────────────────────────
sync = CalendarSync(urls={
    "booking": "https://ical.booking.com/v1/export?t=..."
})

# ── Option C: Both (most common setup) ────────────────────────────
sync = CalendarSync(urls={
    "airbnb":  "https://www.airbnb.com/calendar/ical/...",
    "booking": "https://ical.booking.com/v1/export?t=...",
})

# ── Option D: Everything + custom platforms ────────────────────────
sync = CalendarSync(urls={
    "airbnb":     "https://www.airbnb.com/calendar/ical/...",
    "booking":    "https://ical.booking.com/v1/export?t=...",
    "vrbo":       "https://www.vrbo.com/icalendar/...",
    "google_cal": "https://calendar.google.com/calendar/ical/...",
    "my_website": "https://mybooking.site/export/calendar.ics",
})
# The label (e.g. "airbnb", "vrbo") is just a name YOU choose.
# The library reads any valid .ics URL — it doesn't care which platform.
```

All results from all platforms are **automatically merged** into one unified calendar. One call to `is_available()` checks all of them at once.

---

## Where to Find Your iCal URLs

**Airbnb:**
`Manage Listing → Availability → Export Calendar → Copy Link`

**Booking.com:**
`Property → Calendar → Sync Calendars → Export → iCal Link`

**VRBO / HomeAway:**
`Dashboard → Calendars → Connect → Export`

**Google Calendar:**
`Settings → [Your Calendar] → Integrate calendar → Secret address in iCal format`

---

## Features

| Feature | Details |
|---|---|
| ✅ Multi-platform | Airbnb, Booking.com, VRBO, Google Calendar, any `.ics` feed |
| ✅ You choose | Connect 1 platform or 10 — fully flexible |
| ✅ Built-in caching | Configurable TTL — avoids hammering external APIs |
| ✅ Resilient | Network errors and broken feeds handled gracefully (no crashes) |
| ✅ Fully typed | 100% type hints — full IDE autocomplete |
| ✅ Secure | No `exec()`, no `eval()`, no `os.system()` — clean code only |
| ✅ Django-ready | Drop into any Django view or Celery task in 2 minutes |

---

## Django Integration

```python
# views.py
from ical_rental_sync import CalendarSync
from django.http import JsonResponse

# Initialise once — the built-in cache handles the rest
SYNC = CalendarSync(
    urls={
        "airbnb":  "https://...",
        "booking": "https://...",
    },
    cache_ttl=120,  # cache for 2 minutes
)

def check_availability(request):
    check_in  = request.GET.get("check_in")   # "2026-06-01"
    check_out = request.GET.get("check_out")  # "2026-06-07"
    available = SYNC.is_available(check_in, check_out)
    return JsonResponse({"available": available})

def booked_dates(request):
    booked = SYNC.get_booked_dates()
    return JsonResponse({"booked_dates": [d.isoformat() for d in booked]})
```

### With Celery (background sync every 5 minutes)

```python
# tasks.py
from celery import shared_task
from myapp.views import SYNC

@shared_task
def refresh_calendars():
    """Pre-warm the cache in the background — zero latency for users."""
    SYNC.clear_cache()
    SYNC.get_combined_availability()
```

```python
# settings.py
CELERY_BEAT_SCHEDULE = {
    "refresh-rental-calendars": {
        "task": "myapp.tasks.refresh_calendars",
        "schedule": 300,  # every 5 minutes
    }
}
```

---

## API Reference

### `CalendarSync(urls, *, cache_ttl=60, request_timeout=10)`

| Parameter | Type | Default | Description |
|---|---|---|---|
| `urls` | `dict[str, str]` | required | `{"label": "ical_url"}` — any number of feeds |
| `cache_ttl` | `int` | `60` | Seconds to cache results. `0` = no cache |
| `request_timeout` | `int` | `10` | HTTP timeout in seconds per feed |

### Methods

| Method | Returns | Description |
|---|---|---|
| `is_available(check_in, check_out)` | `bool` | `True` if **no** platform has a booking in that range |
| `get_combined_availability()` | `list[BookedPeriod]` | All blocked periods from all platforms, merged & sorted |
| `get_booked_dates()` | `list[date]` | Flat list of every individual booked date |
| `clear_cache()` | `None` | Invalidate cache — next call fetches fresh data |
| `add_url(label, url)` | `None` | Add a new feed at runtime |
| `remove_url(label)` | `None` | Remove a feed by label |

### `BookedPeriod` object

```python
period.start    # date  — first blocked day
period.end      # date  — first FREE day (half-open interval)
period.source   # str   — the label you gave ("airbnb", "booking", ...)
period.summary  # str   — the event summary from the iCal file
```

---

## Running Tests

```bash
pip install pytest responses
pytest -v
```

---

## Offline Demo (no real URLs needed)

```bash
python test_demo.py
```

Expected output:
```
🎉  All tests passed! Library is working correctly.
```

---

## Created by

**Brigli The Coder** — [brigli-the-coder.github.io/brigli](https://brigli-the-coder.github.io/brigli/)

Built to solve a real problem for rental businesses in Albania and beyond.
Contributions welcome — open an issue or a PR!

---

## License

MIT
