mirror of
https://github.com/YunoHost/tartiflette.git
synced 2024-09-03 20:06:08 +02:00
Initial commit
This commit is contained in:
commit
56aa387b5d
5 changed files with 339 additions and 0 deletions
32
README.md
Normal file
32
README.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
Aiguillette
|
||||
===========
|
||||
|
||||
A quick and dirty PR dashboard using Eden UI
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
- Clone this repo
|
||||
- Download and unzip Eden UI into www : http://scripteden.com/download/eden-ui-bootstrap-3-skin/
|
||||
- Install dependencies :
|
||||
```
|
||||
apt install -y python3-pip
|
||||
pip3 install ansi2html
|
||||
```
|
||||
- Make your web browser serve www/index.html
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
```
|
||||
./fetch.py
|
||||
./analyze.py
|
||||
./publish.py
|
||||
```
|
||||
|
||||
- Edit fetch.py and analyze.py to custom list of repo to fetch (should be moved
|
||||
to a central conf file)
|
||||
- Don't know the number of API calls someone is allowed to do, so limit the call
|
||||
to fetch.py :/
|
||||
- Everything is pretty dirty so far and should be cleaned
|
||||
|
91
analyze.py
Executable file
91
analyze.py
Executable file
|
@ -0,0 +1,91 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import json
|
||||
import datetime
|
||||
|
||||
repos = ["yunohost", "yunohost-admin", "SSOwat", "moulinette", "doc", "ynh-dev",
|
||||
"apps", "CI_package_check", "example_ynh", "package_linter", "Simone",
|
||||
"project-organization", "build.yunohost.org", "dynette", "YunoPorts",
|
||||
"rebuildd", "cd_build", "install_script"]
|
||||
|
||||
|
||||
prs = {}
|
||||
|
||||
|
||||
def githubDateToDaysAgo(date):
|
||||
now = datetime.datetime.now()
|
||||
date = datetime.datetime.strptime(date, "%Y-%m-%dT%H:%M:%SZ")
|
||||
return (now - date).days
|
||||
|
||||
|
||||
def isPRDying(pr):
|
||||
return (pr["createdDaysAgo"] > 60 and pr["updatedDaysAgo"] > 30)
|
||||
|
||||
|
||||
def priority(pr):
|
||||
if "important" in pr["labels"]:
|
||||
base_priority = 100
|
||||
elif "opinion needed" in pr["labels"]:
|
||||
base_priority = 50
|
||||
elif "work needed" in pr["labels"]:
|
||||
base_priority = -50
|
||||
elif "postponed" in pr["labels"]:
|
||||
base_priority = -100
|
||||
elif "inactive" in pr["labels"]:
|
||||
base_priority = -100
|
||||
else:
|
||||
base_priority = 0
|
||||
|
||||
if "dying" in pr["labels"] and base_priority > -100:
|
||||
base_priority += 5
|
||||
|
||||
return base_priority
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
for repo in repos:
|
||||
|
||||
print("Analyzing %s ..." % repo)
|
||||
|
||||
with open("./%s.json" % repo, "r") as f:
|
||||
j = json.loads(f.read())
|
||||
|
||||
for issue in j:
|
||||
|
||||
# Ignore non-pullrequest issues
|
||||
if "pull_request" not in issue.keys():
|
||||
continue
|
||||
|
||||
pr = {
|
||||
"repo": repo,
|
||||
"title": issue["title"],
|
||||
"labels": [label["name"] for label in issue["labels"]],
|
||||
"id": "%s-%s" % (repo, issue["number"]),
|
||||
"createdDaysAgo": githubDateToDaysAgo(issue["created_at"]),
|
||||
"updatedDaysAgo": githubDateToDaysAgo(issue["updated_at"]),
|
||||
"url": issue["pull_request"]["html_url"]
|
||||
}
|
||||
|
||||
if len(pr["title"]) > 53:
|
||||
pr["title"] = pr["title"][0:50] + "..."
|
||||
|
||||
if isPRDying(pr):
|
||||
pr["labels"].append("dying")
|
||||
pr["priority"] = priority(pr)
|
||||
|
||||
prs[pr["id"]] = pr
|
||||
|
||||
prs_sorted = sorted(prs.keys(), key=lambda x: (prs[x]["priority"],
|
||||
prs[x]["createdDaysAgo"]), reverse=True )
|
||||
|
||||
summary = []
|
||||
|
||||
for name in prs_sorted:
|
||||
summary.append(prs[name])
|
||||
|
||||
with open("summary.json", "w") as f:
|
||||
f.write(json.dumps(summary))
|
||||
|
||||
|
||||
main()
|
18
fetch.py
Executable file
18
fetch.py
Executable file
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import requests
|
||||
|
||||
repos = ["yunohost", "yunohost-admin", "SSOwat", "moulinette", "doc", "ynh-dev",
|
||||
"apps", "CI_package_check", "example_ynh", "package_linter", "Simone",
|
||||
"project-organization", "build.yunohost.org", "dynette", "YunoPorts",
|
||||
"rebuildd", "cd_build", "install_script"]
|
||||
|
||||
|
||||
for repo in repos:
|
||||
|
||||
print("Fetching pull requests for %s" % repo)
|
||||
|
||||
issues = requests.get("https://api.github.com/repos/yunohost/%s/issues?per_page=100" % repo)
|
||||
|
||||
with open("./%s.json" % repo, "w") as f:
|
||||
f.write(issues.text)
|
44
publish.py
Executable file
44
publish.py
Executable file
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import os
|
||||
import json
|
||||
|
||||
from jinja2 import Template
|
||||
from ansi2html import Ansi2HTMLConverter
|
||||
from ansi2html.style import get_styles
|
||||
|
||||
###############################################################################
|
||||
|
||||
output_dir = "./www/"
|
||||
|
||||
template_path = os.path.join(output_dir,"template.html")
|
||||
output_path = os.path.join(output_dir,"index.html")
|
||||
|
||||
summary_path = os.path.join("./", "summary.json")
|
||||
|
||||
###############################################################################
|
||||
|
||||
conv = Ansi2HTMLConverter()
|
||||
shell_css = "\n".join(map(str, get_styles(conv.dark_bg, conv.scheme)))
|
||||
|
||||
def shell_to_html(shell):
|
||||
return conv.convert(shell, False)
|
||||
|
||||
###############################################################################
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
# Fetch the list of all reports, sorted in reverse-chronological order
|
||||
|
||||
pr_list = json.load(open(summary_path))
|
||||
|
||||
# Generate the output using the template
|
||||
|
||||
template = open(template_path, "r").read()
|
||||
t = Template(template)
|
||||
|
||||
result = t.render(data=pr_list, convert=shell_to_html, shell_css=shell_css)
|
||||
|
||||
open(output_path, "w").write(result)
|
||||
|
||||
print("Done.")
|
154
www/template.html
Normal file
154
www/template.html
Normal file
|
@ -0,0 +1,154 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta charset="utf-8">
|
||||
<title>Bootstrap Skin: Eden</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="stylesheet" href="css/bootstrap.css" media="screen">
|
||||
<link rel="stylesheet" href="skins/eden.css" media="screen">
|
||||
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link href="css/animate.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
.navbar-holder-dark{
|
||||
padding: 20px 20px 200px 20px;
|
||||
background: #333333;
|
||||
}
|
||||
.container {
|
||||
width:1500px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="page-header" id="banner">
|
||||
<div class="row">
|
||||
<h1>Yunohost Pull Requests Dashboard</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>WTF is this shit? How is priority defined?</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
First with labels :
|
||||
<p>"important" implies top-priority ------
|
||||
"opinion-needed" implies medium priority ------
|
||||
"work needed" implies low priority ------
|
||||
"inactive" / "postponed" implies very low priority.</p>
|
||||
Then with creation time :
|
||||
<p>older PRs have higher priority than newer PR</p>
|
||||
'Dying' status
|
||||
<p>A PR is considered "dying" if it has been created more than 60
|
||||
days ago and not updated since 30 days. This is meant to be an incentive to
|
||||
either revive it or flag it as inactive/postponed... 'Dying' PRs
|
||||
have a small boost in priority to be listed first</a>
|
||||
</br>
|
||||
<p>Repos considered for now (completely arbitrary) : [yunohost, yunohost-admin, SSOwat, moulinette, doc, ynh-dev,
|
||||
apps, CI_package_check, example_ynh, package_linter, Simone,
|
||||
project-organization, build.yunohost.org, dynette, YunoPorts,
|
||||
rebuildd, cd_build, install_script]</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>What should you review ?</h3>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-offset-3 col-md-6">
|
||||
<div class="bs-component">
|
||||
<ul class="nav nav-pills">
|
||||
<li class="active"><a href="#">All <span class="badge">1664</span></a></li>
|
||||
<li><a href="#">Core <span class="badge">42</span></a></li>
|
||||
<li><a href="#">Apps <span class="badge">42</span></a/li>
|
||||
<li><a href="#">Infra / dist<span class="badge">42</span></a></li>
|
||||
<li><a href="#">Doc / i18n<span class="badge">42</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
<div>
|
||||
|
||||
|
||||
<table class="table table-striped table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Title</th>
|
||||
<th>Created</th>
|
||||
<th>Labels</th>
|
||||
<th>Reviews</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for pr in data %}
|
||||
<tr>
|
||||
<td><center><a class="btn
|
||||
{% if pr.priority >= 100 %}btn-warning{% else %}
|
||||
{% if pr.priority >= 50 %}btn-primary{% else %}
|
||||
{% if pr.priority >= 0 %}btn-info{% else %}
|
||||
{% if pr.priority >= -50 %}btn-default{% else %}
|
||||
btn-link{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}"
|
||||
href="{{ pr.url }}">{{ pr.id }}</a></center></td>
|
||||
<td><strong>{{ pr.title }}</strong></td>
|
||||
<td>{{ pr.createdDaysAgo }} days ago</td>
|
||||
<td>
|
||||
{% for label in pr.labels %}
|
||||
<span class="label
|
||||
{% if label == "important" %}label-danger{%endif%}
|
||||
{% if label == "opinion needed" %}label-warning{%endif%}
|
||||
{% if label == "small decision" %}label-info{%endif%}
|
||||
{% if label == "medium decision" %}label-info{%endif%}
|
||||
{% if label == "big decision" %}label-info{%endif%}
|
||||
{% if label == "work needed" %}label-primary{%endif%}
|
||||
{% if label == "inactive" %}label-default{%endif%}
|
||||
{% if label == "postponed" %}label-default{%endif%}
|
||||
{% if label == "dying" %}label-danger{%endif%}
|
||||
">{{ label }}</span>
|
||||
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>
|
||||
<span style="border:1px solid black; border-radius:4px; padding-left:2px; padding-right:2px;">foo <span class="text-success">✔</span></span>
|
||||
<span style="border:1px solid black; border-radius:4px; padding-left:2px; padding-right:2px;">bar <span class="text-danger" >✘</span></span>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<footer>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<hr/>
|
||||
<p>CSS Skin/boilerplate/whatever you call it : <a href="http://scripteden.com/" rel="nofollow">Script Eden</a>.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</footer>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue