Skip to content

Commit

Permalink
frontend: add failed-to-succeeded-stats command
Browse files Browse the repository at this point in the history
Fix #2779
  • Loading branch information
FrostyX committed Jun 29, 2023
1 parent 02aa009 commit 1d55828
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 0 deletions.
147 changes: 147 additions & 0 deletions frontend/coprs_frontend/commands/failed_to_succeeded_stats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
"""
Generate failed to succeeded stats
"""

import os
from datetime import datetime

import click
import pygal
from coprs import models


@click.command()
@click.option("--dest", "-D", required=True, help="Result directory")
def failed_to_succeeded_stats(dest):
"""
Generate failed to succeeded stats
"""
print("Please wait, this will take at least 20 minutes.")
categories = {
"immediately": 0,
"seconds": 0,
"minutes": 0,
"hours": 0,
"days": 0,
"weeks": 0,
}
tuples = failed_to_succeeded_tuples()
for failed, succeeded in tuples:
delta = datetime.fromtimestamp(succeeded) \
- datetime.fromtimestamp(failed)
categories[delta_to_category(delta)] += 1

os.makedirs(dest, exist_ok=True)
path = os.path.join(dest, "failed-to-succeeded-stats.svg")
generate_graph(categories, path)
print("Created: {0}".format(path))


def get_builds():
"""
Return list of all builds
"""
query = (
models.Build.query
.join(models.Package)
.join(models.Copr)
.order_by(models.Build.id)
)

# Packit user
# query = query.filter(models.Copr.user_id==5576)

# For faster development
# query = query.limit(1000)

return query.all()


def builds_per_package():
"""
Return a `dict` where keys are package IDs and values are lists
of all their builds.
"""
builds = get_builds()
result = {}
for build in builds:
result.setdefault(build.package_id, [])
result[build.package_id].append(build)
return result


def failed_to_succeeded_tuples():
"""
Return a list of tuples. First value of each tuple is when the package
failed, and the second value is when it succeeded.
"""
tuples = []
for builds in builds_per_package().values():
if len(builds) <= 1:
# This package has only one build
# Not dealing with this now.
continue

failed = None
succeeded = None

for build in builds:
if not build.ended_on:
continue

if build.state == "failed" and not failed:
failed = build

elif build.state == "succeeded" and failed:
succeeded = build

if failed and succeeded:
assert failed.id < succeeded.id
tuples.append((failed.ended_on, succeeded.ended_on))
failed = None
succeeded = None
return tuples


def delta_to_category(delta):
"""
Convert timedelta into a custom time category
"""
seconds = delta.total_seconds()
if seconds < 0:
return "immediately"
if seconds < 60:
return "seconds"
if seconds < 60 * 60:
return "minutes"
if seconds < 60 * 60 * 24:
return "hours"
if seconds < 60 * 60 * 24 * 7:
return "days"
return "weeks"


def generate_graph(data, path):
"""
Generate graph from the data
"""
title = "How long before devs submit a successfull package after a failure?"
chart = pygal.Bar(
title=title,
print_values=True,
legend_at_bottom=True,
)
for key, value in data.items():
label = label_for_group(key)
chart.add(label, value)
chart.render_to_file(path)
return path


def label_for_group(key):
"""
User-friendly labels for the graph
"""
if key == "immediately":
return "Before it finished"
return key.capitalize()
2 changes: 2 additions & 0 deletions frontend/coprs_frontend/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import commands.chroots_template
import commands.warning_banner
import commands.usage_treemap
import commands.failed_to_succeeded_stats

from coprs import app

Expand Down Expand Up @@ -94,6 +95,7 @@
"delete_dirs",
"warning_banner",
"usage_treemap",
"failed_to_succeeded_stats",
]


Expand Down

0 comments on commit 1d55828

Please sign in to comment.