Expert guide for the Kružna Karta Hrvatska full-stack monorepo with advanced mapping, geocoding, and web scraping
Expert assistant for developing the "Kružna Karta Hrvatska" Croatian events platform - a sophisticated full-stack monorepo with advanced mapping, real-time geocoding, and intelligent web scraping capabilities.
This skill provides comprehensive guidance for working with a complex event aggregation platform that:
**Frontend:**
**Backend:**
**Database:**
```bash
make setup # Initial project setup
make dev # Start full development environment
make frontend-dev # Frontend only (port 3001)
make backend-dev # Backend only
```
```bash
make logs # View all service logs
make frontend-logs # Frontend logs only
make backend-logs # Backend logs only
make db-shell # PostgreSQL shell access
make redis-shell # Redis CLI access
```
```bash
make lint # Run all linters
make test # Run all tests with coverage
make frontend-test # Vitest + React Testing Library
make backend-test # pytest with fixtures
```
```bash
make migrate # Apply pending migrations
make migration # Create new migration with name
make db-reset # Reset database with sample data
```
**Multi-Provider Strategy:**
1. Check venue coordinate cache (30-day refresh)
2. Query Croatian geographic database (70+ cities with coordinates)
3. Fallback to Mapbox API
4. Fallback to Nominatim (OpenStreetMap)
5. Croatian city center coordinates as ultimate fallback
**Croatian Geographic Coverage:**
**Implementation:**
```python
from app.config.components import get_settings
from app.core.geocoding_service import GeocodingService
CONFIG = get_settings()
geocoder = GeocodingService()
coordinates = await geocoder.geocode_venue("Koncertna dvorana Vatroslav Lisinski, Zagreb")
```
**14+ Specialized Scrapers:**
**Scraping Architecture:**
**Adding New Scraper:**
1. Create scraper in `backend/app/scraping/scrapers/new_source_scraper.py`
2. Inherit from `BaseScraper` and implement required methods
3. Add URL patterns to scraper registry
4. Test with `make scrape-test`
5. Update documentation
**Mapbox GL JS Integration:**
**Performance Optimizations:**
**Map Component Usage:**
```typescript
import { EventMap } from '@/components/map/EventMap';
import { useEventClustering } from '@/hooks/useEventClustering';
// In component
const { clusters, bounds } = useEventClustering(events, zoom, mapBounds);
<EventMap
events={events}
onEventSelect={handleEventSelect}
initialCenter={[15.9819, 45.8150]} // Zagreb coordinates
initialZoom={12}
/>
```
**Centralized Configuration:**
```python
from app.config.components import get_settings
CONFIG = get_settings() # Fully type-safe Pydantic settings
database_url = CONFIG.database.url
geocoding_provider = CONFIG.services.geocoding.provider
scraping_config = CONFIG.scraping
redis_url = CONFIG.redis.url
```
**Configuration Features:**
**Component Organization:**
**Performance Best Practices:**
**File Organization:**
```
backend/app/
├── config/components.py # Centralized type-safe config
├── core/geocoding_service.py # Geocoding and location services
├── core/croatian_geo_db.py # Croatian geographic database
├── scraping/base_scraper.py # Base scraper architecture
├── scraping/*_scraper.py # Specialized scrapers
├── models/schemas.py # Pydantic schemas
└── migrations/versions/ # Alembic migrations
```
**API Development:**
1. Add routes in `backend/app/api/routes/`
2. Use dependency injection for database access
3. Define Pydantic schemas in `backend/app/schemas/`
4. Follow existing error handling patterns
**Enhanced Event API:**
**Creating Migrations:**
```bash
make migration name="add_new_feature"
make migrate
make db-reset
```
**Migration Best Practices:**
```env
VITE_MAPBOX_ACCESS_TOKEN=your_mapbox_token
VITE_API_BASE_URL=http://localhost:8000
```
```env
DATABASE_URL=postgresql://user:pass@localhost:5432/kruzna_karta
REDIS_URL=redis://localhost:6379/0
MAPBOX_ACCESS_TOKEN=your_mapbox_token
GEOCODING_PROVIDER=mapbox # or nominatim
```
```env
FRONTEND_PORT=3001
BACKEND_PORT=8000
POSTGRES_PORT=5432
REDIS_PORT=6379
```
**Port Conflicts:**
**Database Connection:**
**Map Not Rendering:**
**Geocoding Failures:**
**Scraping Issues:**
```bash
make help # Show all 40+ available commands
make dev # Start full stack
make logs # View all service logs
make db-shell # Access PostgreSQL
make redis-shell # Access Redis CLI
make lint # Run all linters
make test # Run all tests
```
**Frontend Testing:**
```bash
make frontend-test # Vitest + React Testing Library
cd frontend-new && npm run test:watch # Watch mode
```
**Backend Testing:**
```bash
make backend-test # pytest with coverage
cd backend && pytest -v # Verbose mode
cd backend && pytest tests/test_scrapers.py # Specific test file
```
**Docker Compose:**
**Monitoring:**
```python
from app.scraping.base_scraper import BaseScraper
from typing import List, Dict, Any
class NewVenueScraper(BaseScraper):
def __init__(self):
super().__init__("new-venue", "https://newvenue.hr")
async def scrape(self) -> List[Dict[str, Any]]:
"""Scrape events from New Venue website."""
html = await self.fetch_page(f"{self.base_url}/events")
soup = self.parse_html(html)
events = []
for event_elem in soup.select(".event-card"):
event = {
"title": event_elem.select_one(".title").text.strip(),
"date": self.parse_croatian_date(event_elem.select_one(".date").text),
"location": event_elem.select_one(".location").text.strip(),
"url": self.clean_url(event_elem.select_one("a")["href"]),
}
events.append(event)
return events
```
```typescript
// frontend-new/src/hooks/useCustomClustering.ts
import { useMemo } from 'react';
import { Event } from '@/types/event';
import { calculateDistance } from '@/utils/geo';
export function useCustomClustering(
events: Event[],
zoom: number,
threshold: number = 50
) {
return useMemo(() => {
// Clustering algorithm implementation
const clusters = [];
const processed = new Set();
events.forEach((event, idx) => {
if (processed.has(idx)) return;
const cluster = [event];
processed.add(idx);
events.forEach((other, otherIdx) => {
if (processed.has(otherIdx)) return;
const distance = calculateDistance(
event.latitude,
event.longitude,
other.latitude,
other.longitude
);
if (distance < threshold / zoom) {
cluster.push(other);
processed.add(otherIdx);
}
});
clusters.push(cluster);
});
return clusters;
}, [events, zoom, threshold]);
}
```
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/croatian-events-platform-development-assistant/raw