Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for custom comment message #6

Merged
merged 4 commits into from
Sep 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Produce a document with an overview of your backlog. This can be executed as a script with minimal dependencies or via the provided GitHub Action. The result can be injected into another document such as a README.md or uploaded to a service like GitHub Pages. It is recommended to define a convenient schedule to pull in updates from your issue tracker.

Have a look at the [demo hosted on GitHub Pages](https://kalikiana.github.io/backlogger)!
Have a look at the [demo hosted on GitHub Pages](https://openSUSE.github.io/backlogger)!

## Inputs

Expand Down Expand Up @@ -37,7 +37,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: kalikiana/backlogger@main
- uses: openSUSE/backlogger@main
redmine_api_key: ${{ secrets.REDMINE_API_KEY }}
args: --reminder-comment-on-issues
- uses: JamesIves/github-pages-deploy-action@v4
Expand Down Expand Up @@ -65,7 +65,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: kalikiana/backlogger@main
- uses: openSUSE/backlogger@main
with:
redmine_api_key: ${{ secrets.REDMINE_API_KEY }}
- uses: rossjrw/pr-preview-action@v1
Expand Down
95 changes: 54 additions & 41 deletions backlogger.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,105 +13,118 @@

# Initialize a blank md file to replace the current README
def initialize_md(data):
with open('index.md', 'w') as md:
with open("index.md", "w") as md:
md.write("# Backlog Status\n\n")
md.write("This is the dashboard for [{}]({}).\n".format(data['team'], data['url']))
md.write("**Latest Run:** " + datetime.now().strftime("%Y-%m-%d %H:%M:%S") + " UTC\n")
md.write(
"This is the dashboard for [{}]({}).\n".format(data["team"], data["url"])
)
md.write(
"**Latest Run:** " + datetime.now().strftime("%Y-%m-%d %H:%M:%S") + " UTC\n"
)
md.write("*(Please refresh to see latest results)*\n\n")
md.write("Backlog Query | Number of Issues | Limits | Status\n--- | --- | --- | ---\n")
md.write(
"Backlog Query | Number of Issues | Limits | Status\n--- | --- | --- | ---\n"
)


def get_link(conf):
return data['web'] + '?' + conf['query']
return data["web"] + "?" + conf["query"]


# Append individual results to md file
def results_to_md(conf, number, status):
mdlink = '[' + conf['title'] + '](' + get_link(conf) + ')'
lessthan = conf['max'] + 1
limits = '<' + str(lessthan)
if 'min' in conf:
limits += ', >' + str(conf['min'] - 1)
with open('index.md', 'a') as md:
mdlink = "[" + conf["title"] + "](" + get_link(conf) + ")"
lessthan = conf["max"] + 1
limits = "<" + str(lessthan)
if "min" in conf:
limits += ", >" + str(conf["min"] - 1)
with open("index.md", "a") as md:
md.write(mdlink + " | " + str(number) + " | " + limits + " | " + status + "\n")


def json_rest(method, url, rest=None):
text = json.dumps(rest)
try:
key = os.environ['REDMINE_API_KEY']
key = os.environ["REDMINE_API_KEY"]
except KeyError:
exit('REDMINE_API_KEY is required to be set')
exit("REDMINE_API_KEY is required to be set")
headers = {
'User-Agent': 'backlogger ({})'.format(data['url']),
'Content-Type': 'application/json',
'X-Redmine-API-Key': key,
"User-Agent": "backlogger ({})".format(data["url"]),
"Content-Type": "application/json",
"X-Redmine-API-Key": key,
}
r = requests.request(method, url, data=text, headers=headers)
r.raise_for_status()
return r.json() if r.text else None


def issue_reminder(poo):
priority = poo['priority']['name']
msg = 'This ticket was set to **{priority}** priority but was not updated [within the SLO period]({url}). Please consider picking up this ticket or just set the ticket to the next lower priority.'.format(priority=priority, url=data['url'])
def issue_reminder(conf, poo):
priority = poo["priority"]["name"]
msg = "This ticket was set to **{priority}** priority but was not updated [within the SLO period]({url}). Please consider picking up this ticket or just set the ticket to the next lower priority.".format(
priority=priority, url=data["url"]
)
if "comment" in conf:
msg = conf["comment"]
print(msg)
if '--reminder-comment-on-issues' in sys.argv:
url = '{}/{}.json'.format(data['web'], poo['id'])
json_rest('PUT', url, {'issue': {'notes': msg}})
if "--reminder-comment-on-issues" in sys.argv:
url = "{}/{}.json".format(data["web"], poo["id"])
json_rest("PUT", url, {"issue": {"notes": msg}})


def list_issues(conf, root):
try:
for poo in root['issues']:
print(data['web'] + '/' + str(poo['id']))
if 'updated_on' in conf['query']:
issue_reminder(poo)
for poo in root["issues"]:
print(data["web"] + "/" + str(poo["id"]))
if "updated_on" in conf["query"]:
issue_reminder(conf, poo)
except KeyError:
print("There was an error retrieving the issues " + conf['title'])
print("There was an error retrieving the issues " + conf["title"])
else:
issue_count = int(root["total_count"])
if issue_count > len(root['issues']):
if issue_count > len(root["issues"]):
print("there are more issues, check " + get_link(conf))


def failure_more(conf):
print(conf['title'] + " has more than " + str(conf['max']) + " tickets!")
print(conf["title"] + " has more than " + str(conf["max"]) + " tickets!")
return False


def failure_less(conf):
print(conf['title'] + " has less than " + str(conf['min']) + " tickets!")
print(conf["title"] + " has less than " + str(conf["min"]) + " tickets!")
return False


def check_backlog(conf):
root = json_rest('GET', data['api'] + '?' + conf['query'])
root = json_rest("GET", data["api"] + "?" + conf["query"])
list_issues(conf, root)
issue_count = int(root["total_count"])
if issue_count > conf['max']:
if issue_count > conf["max"]:
res = failure_more(conf)
elif 'min' in conf and issue_count < conf['min']:
elif "min" in conf and issue_count < conf["min"]:
res = failure_less(conf)
else:
res = True
print(conf['title'] + " length is " + str(issue_count) + ", all good!")
print(conf["title"] + " length is " + str(issue_count) + ", all good!")
if not res:
print("Please check " + get_link(conf))
return (res, issue_count)


def check_query(name):
for conf in data['queries']:
def check_query(data):
for conf in data["queries"]:
res = check_backlog(conf)
results_to_md(conf, res[1], result_icons["pass"] if res[0] else result_icons["fail"])
results_to_md(
conf, res[1], result_icons["pass"] if res[0] else result_icons["fail"]
)

if __name__ == '__main__':
filename = sys.argv[1] if len(sys.argv) > 1 else 'queries.yaml'

if __name__ == "__main__":
filename = sys.argv[1] if len(sys.argv) > 1 else "queries.yaml"
try:
with open(filename, 'r') as config:
with open(filename, "r") as config:
data = yaml.safe_load(config)
initialize_md(data)
check_query(data)
except FileNotFoundError:
sys.exit('Configuration file {} not found'.format(filename))
sys.exit("Configuration file {} not found".format(filename))
1 change: 1 addition & 0 deletions queries.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ queries:
min: 11
- title: Untriaged QA
query: query_id=576&project_id=115
comment: This ticket is still untriaged!
max: 0
- title: SLO high (<1 month)
query: query_id=544
Expand Down
27 changes: 19 additions & 8 deletions tests/test_comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,24 @@

class TestComments(unittest.TestCase):
def test_comments(self):
data = {'url': 'https://example.com/issues', 'web': 'https://example.com/wiki'}
data = {"url": "https://example.com/issues", "web": "https://example.com/wiki"}
backlogger.data = data
sys.argv[1:] = ['--reminder-comment-on-issues']
sys.argv[1:] = ["--reminder-comment-on-issues"]
backlogger.json_rest = MagicMock(return_value=None)
backlogger.list_issues({'query': 'query_id=123&c%5B%5D=updated_on'},
{'issues': [{'priority': {'name': 'High'}, 'id': 123}], 'total_count': 1})
backlogger.list_issues({'query': 'query_id=123&'},
{'issues': [{'priority': {'name': 'High'}, 'id': 456}], 'total_count': 1})
backlogger.json_rest.assert_called_once_with('PUT', 'https://example.com/wiki/123.json',
{'issue': {'notes': 'This ticket was set to **High** priority but was not updated [within the SLO period](https://example.com/issues). Please consider picking up this ticket or just set the ticket to the next lower priority.'}})
backlogger.list_issues(
{"query": "query_id=123&c%5B%5D=updated_on"},
{"issues": [{"priority": {"name": "High"}, "id": 123}], "total_count": 1},
)
backlogger.list_issues(
{"query": "query_id=123&"},
{"issues": [{"priority": {"name": "High"}, "id": 456}], "total_count": 1},
)
backlogger.json_rest.assert_called_once_with(
"PUT",
"https://example.com/wiki/123.json",
{
"issue": {
"notes": "This ticket was set to **High** priority but was not updated [within the SLO period](https://example.com/issues). Please consider picking up this ticket or just set the ticket to the next lower priority."
}
},
)