k
This commit is contained in:
192
docs/FILTERING.md
Normal file
192
docs/FILTERING.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# Filtering Guide
|
||||
|
||||
Universal filter syntax for all queryable objects in PY-Tarot.
|
||||
|
||||
## Basic Filtering (AND Logic)
|
||||
|
||||
Multiple filters use AND logic - all conditions must match.
|
||||
|
||||
### Cards - Basic
|
||||
|
||||
```python
|
||||
from tarot import Tarot
|
||||
|
||||
# Single filter
|
||||
Tarot.deck.card.filter(suit="Cups")
|
||||
# → All Cups cards (1-14)
|
||||
|
||||
# Multiple filters (AND)
|
||||
Tarot.deck.card.filter(suit="Cups", type="Court")
|
||||
# → Knight, Prince, Princess, Queen of Cups (4 cards)
|
||||
|
||||
# Filter by pip number
|
||||
Tarot.deck.card.filter(pip=3)
|
||||
# → 3 of Cups, Pentacles, Swords, Wands (4 cards)
|
||||
|
||||
# Filter Aces
|
||||
Tarot.deck.card.filter(type="Ace")
|
||||
# → Ace of Cups, Pentacles, Swords, Wands (4 cards)
|
||||
|
||||
# Filter specific court rank
|
||||
Tarot.deck.card.filter(court_rank="Knight")
|
||||
# → All 4 Knights (all suits)
|
||||
|
||||
# Combine: Knights of Cups
|
||||
Tarot.deck.card.filter(court_rank="Knight", suit="Cups")
|
||||
# → Knight of Cups (1 card)
|
||||
```
|
||||
|
||||
### Cards - By Card Type
|
||||
|
||||
```python
|
||||
# All Major Arcana
|
||||
Tarot.deck.card.filter(type="Major")
|
||||
# → The Fool through The World (22 cards)
|
||||
|
||||
# All Court Cards
|
||||
Tarot.deck.card.filter(type="Court")
|
||||
# → 16 cards (4 ranks × 4 suits)
|
||||
|
||||
# All Pips (2-10)
|
||||
Tarot.deck.card.filter(type="Pip")
|
||||
# → 36 cards
|
||||
|
||||
# All Aces
|
||||
Tarot.deck.card.filter(type="Ace")
|
||||
# → 4 cards
|
||||
|
||||
# Specific pip type + suit
|
||||
Tarot.deck.card.filter(type="Pip", pip=5, suit="Wands")
|
||||
# → 5 of Wands (1 card)
|
||||
```
|
||||
|
||||
### Cards - Court Cards
|
||||
|
||||
```python
|
||||
# All Knights
|
||||
Tarot.deck.card.filter(type="Court", court_rank="Knight")
|
||||
# → Knight of Cups, Pentacles, Swords, Wands (4 cards)
|
||||
|
||||
# All Queens
|
||||
Tarot.deck.card.filter(type="Court", court_rank="Queen")
|
||||
# → Queen of Cups, Pentacles, Swords, Wands (4 cards)
|
||||
|
||||
# Queen of specific element
|
||||
Tarot.deck.card.filter(type="Court", court_rank="Queen", suit="Wands")
|
||||
# → Queen of Wands (1 card)
|
||||
|
||||
# All Princes
|
||||
Tarot.deck.card.filter(court_rank="Prince")
|
||||
# → Prince of Cups, Pentacles, Swords, Wands (4 cards)
|
||||
|
||||
# All Princesses
|
||||
Tarot.deck.card.filter(court_rank="Princess")
|
||||
# → Princess of Cups, Pentacles, Swords, Wands (4 cards)
|
||||
```
|
||||
|
||||
## Advanced Filtering
|
||||
|
||||
### Multiple Values (OR Logic)
|
||||
|
||||
Use `filter()` multiple times and combine results:
|
||||
|
||||
```python
|
||||
# Aces OR Pips with value 3
|
||||
aces = Tarot.deck.card.filter(type="Ace")
|
||||
threes = Tarot.deck.card.filter(pip=3)
|
||||
result = aces + threes
|
||||
# → Ace of all suits + 3 of all suits (8 cards)
|
||||
|
||||
# Wands OR Pentacles
|
||||
wands = Tarot.deck.card.filter(suit="Wands")
|
||||
pentacles = Tarot.deck.card.filter(suit="Pentacles")
|
||||
result = wands + pentacles
|
||||
# → All Wands + all Pentacles (28 cards)
|
||||
|
||||
# Knights OR Queens
|
||||
knights = Tarot.deck.card.filter(court_rank="Knight")
|
||||
queens = Tarot.deck.card.filter(court_rank="Queen")
|
||||
result = knights + queens
|
||||
# → All Knights + all Queens (8 cards)
|
||||
```
|
||||
|
||||
### Complex Queries
|
||||
|
||||
```python
|
||||
# All Cups court cards
|
||||
cups_court = Tarot.deck.card.filter(suit="Cups", type="Court")
|
||||
# → Knight, Prince, Princess, Queen of Cups (4 cards)
|
||||
|
||||
# All water element (Cups and lower pips)
|
||||
water_cards = Tarot.deck.card.filter(suit="Cups")
|
||||
# → All 14 Cups cards
|
||||
|
||||
# Fire element court cards
|
||||
fire_court = Tarot.deck.card.filter(suit="Wands", type="Court")
|
||||
# → Knight, Prince, Princess, Queen of Wands (4 cards)
|
||||
|
||||
# All numbered cards from 2-10 (pips) in specific suits
|
||||
fives_in_water_earth = (
|
||||
Tarot.deck.card.filter(pip=5, suit="Cups") +
|
||||
Tarot.deck.card.filter(pip=5, suit="Pentacles")
|
||||
)
|
||||
# → 5 of Cups + 5 of Pentacles (2 cards)
|
||||
```
|
||||
|
||||
## Available Filter Fields
|
||||
|
||||
### All Cards
|
||||
- `type` → "Major", "Pip", "Ace", "Court"
|
||||
- `arcana` → "Major", "Minor"
|
||||
- `number` → Card's position in deck (1-78)
|
||||
- `name` → Full card name (case-insensitive)
|
||||
|
||||
### Minor Arcana Only
|
||||
- `suit` → "Cups", "Pentacles", "Swords", "Wands"
|
||||
- `pip` → 1-10 (1 for Ace, 2-10 for pips)
|
||||
- `court_rank` → "Knight", "Prince", "Princess", "Queen"
|
||||
|
||||
### Court Cards Only
|
||||
- `court_rank` → "Knight", "Prince", "Princess", "Queen"
|
||||
- `associated_element` → Element object for the court rank
|
||||
|
||||
### Major Arcana Only
|
||||
- `kabbalistic_number` → 0-21
|
||||
|
||||
## Display Results
|
||||
|
||||
```python
|
||||
# Print as formatted list
|
||||
cards = Tarot.deck.card.filter(suit="Cups")
|
||||
print(cards)
|
||||
|
||||
# Print nicely formatted
|
||||
cards_str = Tarot.deck.card.display_filter(suit="Cups")
|
||||
print(cards_str)
|
||||
|
||||
# Access individual cards
|
||||
cups = Tarot.deck.card.filter(suit="Cups")
|
||||
first_cup = cups[0] # Ace of Cups
|
||||
print(f"{first_cup.number}. {first_cup.name}")
|
||||
```
|
||||
|
||||
## Case Sensitivity
|
||||
|
||||
All filters are case-insensitive:
|
||||
|
||||
```python
|
||||
Tarot.deck.card.filter(suit="cups") # Works
|
||||
Tarot.deck.card.filter(suit="CUPS") # Works
|
||||
Tarot.deck.card.filter(suit="Cups") # Works
|
||||
Tarot.deck.card.filter(type="ace") # Works
|
||||
Tarot.deck.card.filter(type="ACE") # Works
|
||||
Tarot.deck.card.filter(type="Ace") # Works
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
- Use `type` to filter by card class (Major, Pip, Ace, Court)
|
||||
- Use `suit` to filter by element (Cups/Water, Pentacles/Earth, Swords/Air, Wands/Fire)
|
||||
- Multiple kwargs = AND logic
|
||||
- For OR logic, call `filter()` separately and combine lists with `+`
|
||||
- All string comparisons are case-insensitive
|
||||
110
docs/REGISTRY_MAPPING.md
Normal file
110
docs/REGISTRY_MAPPING.md
Normal file
@@ -0,0 +1,110 @@
|
||||
## Registry System: Position-Based Card Details
|
||||
|
||||
### Overview
|
||||
The registry uses a **position-based lookup system** that maps card positions (1-78) to interpretive data stored in `CardDetailsRegistry`. This system is independent of card names, allowing the same registry entries to work across different deck variants.
|
||||
|
||||
### Design Principle
|
||||
**Card position is the permanent identifier**, not the card name. This means:
|
||||
- Card #44 is always "Magus position" in the deck order
|
||||
- If you rename card #44 to "Magus", "Magician", "Mage", or any other variant, it still maps to the same registry entry
|
||||
- Different deck variants can have completely different names but use the same spiritual/interpretive data
|
||||
|
||||
### File Organization
|
||||
Your deck files follow this numbering (which drives card position):
|
||||
- **1-14**: Cups (Ace, Ten, 2-9, Knight, Prince, Princess, Queen)
|
||||
- **15-28**: Pentacles/Disks (same structure)
|
||||
- **29-42**: Swords (same structure)
|
||||
- **43-64**: Major Arcana (43=Fool, 44=Magus, ..., 64=Universe)
|
||||
- **65-78**: Wands (same structure)
|
||||
|
||||
Example: `44_Magus.webp` → Card at position 44 → Magus name → Registry position 44 → Details for position 44
|
||||
|
||||
### Position-Based Lookup Process
|
||||
|
||||
```
|
||||
Card created with number (position)
|
||||
↓
|
||||
card.number = 44
|
||||
↓
|
||||
load_into_card(card)
|
||||
↓
|
||||
get_by_position(44)
|
||||
↓
|
||||
Position 44 maps to registry key "I" (1st trump after Fool)
|
||||
↓
|
||||
registry.get("I") → Returns Magician/Magus details
|
||||
↓
|
||||
Details loaded into card object (independent of card.name)
|
||||
```
|
||||
|
||||
### Position Mapping
|
||||
|
||||
**Minor Arcana Positions:**
|
||||
- 1-14: Cups - Maps to "Ace of Cups" through "Queen of Cups"
|
||||
- 15-28: Pentacles - Maps to "Ace of Pentacles" through "Queen of Pentacles"
|
||||
- 29-42: Swords - Maps to "Ace of Swords" through "Queen of Swords"
|
||||
- 65-78: Wands - Maps to "Ace of Wands" through "Queen of Wands"
|
||||
|
||||
**Major Arcana Positions:**
|
||||
- 43 → "o" (Roman for 0/Fool)
|
||||
- 44 → "I" (Roman for 1/Magus)
|
||||
- 45 → "II" (Roman for 2)
|
||||
- ...continuing through...
|
||||
- 64 → "XXI" (Roman for 21/Universe)
|
||||
|
||||
### Implementation
|
||||
|
||||
```python
|
||||
def _build_position_map(self) -> Dict[int, str]:
|
||||
"""
|
||||
Maps card position (1-78) to registry key:
|
||||
- Minor Arcana: position → card name ("Ace of Cups", etc.)
|
||||
- Major Arcana: position → Roman numeral ("o", "I", "II", etc.)
|
||||
"""
|
||||
# Builds complete 1-78 mapping
|
||||
return position_map
|
||||
|
||||
def get_by_position(self, position: int) -> Optional[Dict[str, Any]]:
|
||||
"""Get details for a card by its position (1-78)."""
|
||||
registry_key = self._position_map.get(position)
|
||||
return self._details.get(registry_key)
|
||||
|
||||
def load_into_card(self, card: 'Card') -> bool:
|
||||
"""Load card details using position-based lookup."""
|
||||
details = self.get_by_position(card.number)
|
||||
# Populate card with: explanation, interpretation, keywords, etc.
|
||||
```
|
||||
|
||||
### Why Position-Based?
|
||||
|
||||
1. **Deck Variant Independence**: Different decks can use completely different names
|
||||
2. **Stable Identity**: Card position never changes across variants
|
||||
3. **Scalable**: Easily support new deck variants by just changing card names
|
||||
4. **Future Proof**: New interpretations can be added keyed to positions, not names
|
||||
|
||||
### Example Flow
|
||||
|
||||
File: `44_Magus.webp`
|
||||
↓
|
||||
Card object: number=44, name="Magus", arcana="Major"
|
||||
↓
|
||||
`load_into_card(card)` called
|
||||
↓
|
||||
`get_by_position(44)` returns registry key "I"
|
||||
↓
|
||||
Registry lookup: `registry.get("I")`
|
||||
↓
|
||||
Populates card with Magician/Magus interpretation:
|
||||
- keywords: ["manifestation", "resourcefulness", ...]
|
||||
- interpretation: "Communication; Conscious Will; ..."
|
||||
- guidance: "Focus your energy and intention..."
|
||||
|
||||
### Key Point
|
||||
|
||||
Even if you rename card #44 to something completely different, it will still load the same interpretation because the lookup is based on **position (44)**, not **name ("Magus")**.
|
||||
|
||||
↓
|
||||
Registry lookup: `registry.get("o")`
|
||||
↓
|
||||
Populates card with: explanation, interpretation, keywords, etc.
|
||||
|
||||
Reference in New Issue
Block a user