diff --git a/apps/codecov-api/core/admin.py b/apps/codecov-api/core/admin.py index 1f70a78f61..914af39a6f 100644 --- a/apps/codecov-api/core/admin.py +++ b/apps/codecov-api/core/admin.py @@ -18,6 +18,7 @@ from shared.django_apps.reports.models import ReportType from shared.django_apps.ta_timeseries.models import Testrun from shared.django_apps.timeseries.models import Measurement, MeasurementName +from shared.django_apps.upload_breadcrumbs.models import UploadBreadcrumb from shared.django_apps.utils.paginator import EstimatedCountPaginator from shared.helpers.redis import get_redis_connection from shared.reports.enums import UploadState @@ -270,6 +271,7 @@ class CommitAdmin(AdminMixin, admin.ModelAdmin): "deleted", "notified", "reprocess_actions", + "upload_pipeline_timeline", ) fields = readonly_fields paginator = EstimatedCountPaginator @@ -359,6 +361,84 @@ def reprocess_actions(self, obj): return format_html("
{}
", format_html("".join(buttons))) + @admin.display(description="Upload Pipeline Timeline") + def upload_pipeline_timeline(self, obj): + if obj.pk is None: + return "" + + breadcrumbs = UploadBreadcrumb.objects.filter( + commit_sha=obj.commitid, + repo_id=obj.repository.repoid, + ).order_by("created_at")[:200] + + if not breadcrumbs: + return format_html("No breadcrumbs recorded for this commit.") + + rows = [] + for bc in breadcrumbs: + data = bc.breadcrumb_data or {} + milestone = data.get("milestone", "") + error = data.get("error", "") + error_text = data.get("error_text", "") + task_name = data.get("task_name", "") + parent_task_id = data.get("parent_task_id", "") + endpoint = data.get("endpoint", "") + + if error: + color = "#d32f2f" + elif milestone in ("lac", "lr", "uc", "ns"): + color = "#388e3c" + elif milestone in ("la", "pu", "nt"): + color = "#1565c0" + else: + color = "#555" + + upload_ids_str = ( + ", ".join(str(uid) for uid in bc.upload_ids) if bc.upload_ids else "" + ) + + detail_parts = [] + if milestone: + detail_parts.append(f"{milestone}") + if endpoint: + detail_parts.append(f"endpoint={endpoint}") + if error: + detail_parts.append(f'err={error}') + if error_text: + detail_parts.append( + f'{error_text[:120]}' + ) + detail = " · ".join(detail_parts) if detail_parts else "—" + + rows.append( + f"" + f'' + f"{bc.created_at:%Y-%m-%d %H:%M:%S}" + f'' + f"{task_name}" + f'' + f"{parent_task_id}" + f'{detail}' + f'' + f"{upload_ids_str}" + f"" + ) + + table = ( + '' + "" + '' + '' + '' + '' + '' + "" + "".join(rows) + "
TimeTaskParent Task IDDetailUpload IDs
" + ) + + return format_html( + '
{}
', format_html(table) + ) + def _reprocess_uploads( self, request, commit: Commit, config: ReprocessConfig ) -> HttpResponseRedirect: