Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
5420076
Implement vim-style dark theme for documentation
knowlen Jul 13, 2025
51b6b1c
Fix vim theme: improve spacing and match exact code colors
knowlen Jul 13, 2025
cced134
Fix code syntax highlighting colors to match desired theme
knowlen Jul 13, 2025
8158424
Attempt to fix syntax highlighting with correct VS Code colors
knowlen Jul 13, 2025
4f265b1
Fix syntax highlighting with correct Pygments classes
knowlen Jul 14, 2025
5979300
Update API coverage to 83% and refine vim dark theme
knowlen Jul 14, 2025
bf3e056
Complete documentation formatting and logo integration
knowlen Jul 14, 2025
9db3c51
Make README logo span full page width
knowlen Jul 14, 2025
4d9b011
Replace logos with AI-processed transparent backgrounds
knowlen Jul 14, 2025
0e6f5ac
Add logo to docs hero section and fix README banner
knowlen Jul 14, 2025
aed0a0f
Add CSS architecture docs, explicit font-display, and motion preferences
knowlen Jul 15, 2025
04e4de0
Update API reference tables to use code formatting for parameter/fiel…
knowlen Jul 15, 2025
a8aaaac
Fix API reference table column widths to prevent code snippet wrapping
knowlen Jul 15, 2025
8950700
Fix status badge wrapping and improve navigation tab hover styling
knowlen Jul 15, 2025
56af482
Revert navigation tab sizing but keep vim visual mode colors
knowlen Jul 15, 2025
703e33d
Add MkDocs performance optimizations: lazy loading, DOM pruning, enha…
knowlen Jul 15, 2025
1e98dbb
Fix minify plugin configuration - remove non-existent file references
knowlen Jul 15, 2025
21a7c90
Update text selection colors to match navigation hover (light grey bg…
knowlen Jul 15, 2025
b0334bb
Remove non-functional cookie settings link and disable analytics
knowlen Jul 15, 2025
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
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
<div align="center">
<img src="docs/assets/logo.png" alt="ESO Logs Python" width="300">
</div>

# ESO Logs Python Client

[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
Expand All @@ -9,26 +13,26 @@ A comprehensive Python client library for the [ESO Logs API v2](https://www.esol

## Project Status

**Current Version:** 0.2.0-alpha
**API Coverage:** ~83% (comprehensive analysis shows 6/8 API sections fully implemented)
**Development Stage:** Active development
**Documentation:** [Read the Docs](https://esologs-python.readthedocs.io/)
**Current Version:** 0.2.0-alpha
**API Coverage:** ~83% (comprehensive analysis shows 6/8 API sections fully implemented)
**Development Stage:** Active development
**Documentation:** [Read the Docs](https://esologs-python.readthedocs.io/)
**Tests:** 278 tests across unit, integration, documentation, and sanity suites

### Current API Coverage
**Implemented (6/8 sections):**
1. βœ… **gameData** - 13 methods
1. βœ… **gameData** - 13 methods
2. βœ… **characterData** - 5 methods
3. βœ… **reportData** - 9 methods
3. βœ… **reportData** - 9 methods
4. βœ… **worldData** - 4 methods
5. βœ… **rateLimitData** - 1 method
5. βœ… **rateLimitData** - 1 method
6. 🟑 **guildData** - 2 methods (PARTIAL - missing 4 advanced methods)

**Missing (2/8 sections):**
- ❌ **userData** - 0/3 methods (MISSING - requires user auth)
- ❌ **progressRaceData** - 0/1 method (MISSING - niche racing feature)

### Roadmap
### Roadmap
- 🚧 Progress race tracking
- 🚧 User account integration
- 🚧 Client architecture refactor (modular design)
Expand Down
152 changes: 76 additions & 76 deletions docs/api-reference/character-data.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Character Data API
# Character Data

Access ESO character profiles, reports, and performance data through the ESO Logs API.
Enables the retrieval of single characters or filtered collections of characters. Eg; Character profiles, reports, and performance data

## Overview

Expand All @@ -16,20 +16,20 @@ Access ESO character profiles, reports, and performance data through the ESO Log

| Parameters | Type | Required | Description |
|-----------|------|----------|-------------|
| id | int | Yes | The character ID to retrieve |
| `id` | *int* | Yes | The character ID to retrieve |

**Returns**: `GetCharacterById` object with the following structure:

| Field | Type | Description |
|-------|------|-------------|
| character_data.character.id | int | Character ID |
| character_data.character.name | str | Character name |
| character_data.character.class_id | int | Character class ID |
| character_data.character.race_id | int | Character race ID |
| character_data.character.guild_rank | int | Guild rank (0 if not in guild) |
| character_data.character.hidden | bool | Whether character profile is hidden |
| character_data.character.server.name | str | Server name |
| character_data.character.server.region.name | str | Server region name |
| `character_data.character.id` | *int* | Character ID |
| `character_data.character.name` | *str* | Character name |
| `character_data.character.class_id` | *int* | Character class ID |
| `character_data.character.race_id` | *int* | Character race ID |
| `character_data.character.guild_rank` | *int* | Guild rank (0 if not in guild) |
| `character_data.character.hidden` | *bool* | Whether character profile is hidden |
| `character_data.character.server.name` | *str* | Server name |
| `character_data.character.server.region.name` | *str* | Server region name |

**Example**:
```python
Expand All @@ -43,9 +43,9 @@ async def get_character_profile():
url="https://www.esologs.com/api/v2/client",
headers={"Authorization": f"Bearer {token}"}
) as client:

character = await client.get_character_by_id(id=314050)

if character.character_data and character.character_data.character:
char = character.character_data.character
print(f"Character: {char.name} (ID: {char.id})")
Expand All @@ -70,30 +70,30 @@ Guild Rank: 0

| Parameters | Type | Required | Description |
|-----------|------|----------|-------------|
| character_id | int | Yes | The character ID to get reports for |
| limit | int | No | Number of reports to return (default: 10) |
| `character_id` | *int* | Yes | The character ID to get reports for |
| `limit` | *int* | No | Number of reports to return (default: 10) |

**Returns**: `GetCharacterReports` object with the following structure:

| Field | Type | Description |
|-------|------|-------------|
| character_data.character.recent_reports.data | List[Report] | List of report objects |
| character_data.character.recent_reports.total | int | Total number of reports |
| character_data.character.recent_reports.per_page | int | Reports per page |
| character_data.character.recent_reports.current_page | int | Current page number |
| character_data.character.recent_reports.from_ | int \| None | Starting record number |
| character_data.character.recent_reports.to | int \| None | Ending record number |
| character_data.character.recent_reports.last_page | int | Last page number |
| character_data.character.recent_reports.has_more_pages | bool | Whether more pages exist |
| `character_data.character.recent_reports.data` | *List[Report]* | List of report objects |
| `character_data.character.recent_reports.total` | *int* | Total number of reports |
| `character_data.character.recent_reports.per_page` | *int* | Reports per page |
| `character_data.character.recent_reports.current_page` | *int* | Current page number |
| `character_data.character.recent_reports.from_` | *int \| None* | Starting record number |
| `character_data.character.recent_reports.to` | *int \| None* | Ending record number |
| `character_data.character.recent_reports.last_page` | *int* | Last page number |
| `character_data.character.recent_reports.has_more_pages` | *bool* | Whether more pages exist |

**Report Object Fields**:

| Field | Type | Description |
|-------|------|-------------|
| code | str | Unique report code |
| start_time | float | Report start timestamp |
| end_time | float | Report end timestamp |
| zone.name | str | Zone name where report was recorded |
| `code` | *str* | Unique report code |
| `start_time` | *float* | Report start timestamp |
| `end_time` | *float* | Report end timestamp |
| `zone.name` | *str* | Zone name where report was recorded |

**Example**:
```python
Expand All @@ -107,15 +107,15 @@ async def get_character_recent_reports():
url="https://www.esologs.com/api/v2/client",
headers={"Authorization": f"Bearer {token}"}
) as client:

reports = await client.get_character_reports(character_id=314050, limit=5)

if reports.character_data and reports.character_data.character:
recent_reports = reports.character_data.character.recent_reports
if recent_reports:
print(f"Total reports: {recent_reports.total}")
print(f"Showing {len(recent_reports.data)} reports:")

for report in recent_reports.data:
if report:
zone_name = report.zone.name if report.zone else "Unknown Zone"
Expand All @@ -138,14 +138,14 @@ Showing 5 reports:

| Parameters | Type | Required | Description |
|-----------|------|----------|-------------|
| character_id | int | Yes | The character ID |
| encounter_id | int | Yes | The encounter ID to get rankings for |
| `character_id` | *int* | Yes | The character ID |
| `encounter_id` | *int* | Yes | The encounter ID to get rankings for |

**Returns**: `GetCharacterEncounterRanking` object with the following structure:

| Field | Type | Description |
|-------|------|-------------|
| character_data.character.encounter_rankings | Any | Rankings data (structure varies by encounter) |
| `character_data.character.encounter_rankings` | *Any* | Rankings data (structure varies by encounter) |

**Example**:
```python
Expand All @@ -159,12 +159,12 @@ async def get_character_encounter_ranking():
url="https://www.esologs.com/api/v2/client",
headers={"Authorization": f"Bearer {token}"}
) as client:

ranking = await client.get_character_encounter_ranking(
character_id=314050,
encounter_id=63 # Rockgrove encounter
)

if ranking.character_data and ranking.character_data.character:
rankings = ranking.character_data.character.encounter_rankings
if rankings:
Expand All @@ -188,26 +188,26 @@ Available data: ['bestAmount', 'medianPerformance', 'averagePerformance', 'total

| Parameters | Type | Required | Description |
|-----------|------|----------|-------------|
| character_id | int | Yes | The character ID |
| encounter_id | int | Yes | The encounter ID to get rankings for |
| by_bracket | bool | No | Group rankings by bracket |
| class_name | str | No | Filter by class name |
| compare | RankingCompareType | No | Comparison type for rankings |
| difficulty | int | No | Difficulty level filter |
| include_combatant_info | bool | No | Include combatant information |
| include_private_logs | bool | No | Include private logs in rankings |
| metric | CharacterRankingMetricType | No | Ranking metric type |
| partition | int | No | Partition number |
| role | RoleType | No | Role filter (Tank, Healer, DPS) |
| size | int | No | Number of results to return |
| spec_name | str | No | Specialization name filter |
| timeframe | RankingTimeframeType | No | Time period for rankings |
| `character_id` | *int* | Yes | The character ID |
| `encounter_id` | *int* | Yes | The encounter ID to get rankings for |
| `by_bracket` | *bool* | No | Group rankings by bracket |
| `class_name` | *str* | No | Filter by class name |
| `compare` | *RankingCompareType* | No | Comparison type for rankings |
| `difficulty` | *int* | No | Difficulty level filter |
| `include_combatant_info` | *bool* | No | Include combatant information |
| `include_private_logs` | *bool* | No | Include private logs in rankings |
| `metric` | *CharacterRankingMetricType* | No | Ranking metric type |
| `partition` | *int* | No | Partition number |
| `role` | *RoleType* | No | Role filter (Tank, Healer, DPS) |
| `size` | *int* | No | Number of results to return |
| `spec_name` | *str* | No | Specialization name filter |
| `timeframe` | *RankingTimeframeType* | No | Time period for rankings |

**Returns**: `GetCharacterEncounterRankings` object with the following structure:

| Field | Type | Description |
|-------|------|-------------|
| character_data.character.encounter_rankings | Any | Detailed rankings data with filters applied |
| `character_data.character.encounter_rankings` | *Any* | Detailed rankings data with filters applied |

**Example**:
```python
Expand All @@ -221,13 +221,13 @@ async def get_character_encounter_rankings():
url="https://www.esologs.com/api/v2/client",
headers={"Authorization": f"Bearer {token}"}
) as client:

rankings = await client.get_character_encounter_rankings(
character_id=314050,
encounter_id=63, # Rockgrove encounter
include_combatant_info=True
)

if rankings.character_data and rankings.character_data.character:
encounter_rankings = rankings.character_data.character.encounter_rankings
if encounter_rankings:
Expand Down Expand Up @@ -261,25 +261,25 @@ Rank percentile: 68.0%

| Parameters | Type | Required | Description |
|-----------|------|----------|-------------|
| character_id | int | Yes | The character ID |
| zone_id | int | No | The zone ID to get rankings for |
| by_bracket | bool | No | Group rankings by bracket |
| class_name | str | No | Filter by class name |
| compare | RankingCompareType | No | Comparison type for rankings |
| difficulty | int | No | Difficulty level filter |
| include_private_logs | bool | No | Include private logs in rankings |
| metric | CharacterRankingMetricType | No | Ranking metric type |
| partition | int | No | Partition number |
| role | RoleType | No | Role filter (Tank, Healer, DPS) |
| size | int | No | Number of results to return |
| spec_name | str | No | Specialization name filter |
| timeframe | RankingTimeframeType | No | Time period for rankings |
| `character_id` | *int* | Yes | The character ID |
| `zone_id` | *int* | No | The zone ID to get rankings for |
| `by_bracket` | *bool* | No | Group rankings by bracket |
| `class_name` | *str* | No | Filter by class name |
| `compare` | *RankingCompareType* | No | Comparison type for rankings |
| `difficulty` | *int* | No | Difficulty level filter |
| `include_private_logs` | *bool* | No | Include private logs in rankings |
| `metric` | *CharacterRankingMetricType* | No | Ranking metric type |
| `partition` | *int* | No | Partition number |
| `role` | *RoleType* | No | Role filter (Tank, Healer, DPS) |
| `size` | *int* | No | Number of results to return |
| `spec_name` | *str* | No | Specialization name filter |
| `timeframe` | *RankingTimeframeType* | No | Time period for rankings |

**Returns**: `GetCharacterZoneRankings` object with the following structure:

| Field | Type | Description |
|-------|------|-------------|
| character_data.character.zone_rankings | Any | Zone-specific rankings data with filters applied |
| `character_data.character.zone_rankings` | *Any* | Zone-specific rankings data with filters applied |

**Example**:
```python
Expand All @@ -293,13 +293,13 @@ async def get_character_zone_rankings():
url="https://www.esologs.com/api/v2/client",
headers={"Authorization": f"Bearer {token}"}
) as client:

rankings = await client.get_character_zone_rankings(
character_id=314050,
zone_id=19, # Ossein Cage zone
size=5
)

if rankings.character_data and rankings.character_data.character:
zone_rankings = rankings.character_data.character.zone_rankings
if zone_rankings:
Expand Down Expand Up @@ -333,15 +333,15 @@ async def analyze_character(character_id: int):
url="https://www.esologs.com/api/v2/client",
headers={"Authorization": f"Bearer {token}"}
) as client:

# Get character profile
character = await client.get_character_by_id(id=character_id)

if character.character_data and character.character_data.character:
char = character.character_data.character
print(f"Analyzing: {char.name}")
print(f"Server: {char.server.name} ({char.server.region.name})")

# Get recent reports
reports = await client.get_character_reports(character_id=character_id)
if reports.character_data and reports.character_data.character:
Expand Down Expand Up @@ -374,14 +374,14 @@ async def track_character_performance(character_id: int, encounter_id: int):
url="https://www.esologs.com/api/v2/client",
headers={"Authorization": f"Bearer {token}"}
) as client:

# Get encounter rankings
rankings = await client.get_character_encounter_rankings(
character_id=character_id,
encounter_id=encounter_id,
include_combatant_info=True
)

if rankings.character_data and rankings.character_data.character:
encounter_rankings = rankings.character_data.character.encounter_rankings
if encounter_rankings:
Expand All @@ -391,7 +391,7 @@ async def track_character_performance(character_id: int, encounter_id: int):
print(f"Difficulty: {encounter_rankings.get('difficulty', 'Unknown')}")
ranks = encounter_rankings.get('ranks', [])
print(f"Number of ranking entries: {len(ranks)}")

if ranks:
# Show recent performance trend
recent_scores = [rank.get('amount', 0) for rank in ranks[:3]]
Expand Down Expand Up @@ -438,7 +438,7 @@ except GraphQLClientHttpError as e:
## Rate Limiting Notes

- Character profile requests: 2-3 points
- Character reports: 3-4 points
- Character reports: 3-4 points
- Character rankings: 4-5 points
- Add delays between requests: `await asyncio.sleep(0.2)`
- Monitor rate limits using `get_rate_limit_data()`
- Monitor rate limits using `get_rate_limit_data()`
Loading