Initial commit: AutonetSellCar platform with deployment system
- Frontend: Next.js 14 with TypeScript - Backend: FastAPI with SQLAlchemy - Agent: Carmodoo sync agent - Deployment: Docker Compose based staging/production setup - Scripts: Automated deployment with rollback support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
159
agent/src/sync_agent.py
Normal file
159
agent/src/sync_agent.py
Normal file
@@ -0,0 +1,159 @@
|
||||
"""
|
||||
Carmodoo Sync Agent - Syncs makers/models from Carmodoo to AutonetSellCar Backend
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import logging
|
||||
import httpx
|
||||
from dotenv import load_dotenv
|
||||
from .carmodoo_client import CarmodooClient, CarmodooConfig
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger('sync_agent')
|
||||
|
||||
|
||||
class SyncAgent:
|
||||
def __init__(self):
|
||||
load_dotenv()
|
||||
|
||||
# Carmodoo config
|
||||
self.carmodoo_config = CarmodooConfig(
|
||||
user_id=os.getenv('CARMODOO_USER_ID', ''),
|
||||
password=os.getenv('CARMODOO_PASSWORD', ''),
|
||||
)
|
||||
|
||||
# Backend API
|
||||
self.api_url = os.getenv('API_SERVER_URL', 'http://autonet-backend:8000/api')
|
||||
self.api_key = os.getenv('AGENT_API_KEY', '')
|
||||
|
||||
self.carmodoo: CarmodooClient = None
|
||||
self.http_client: httpx.AsyncClient = None
|
||||
|
||||
async def start(self):
|
||||
"""Initialize connections"""
|
||||
logger.info("Starting Sync Agent...")
|
||||
|
||||
self.carmodoo = CarmodooClient(self.carmodoo_config)
|
||||
await self.carmodoo.create_session()
|
||||
|
||||
self.http_client = httpx.AsyncClient(timeout=30.0)
|
||||
|
||||
# Login to Carmodoo
|
||||
if not await self.carmodoo.login():
|
||||
logger.error("Failed to login to Carmodoo")
|
||||
return False
|
||||
|
||||
logger.info("Sync Agent started successfully")
|
||||
return True
|
||||
|
||||
async def stop(self):
|
||||
"""Cleanup connections"""
|
||||
if self.carmodoo:
|
||||
await self.carmodoo.close()
|
||||
if self.http_client:
|
||||
await self.http_client.aclose()
|
||||
logger.info("Sync Agent stopped")
|
||||
|
||||
async def sync_makers(self):
|
||||
"""Sync car makers from Carmodoo to Backend"""
|
||||
logger.info("Syncing car makers...")
|
||||
|
||||
makers = await self.carmodoo.get_car_makers()
|
||||
logger.info(f"Found {len(makers)} makers from Carmodoo")
|
||||
|
||||
synced = 0
|
||||
for maker in makers:
|
||||
try:
|
||||
response = await self.http_client.post(
|
||||
f"{self.api_url}/cars/makers/",
|
||||
json={
|
||||
"code": maker.code,
|
||||
"name": maker.name,
|
||||
}
|
||||
)
|
||||
if response.status_code in [200, 201]:
|
||||
synced += 1
|
||||
except Exception as e:
|
||||
logger.error(f"Error syncing maker {maker.code}: {e}")
|
||||
|
||||
logger.info(f"Synced {synced}/{len(makers)} makers")
|
||||
return makers
|
||||
|
||||
async def sync_models(self, makers):
|
||||
"""Sync car models for all makers"""
|
||||
logger.info("Syncing car models...")
|
||||
|
||||
total_models = 0
|
||||
synced = 0
|
||||
|
||||
for maker in makers:
|
||||
await asyncio.sleep(0.5) # Rate limiting
|
||||
|
||||
models = await self.carmodoo.get_car_models(maker.code)
|
||||
total_models += len(models)
|
||||
|
||||
# Get maker ID from backend
|
||||
try:
|
||||
response = await self.http_client.get(f"{self.api_url}/cars/makers/")
|
||||
if response.status_code == 200:
|
||||
backend_makers = response.json()
|
||||
maker_id = None
|
||||
for bm in backend_makers:
|
||||
if bm['code'] == maker.code:
|
||||
maker_id = bm['id']
|
||||
break
|
||||
|
||||
if maker_id:
|
||||
for model in models:
|
||||
try:
|
||||
resp = await self.http_client.post(
|
||||
f"{self.api_url}/cars/models/",
|
||||
json={
|
||||
"code": model.code,
|
||||
"maker_id": maker_id,
|
||||
"name": model.name,
|
||||
}
|
||||
)
|
||||
if resp.status_code in [200, 201]:
|
||||
synced += 1
|
||||
except Exception as e:
|
||||
logger.error(f"Error syncing model {model.code}: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting makers from backend: {e}")
|
||||
|
||||
logger.debug(f"Maker {maker.name}: {len(models)} models")
|
||||
|
||||
logger.info(f"Synced {synced}/{total_models} models")
|
||||
|
||||
async def run_sync(self):
|
||||
"""Run full sync"""
|
||||
if not await self.start():
|
||||
return
|
||||
|
||||
try:
|
||||
# Sync makers
|
||||
makers = await self.sync_makers()
|
||||
|
||||
# Sync models
|
||||
await self.sync_models(makers)
|
||||
|
||||
logger.info("Sync completed successfully!")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Sync error: {e}")
|
||||
finally:
|
||||
await self.stop()
|
||||
|
||||
|
||||
async def main():
|
||||
agent = SyncAgent()
|
||||
await agent.run_sync()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user