Skip to content
Open
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
101 changes: 101 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# cellartracker

Python package to export data from [CellarTracker](https://www.cellartracker.com), the wine cellar management platform.

[![PyPI](https://img.shields.io/pypi/v/cellartracker.svg)](https://pypi.python.org/pypi/cellartracker)
[![License](https://img.shields.io/pypi/l/cellartracker.svg)](https://github.com/mathroule/cellartracker/blob/main/LICENSE)

## Install

```bash
pip install cellartracker
```

## Quick Start

```python
from cellartracker import cellartracker

ct = cellartracker.CellarTracker('your_handle', 'your_password')

# Get your full inventory
inventory = ct.get_inventory()
print(f"Your cellar: {len(inventory)} bottles")

# First bottle
bottle = inventory[0]
print(f"{bottle['Vintage']} {bottle['Wine']} — ${bottle['Price']}")
```

## Available Methods

### Inventory & Wines
| Method | Returns | Description |
|--------|---------|-------------|
| `get_inventory()` | `list[dict]` | Full inventory with valuations, scores, purchase details |
| `get_list()` | `list[dict]` | Current wine list with stock levels |
| `get_bottles()` | `list[dict]` | Every bottle individually (for state tracking) |
| `get_availability()` | `list[dict]` | Drinkability report with drinking windows |

### Purchase & Consumption
| Method | Returns | Description |
|--------|---------|-------------|
| `get_purchase()` | `list[dict]` | Purchase history with store, price, quantity |
| `get_consumed()` | `list[dict]` | Consumption history |
| `get_pending()` | `list[dict]` | Pending deliveries |

### Reviews & Tags
| Method | Returns | Description |
|--------|---------|-------------|
| `get_notes()` | `list[dict]` | Public tasting notes and ratings |
| `get_private_notes()` | `list[dict]` | Your private tasting notes |
| `get_pro_review()` | `list[dict]` | Professional critic scores |
| `get_tag()` | `list[dict]` | Wish list / tagged wines |
| `get_food_tag()` | `list[dict]` | Food pairing tags |

## Common Fields

Each bottle in `get_inventory()` includes these useful fields:

| Field | Description |
|-------|-------------|
| `iWine` | Unique wine ID (for API operations) |
| `Wine` | Wine name |
| `Vintage` | Vintage year |
| `Producer` | Producer / winery |
| `Varietal` | Grape variety |
| `Country` / `Region` | Origin |
| `Price` | Purchase price |
| `Valuation` | Current market valuation |
| `StoreName` | Where purchased |
| `PurchaseDate` | Date of purchase |
| `Location` | Storage location |
| `Size` | Bottle size (750ml, 1.5L, etc.) |
| `CT` | Community score |
| `CNotes` | Number of community tasting notes |

## CLI Usage

```bash
# Export your inventory as tab-separated data
cellartracker --username=your_handle --password=your_password --table=Inventory

# Export purchase history as CSV
cellartracker --username=your_handle --password=your_password --table=Purchase --format=csv
```

Available tables: `List`, `Inventory`, `Notes`, `PrivateNotes`, `Purchase`, `Pending`, `Consumed`, `Availability`, `Tag`, `ProReview`, `Bottles`, `FoodTag`

Available formats: `tab`, `csv`, `xml`, `html`

## Authentication

Your CellarTracker **handle** is required — this is your username, not your email address. If you sign in at cellartracker.com as "coop789", that's your handle.

## Data Source

This package uses CellarTracker's [official export functionality](https://support.cellartracker.com/article/29-exporting-data) to access your data via the `xlquery.asp` endpoint.

## License

MIT license.
142 changes: 108 additions & 34 deletions cellartracker/cellartracker.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
"""Main module."""
"""Main module for CellarTracker read API.

Provides the CellarTracker class for exporting data from CellarTracker
via the xlquery.asp export endpoint.
"""

import csv
import logging

from io import StringIO
from typing import Any, Optional

from .client import CellarTrackerClient
from .enum import CellarTrackerFormat, CellarTrackerTable
Expand All @@ -11,76 +16,145 @@


class CellarTracker(object):
"""
CellarTracker is the class handling the CellarTracker data export.
"""CellarTracker data export client.

Provides read-only access to CellarTracker data via the official
export endpoint. Each method returns a list of dicts with the
fields available for that table.

Args:
username: CellarTracker handle (not email)
password: CellarTracker account password

Example:
>>> from cellartracker import cellartracker
>>> ct = cellartracker.CellarTracker('myhandle', 'mypassword')
>>> inventory = ct.get_inventory()
>>> len(inventory)
82
>>> inventory[0]['Wine']
'Château Lafite Rothschild'
"""

def __init__(self, username: None, password: None):
def __init__(self, username: str, password: str):
self.client = CellarTrackerClient(username, password)

def get_list(self):
"""Get list."""
def get_list(self) -> list[dict[str, Any]]:
"""Get your current wine list with stock levels.

Returns a list of wines currently in your cellar with
quantity, location, and size information.

Keys include: iWine, Wine, Vintage, Location, Quantity, Size
"""
return self._get_data(table=CellarTrackerTable.List)

def get_inventory(self):
"""Get inventory."""
def get_inventory(self) -> list[dict[str, Any]]:
"""Get full inventory with valuations and purchase details.

The most comprehensive table. Returns all bottles with
pricing, valuation, store, purchase date, tasting scores,
and community stats.

Keys include: iWine, Wine, Vintage, Location, Price, Valuation,
StoreName, PurchaseDate, Size, Varietal, Producer, Country,
CT (community score), and many more.
"""
return self._get_data(table=CellarTrackerTable.Inventory)

def get_notes(self):
"""Get notes."""
def get_notes(self) -> list[dict[str, Any]]:
"""Get public tasting notes.

Returns community tasting notes with ratings.
Keys include: iNote, iWine, Vintage, Wine, Note, Rating, Date
"""
return self._get_data(table=CellarTrackerTable.Notes)

def get_private_notes(self):
"""Get private notes data."""
def get_private_notes(self) -> list[dict[str, Any]]:
"""Get private tasting notes (your notes only)."""
return self._get_data(table=CellarTrackerTable.PrivateNotes)

def get_purchase(self):
"""Get purchase data."""
def get_purchase(self) -> list[dict[str, Any]]:
"""Get purchase history.

Returns all purchase records with store, price, quantity,
and delivery status.

Keys include: iWine, iPurchase, PurchaseDate, StoreName,
Price, Quantity, Remaining, Size, Wine, Vintage
"""
return self._get_data(table=CellarTrackerTable.Purchase)

def get_pending(self):
"""Get pending."""
def get_pending(self) -> list[dict[str, Any]]:
"""Get pending deliveries (ordered but not yet received)."""
return self._get_data(table=CellarTrackerTable.Pending)

def get_consumed(self):
"""Get consumed."""
def get_consumed(self) -> list[dict[str, Any]]:
"""Get consumption history (bottles marked as consumed/drunk).

Keys include: iConsumed, iWine, Vintage, Wine, Consumed, Type
"""
return self._get_data(table=CellarTrackerTable.Consumed)

def get_availability(self):
"""Get availability."""
def get_availability(self) -> list[dict[str, Any]]:
"""Get drinkability report with drinking windows.

Keys include: iWine, Wine, Vintage, Available, BeginConsume,
EndConsume, Type, Color, Varietal
"""
return self._get_data(table=CellarTrackerTable.Availability)

def get_tag(self):
"""Get tag."""
def get_tag(self) -> list[dict[str, Any]]:
"""Get wish list / tagged wines."""
return self._get_data(table=CellarTrackerTable.Tag)

def get_pro_review(self):
"""Get pro review."""
def get_pro_review(self) -> list[dict[str, Any]]:
"""Get professional critic reviews (WA, WS, etc.)."""
return self._get_data(table=CellarTrackerTable.ProReview)

def get_bottles(self):
"""Get bottles."""
def get_bottles(self) -> list[dict[str, Any]]:
"""Get individual bottle records with state tracking.

Returns every bottle individually (vs aggregated by wine).
Keys include: BottleState, Barcode, iWine, Vintage, Wine,
Location, Size

Useful for tracking individual bottle movement and states.
"""
return self._get_data(table=CellarTrackerTable.Bottles)

def get_food_tag(self):
"""Get food tag."""
def get_food_tag(self) -> list[dict[str, Any]]:
"""Get food pairing tags."""
return self._get_data(table=CellarTrackerTable.FoodTag)

def _get_data(self, table: CellarTrackerTable):
"""Get data."""
def _get_data(self, table: CellarTrackerTable) -> list[dict[str, Any]]:
"""Fetch and parse tab-separated data from CellarTracker.

Args:
table: The table to export

Returns:
List of dicts with column names as keys
"""
return _parse_data(
self.client.get(table=table, format=CellarTrackerFormat.tab)
)


def _parse_data(data: str):
def _parse_data(data: str) -> list[dict[str, str]]:
"""Parse tab-separated data from CellarTracker.

Args:
data: Raw tab-separated string from the export API

Returns:
List of dicts with header columns as keys
"""
reader = csv.DictReader(StringIO(data), dialect="excel-tab")
results = []
for row in reader:
result = {}
for key, value in row.items():
result[key] = value

results.append(result)

return results
Loading