|
| 1 | +import time |
| 2 | +import os |
| 3 | +import re |
| 4 | +from github import Github |
| 5 | +from datetime import date, datetime |
| 6 | +import subprocess as sp |
| 7 | +from azure.storage.blob import BlobClient |
| 8 | + |
| 9 | +_NULL = ' ' |
| 10 | +_FILE_OUT = 'release_issue_status.csv' |
| 11 | + |
| 12 | + |
| 13 | +def my_print(cmd): |
| 14 | + print('==' + cmd + ' ==\n') |
| 15 | + |
| 16 | + |
| 17 | +def print_check(cmd): |
| 18 | + my_print(cmd) |
| 19 | + sp.check_call(cmd, shell=True) |
| 20 | + |
| 21 | + |
| 22 | +class IssueStatus: |
| 23 | + link = _NULL |
| 24 | + author = _NULL |
| 25 | + package = _NULL |
| 26 | + create_date = 0.0 |
| 27 | + delay_from_create_date = 0 |
| 28 | + latest_update = 0.0 |
| 29 | + delay_from_latest_update = 0 |
| 30 | + status = 'confirm' |
| 31 | + bot_advice = _NULL |
| 32 | + comment_num = 0 |
| 33 | + language = _NULL |
| 34 | + author_latest_comment = _NULL |
| 35 | + |
| 36 | + def output(self): |
| 37 | + return '{},{},{},{},{},{},{},{},{},{}\n'.format(self.language, self.link, self.author, |
| 38 | + self.package, |
| 39 | + str(date.fromtimestamp(self.create_date)), |
| 40 | + self.delay_from_create_date, |
| 41 | + str(date.fromtimestamp(self.latest_update)), |
| 42 | + self.delay_from_latest_update, |
| 43 | + self.status, self.bot_advice) |
| 44 | + |
| 45 | + |
| 46 | +def _extract(str_list, key_word): |
| 47 | + for item in str_list: |
| 48 | + if re.fullmatch(key_word, item): |
| 49 | + return item.strip() |
| 50 | + return _NULL |
| 51 | + |
| 52 | + |
| 53 | +def _time_format_transform(time_gmt): |
| 54 | + return str(datetime.strptime(time_gmt, '%a, %d %b %Y %H:%M:%S GMT')) |
| 55 | + |
| 56 | + |
| 57 | +def _judge_status(labels): |
| 58 | + for label in labels: |
| 59 | + if label.name == 'release': |
| 60 | + return 'release' |
| 61 | + return 'confirm' |
| 62 | + |
| 63 | + |
| 64 | +def _extract_language(labels): |
| 65 | + language = {'Python', 'JS', 'Go', 'Java', 'Ruby'} |
| 66 | + label_set = {label.name for label in labels} |
| 67 | + intersect = language.intersection(label_set) |
| 68 | + return _NULL if not intersect else intersect.pop() |
| 69 | + |
| 70 | + |
| 71 | +def _key_select(item): |
| 72 | + return item.package |
| 73 | + |
| 74 | + |
| 75 | +def _extract_author_latest_comment(comments): |
| 76 | + q = [(comment.updated_at.timestamp(), comment.user.login) for comment in comments] |
| 77 | + q.sort() |
| 78 | + |
| 79 | + return _NULL if not q else q[-1][1] |
| 80 | + |
| 81 | + |
| 82 | +def main(): |
| 83 | + # get latest issue status |
| 84 | + g = Github(os.getenv('TOKEN')) # please fill user_token |
| 85 | + repo = g.get_repo('Azure/sdk-release-request') |
| 86 | + label1 = repo.get_label('ManagementPlane') |
| 87 | + open_issues = repo.get_issues(state='open', labels=[label1]) |
| 88 | + issue_status = [] |
| 89 | + duplicated_issue = dict() |
| 90 | + start_time = time.time() |
| 91 | + for item in open_issues: |
| 92 | + if not item.number: |
| 93 | + continue |
| 94 | + issue = IssueStatus() |
| 95 | + issue.link = f'https://github.com/Azure/sdk-release-request/issues/{item.number}' |
| 96 | + issue.author = item.user.login |
| 97 | + issue.package = _extract(item.body.split('\n'), 'azure-.*') |
| 98 | + issue.create_date = item.created_at.timestamp() |
| 99 | + issue.delay_from_create_date = int((time.time() - item.created_at.timestamp()) / 3600 / 24) |
| 100 | + issue.latest_update = item.updated_at.timestamp() |
| 101 | + issue.delay_from_latest_update = int((time.time() - item.updated_at.timestamp()) / 3600 / 24) |
| 102 | + issue.status = _judge_status(item.labels) |
| 103 | + issue.comment_num = item.comments |
| 104 | + issue.language = _extract_language(item.labels) |
| 105 | + issue.author_latest_comment = _extract_author_latest_comment(item.get_comments()) |
| 106 | + |
| 107 | + issue_status.append(issue) |
| 108 | + key = (issue.language, issue.package) |
| 109 | + duplicated_issue[key] = duplicated_issue.get(key, 0) + 1 |
| 110 | + |
| 111 | + my_print('Have cost {} seconds'.format(int(time.time() - start_time))) |
| 112 | + |
| 113 | + # rule1: if status is 'release', need to release asap |
| 114 | + # rule2: if latest comment is from author, need response asap |
| 115 | + # rule3: if comment num is 0, it is new issue, better to deal with it asap |
| 116 | + # rule4: if delay from latest update is over 7 days, better to deal with it soon. |
| 117 | + # rule5: if delay from created date is over 30 days, better to close. |
| 118 | + for item in issue_status: |
| 119 | + if item.status == 'release': |
| 120 | + item.bot_advice = 'better to release asap.' |
| 121 | + elif item.author == item.author_latest_comment: |
| 122 | + item.bot_advice = 'new comment for author.' |
| 123 | + elif item.comment_num == 0: |
| 124 | + item.bot_advice = 'new issue and better to confirm quickly.' |
| 125 | + elif item.delay_from_latest_update >= 7: |
| 126 | + item.bot_advice = 'delay for a long time and better to handle now.' |
| 127 | + elif item.delay_from_create_date >= 30: |
| 128 | + item.bot_advice = 'delay for a month and better to close.' |
| 129 | + |
| 130 | + # judge whether there is duplicated issue for same package |
| 131 | + if item.package != _NULL and duplicated_issue.get((item.language, item.package)) > 1: |
| 132 | + item.bot_advice = f'Warning:There is duplicated issue for {item.package}. ' + item.bot_advice |
| 133 | + |
| 134 | + # output result |
| 135 | + with open(_FILE_OUT, 'w') as file_out: |
| 136 | + file_out.write('language,issue,author,package,created date,delay from created date,latest update time,' |
| 137 | + 'delay from latest update,status,bot advice\n') |
| 138 | + file_out.writelines([item.output() for item in sorted(issue_status, key=_key_select)]) |
| 139 | + |
| 140 | + # commit to github |
| 141 | + print_check('git add .') |
| 142 | + print_check('git commit -m \"update excel\"') |
| 143 | + print_check('git push -f origin HEAD') |
| 144 | + |
| 145 | + # upload to storage account(it is created in advance) |
| 146 | + blob = BlobClient.from_connection_string(conn_str=os.getenv('CONN_STR'), container_name=os.getenv('FILE'), |
| 147 | + blob_name=_FILE_OUT) |
| 148 | + with open(_FILE_OUT, 'rb') as data: |
| 149 | + blob.upload_blob(data, overwrite=True) |
| 150 | + |
| 151 | + |
| 152 | +if __name__ == '__main__': |
| 153 | + main() |
0 commit comments