187 lines
5 KiB
Markdown
187 lines
5 KiB
Markdown
# AGENTS.md
|
|
|
|
This file provides guidance for AI coding agents working in this repository.
|
|
|
|
## Project Overview
|
|
|
|
Top 500 Albums analysis with data from Rolling Stone (2020) and Wikipedia (2023). Includes Python data processing scripts, an interactive web interface (vanilla HTML/CSS/JS), and album cover artwork from the iTunes API.
|
|
|
|
## Build/Lint/Test Commands
|
|
|
|
**No formal build system.** All Python scripts use only the standard library.
|
|
|
|
```bash
|
|
# Run Python scripts directly
|
|
python scripts/download_all_covers.py
|
|
python scripts/compare_top500_albums.py
|
|
|
|
# Local dev server (required for CORS)
|
|
python -m http.server 8000
|
|
```
|
|
|
|
**No tests or linting configured.** Manual testing only - run scripts and verify output, open website and check functionality.
|
|
|
|
## Code Style Guidelines
|
|
|
|
### Python
|
|
|
|
```python
|
|
#!/usr/bin/env python3
|
|
"""Module docstring explaining the script's purpose."""
|
|
|
|
import csv
|
|
import json
|
|
import os
|
|
import re
|
|
from typing import Dict, List, Optional, Tuple
|
|
|
|
# Constants: UPPER_SNAKE_CASE
|
|
MAX_RETRIES = 3
|
|
|
|
# Functions/variables: snake_case
|
|
def normalize_text(text: str) -> str:
|
|
"""Normalize text for comparison."""
|
|
pass
|
|
|
|
albums_2020 = {}
|
|
```
|
|
|
|
**Key patterns:**
|
|
- Always use shebang: `#!/usr/bin/env python3`
|
|
- Include module-level docstring
|
|
- Standard library only - no external dependencies
|
|
- Type hints for function signatures
|
|
- Use `encoding='utf-8'` and `newline=''` for CSV operations
|
|
|
|
**Error handling:**
|
|
```python
|
|
try:
|
|
with urllib.request.urlopen(url, timeout=15) as response:
|
|
data = json.loads(response.read().decode())
|
|
except Exception as e:
|
|
print(f"Error: {e}")
|
|
return None
|
|
```
|
|
|
|
### JavaScript
|
|
|
|
```javascript
|
|
// Global variables at top
|
|
let albumsData = [];
|
|
const itemsPerPage = 50;
|
|
|
|
// DOM refs cached on load
|
|
const albumsGrid = document.getElementById('albumsGrid');
|
|
|
|
// Initialize on DOMContentLoaded
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadData();
|
|
});
|
|
|
|
// Async/await for fetch
|
|
async function loadAlbumsData() {
|
|
try {
|
|
const response = await fetch('data.csv');
|
|
if (!response.ok) throw new Error('Failed');
|
|
// ...
|
|
} catch (err) {
|
|
console.error('Error:', err);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Key patterns:**
|
|
- Variables/functions: camelCase
|
|
- Use `console.warn()` for non-critical failures
|
|
- Vanilla JS only - no frameworks
|
|
|
|
### HTML/CSS
|
|
|
|
- Semantic HTML5 elements
|
|
- CSS custom properties for theming
|
|
- Responsive design with Grid/Flexbox
|
|
|
|
## Accessibility Requirements
|
|
|
|
All UI changes must follow WCAG 2.1 AA guidelines:
|
|
|
|
**Keyboard Navigation:**
|
|
- All interactive elements must be keyboard accessible
|
|
- Use `tabindex` appropriately (0 for focusable, -1 for programmatic focus)
|
|
- Implement visible focus indicators (`:focus-visible` with `outline`)
|
|
- Support skip links for main content
|
|
|
|
**Screen Readers:**
|
|
- Use semantic HTML (`<button>`, `<nav>`, `<main>`, `<article>`)
|
|
- Add ARIA labels for icon-only buttons: `aria-label="Share album"`
|
|
- Use `.sr-only` class for screen-reader-only text
|
|
- Ensure proper heading hierarchy (`h1` > `h2` > `h3`)
|
|
|
|
**Color & Contrast:**
|
|
- Minimum 4.5:1 contrast ratio for normal text
|
|
- Minimum 3:1 for large text and UI components
|
|
- Never use color alone to convey information
|
|
|
|
**Motion & Animations:**
|
|
- Respect `prefers-reduced-motion` media query
|
|
- Avoid layout shifts on hover (no `transform: translateY()` on cards)
|
|
|
|
**Focus Management:**
|
|
- Trap focus in modals/dialogs
|
|
- Return focus to trigger element when closing overlays
|
|
|
|
**Existing Patterns:**
|
|
```css
|
|
/* Screen reader only */
|
|
.sr-only { position: absolute; width: 1px; height: 1px; ... }
|
|
|
|
/* Focus indicators */
|
|
button:focus-visible { outline: 2px solid var(--primary-color); outline-offset: 2px; }
|
|
|
|
/* Reduced motion */
|
|
@media (prefers-reduced-motion: reduce) { ... }
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
top500albums/
|
|
├── index.html, script.js, styles.css # Website
|
|
├── *.csv, *.json # Data files
|
|
├── covers/ # Album artwork (500 images)
|
|
└── scripts/ # Python utilities (36 scripts)
|
|
```
|
|
|
|
## Data Files
|
|
|
|
| File | Notes |
|
|
|------|-------|
|
|
| `rolling_stone_top_500_albums_2020.csv` | Title case columns, ranks in reverse order |
|
|
| `wikipedia_top_500_albums.csv` | Lowercase columns (`rank`, `artist`, `album`) |
|
|
| `top_500_albums_2023.csv` | Combined comparison file |
|
|
|
|
## Key Patterns
|
|
|
|
**CSV column naming:**
|
|
- Rolling Stone/Combined: Title case (`Rank`, `Artist`, `Album`)
|
|
- Wikipedia: lowercase (`rank`, `artist`, `album`)
|
|
|
|
**Album matching:** Uses `difflib` for fuzzy matching variations like "The Beatles" vs "Beatles"
|
|
|
|
**API rate limiting:** `time.sleep(1.2)` between requests
|
|
|
|
## Common Tasks
|
|
|
|
**New Python script:**
|
|
1. Create in `scripts/`, add shebang + docstring
|
|
2. Standard library only
|
|
3. Include `if __name__ == "__main__":` block
|
|
|
|
**Working with album data:**
|
|
```python
|
|
import csv
|
|
with open('top_500_albums_2023.csv', 'r', encoding='utf-8') as f:
|
|
for row in csv.DictReader(f):
|
|
rank, artist, album = int(row['Rank']), row['Artist'], row['Album']
|
|
status = row['Status'] # "New in 2023", "+10", "-5", "No change"
|
|
```
|