fix: resolve API field mismatches and issue count fetching
- Fix PoolStatus.to_dict() field names to match frontend types (active_count, active_tasks instead of active, tasks) - Fix AgentTaskInfo.to_dict() to use start_time instead of started_at - Fix _get_issues() to use correct YouTrackClient method signature (get_issues_by_state takes project and state, not state and limit) - Fix _get_builds() to use get_running_builds() instead of non-existent get_builds() method - Fix _get_issue_counts() to actually fetch counts from YouTrack API instead of returning all zeros 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -508,33 +508,35 @@ class DashboardAPIHandler(BaseHTTPRequestHandler):
|
||||
|
||||
# Helper methods for external service calls
|
||||
|
||||
def _get_issues(self, states: list[str], limit: int) -> dict:
|
||||
def _get_issues(self, states: list[str], limit: int) -> list:
|
||||
"""Fetch issues from YouTrack."""
|
||||
if not self.runner:
|
||||
return {"issues": [], "counts": {}}
|
||||
return []
|
||||
|
||||
try:
|
||||
issues = []
|
||||
counts = {}
|
||||
project = self.runner.config.get("project", {}).get("name", "CG")
|
||||
|
||||
for state in states:
|
||||
state_issues = self.runner.youtrack.get_issues_by_state(state, limit=limit)
|
||||
counts[state] = len(state_issues)
|
||||
for issue in state_issues:
|
||||
issues.append({
|
||||
"id": issue.id,
|
||||
"summary": issue.summary,
|
||||
"state": state,
|
||||
"priority": issue.priority,
|
||||
"repository": getattr(issue, 'repository', None),
|
||||
"created": getattr(issue, 'created', None),
|
||||
"updated": getattr(issue, 'updated', None),
|
||||
})
|
||||
try:
|
||||
state_issues = self.runner.youtrack.get_issues_by_state(project, state)
|
||||
for issue in state_issues[:limit]: # Apply limit after fetching
|
||||
issues.append({
|
||||
"id": issue.id,
|
||||
"summary": issue.summary,
|
||||
"state": state,
|
||||
"priority": getattr(issue, 'priority', None),
|
||||
"type": getattr(issue, 'type', None),
|
||||
"created": getattr(issue, 'created', None),
|
||||
"updated": getattr(issue, 'updated', None),
|
||||
})
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to fetch issues for state {state}: {e}")
|
||||
|
||||
return {"issues": issues, "counts": counts}
|
||||
return issues
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to fetch issues: {e}")
|
||||
return {"issues": [], "counts": {}, "error": str(e)}
|
||||
return []
|
||||
|
||||
def _get_issue(self, issue_id: str) -> dict:
|
||||
"""Fetch single issue from YouTrack."""
|
||||
@@ -592,45 +594,42 @@ class DashboardAPIHandler(BaseHTTPRequestHandler):
|
||||
# TODO: Implement issue creation
|
||||
return {"success": False, "message": "Issue creation not yet implemented"}
|
||||
|
||||
def _get_builds(self, repo: Optional[str], status: Optional[str], limit: int) -> dict:
|
||||
def _get_builds(self, repo: Optional[str], status: Optional[str], limit: int) -> list:
|
||||
"""Fetch builds from Woodpecker."""
|
||||
if not self.runner:
|
||||
return {"builds": []}
|
||||
if not self.runner or not self.runner.woodpecker:
|
||||
return []
|
||||
|
||||
try:
|
||||
builds = []
|
||||
repos = [repo] if repo else list(self.runner.config.get("repos", {}).keys())
|
||||
|
||||
for repo_name in repos[:3]: # Limit repos to avoid too many API calls
|
||||
repo_config = self.runner.config.get("repos", {}).get(repo_name, {})
|
||||
woodpecker_repo = repo_config.get("name")
|
||||
if not woodpecker_repo:
|
||||
continue
|
||||
# Get running builds
|
||||
try:
|
||||
build_type = repo if repo else None
|
||||
running = self.runner.woodpecker.get_running_builds(build_type)
|
||||
for build in running:
|
||||
builds.append({
|
||||
"id": build.pipeline_id,
|
||||
"number": build.pipeline_id,
|
||||
"status": build.status,
|
||||
"event": "push",
|
||||
"branch": build.branch,
|
||||
"message": build.commit_message or "",
|
||||
"author": build.author or "",
|
||||
"started": int(build.started.timestamp()) if build.started else None,
|
||||
"finished": int(build.finished.timestamp()) if build.finished else None,
|
||||
"created": int(build.started.timestamp()) if build.started else 0,
|
||||
})
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to fetch running builds: {e}")
|
||||
|
||||
try:
|
||||
repo_builds = self.runner.woodpecker.get_builds(
|
||||
woodpecker_repo,
|
||||
limit=limit
|
||||
)
|
||||
for build in repo_builds:
|
||||
if status and build.status.lower() != status.lower():
|
||||
continue
|
||||
builds.append({
|
||||
"build_id": build.id,
|
||||
"repo": repo_name,
|
||||
"branch": build.branch,
|
||||
"status": build.status,
|
||||
"commit": build.commit[:8] if build.commit else "",
|
||||
"started": build.started,
|
||||
"web_url": build.web_url,
|
||||
})
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to fetch builds for {repo_name}: {e}")
|
||||
# Filter by status if requested
|
||||
if status:
|
||||
builds = [b for b in builds if b["status"].lower() == status.lower()]
|
||||
|
||||
return {"builds": builds[:limit]}
|
||||
return builds[:limit]
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to fetch builds: {e}")
|
||||
return {"builds": [], "error": str(e)}
|
||||
return []
|
||||
|
||||
|
||||
def setup_api_handler(runner: "Runner", oauth=None):
|
||||
|
||||
@@ -53,7 +53,7 @@ class AgentTaskInfo:
|
||||
"repo": self.repo,
|
||||
"platform": self.platform,
|
||||
"task_type": self.task_type,
|
||||
"started_at": self.started_at.isoformat() if self.started_at else None,
|
||||
"start_time": self.started_at.isoformat() if self.started_at else None,
|
||||
"elapsed_seconds": round(self.elapsed_seconds, 1),
|
||||
}
|
||||
|
||||
@@ -70,10 +70,10 @@ class PoolStatus:
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"max_agents": self.max_agents,
|
||||
"active": self.active,
|
||||
"active_count": self.active,
|
||||
"available": self.available,
|
||||
"timeout_seconds": self.timeout_seconds,
|
||||
"tasks": [t.to_dict() for t in self.tasks],
|
||||
"active_tasks": [t.to_dict() for t in self.tasks],
|
||||
}
|
||||
|
||||
|
||||
@@ -253,13 +253,19 @@ class DashboardAPI:
|
||||
Consider caching in production.
|
||||
"""
|
||||
states = self._runner.config.get("project", {}).get("states", {})
|
||||
project = self._runner.config.get("project", {}).get("name", "CG")
|
||||
counts = {}
|
||||
|
||||
for state_key, state_name in states.items():
|
||||
# Skip certain states we don't track
|
||||
if state_key in ("triage", "backlog", "done"):
|
||||
continue
|
||||
counts[state_name] = 0 # Default to 0, actual counts fetched on demand
|
||||
try:
|
||||
issues = self._runner.youtrack.get_issues_by_state(project, state_name)
|
||||
counts[state_name] = len(issues)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to get count for state {state_name}: {e}")
|
||||
counts[state_name] = 0
|
||||
|
||||
return counts
|
||||
|
||||
|
||||
Reference in New Issue
Block a user