11from datetime import date , datetime
22from typing import Set , List , Dict
33import os
4- from utils import IssuePackage , REQUEST_REPO , AUTO_ASSIGN_LABEL , AUTO_PARSE_LABEL , get_origin_link_and_tag
4+ from utils import IssuePackage , REQUEST_REPO , AUTO_ASSIGN_LABEL , AUTO_PARSE_LABEL , get_origin_link_and_tag ,\
5+ MULTI_LINK_LABEL
56import re
67import logging
78import time
9+ from random import randint
810from github import Github
911from github .Repository import Repository
10- import subprocess as sp
1112
1213_LOG = logging .getLogger (__name__ )
1314
1415# assignee dict which will be assigned to handle issues
1516_LANGUAGE_OWNER = {'msyyc' }
1617
1718# 'github assignee': 'token'
18- _ASSIGNEE_TOKEN = {'msyyc' : os .getenv ('PYTHON_MSYYC_TOKEN ' )}
19+ _ASSIGNEE_TOKEN = {'msyyc' : os .getenv ('AZURESDK_BOT_TOKEN ' )}
1920
2021_SWAGGER_URL = 'https://github.com/Azure/azure-rest-api-specs/blob/main/specification'
2122_SWAGGER_PULL = 'https://github.com/Azure/azure-rest-api-specs/pull'
2223
2324
24- _SWAGGER_URL = 'https://github.com/Azure/azure-rest-api-specs/blob/main/specification'
25- _SWAGGER_PULL = 'https://github.com/Azure/azure-rest-api-specs/pull'
26-
2725class IssueProcess :
26+ """
2827 # won't be changed anymore after __init__
2928 request_repo_dict = {} # request repo instance generated by different token
3029 owner = '' # issue owner
3130 assignee_candidates = {} # assignee candidates who will be assigned to handle issue
3231 language_owner = {} # language owner who may handle issue
3332
3433 # will be changed by order
35- issue = None # issue that needs to handle
34+ issue_package = None # issue that needs to handle
3635 assignee = ''
37- bot = [] # bot advice to help SDK owner
36+ bot_advice = [] # bot advice to help SDK owner
3837 target_readme_tag = '' # swagger content that customers want
3938 readme_link = '' # https link which swagger definition is in
4039 default_readme_tag = '' # configured in `README.md`
40+ package_name = '' # target package name
41+ target_date = '' # target release date asked by customer
42+ date_from_target = 0
43+ """
4144
42- def __init__ (self , issue : IssuePackage , request_repo_dict : Dict [str , Repository ],
45+ def __init__ (self , issue_package : IssuePackage , request_repo_dict : Dict [str , Repository ],
4346 assignee_candidates : Set [str ], language_owner : Set [str ]):
44- self .issue_package = issue
47+ self .issue_package = issue_package
4548 self .request_repo_dict = request_repo_dict
46- self .assignee = issue .issue .assignee .login
47- self .owner = issue .issue .user .login
49+ self .assignee = issue_package .issue .assignee .login
50+ self .owner = issue_package .issue .user .login
4851 self .assignee_candidates = assignee_candidates
4952 self .language_owner = language_owner
53+ self .bot_advice = []
54+ self .target_readme_tag = ''
55+ self .readme_link = ''
56+ self .default_readme_tag = ''
57+ self .package_name = ''
58+ self .target_date = ''
59+ self .date_from_target = 0
5060
5161 def get_issue_body (self ) -> List [str ]:
5262 return [i for i in self .issue_package .issue .body .split ("\n " ) if i ]
@@ -82,8 +92,7 @@ def get_readme_from_pr_link(self, link: str) -> str:
8292 pr = f"{ _SWAGGER_PULL } /{ pr_number } "
8393 self .comment (
8494 f'Hi, @{ self .assignee } , by parsing { pr } , there are multi service link: { multi_link } . Please decide which one is the right.' )
85-
86- self .bot .append ('multi readme link!' )
95+ self .add_label (MULTI_LINK_LABEL )
8796 raise Exception (f'multi link in "{ pr } "' )
8897
8998 return readme_link [0 ]
@@ -190,7 +199,7 @@ def auto_assign(self) -> None:
190199 return
191200 # assign averagely
192201 assignees = list (self .assignee_candidates )
193- random_idx = int ( str ( time . time ())[ - 1 ]) % len (assignees ) if len (assignees ) > 1 else 0
202+ random_idx = randint ( 0 , len (assignees ) - 1 ) if len (assignees ) > 1 else 0
194203 assignee = assignees [random_idx ]
195204
196205 # update assignee
@@ -203,83 +212,127 @@ def auto_assign(self) -> None:
203212 self .update_issue_instance ()
204213 self .add_label (AUTO_ASSIGN_LABEL )
205214
206- def bot_advice (self ):
207- latest_comments = ''
215+ def new_issue_policy (self ):
216+ new_issue_advice = 'new issue.'
217+ if self .issue_package .issue .comments == 0 :
218+ self .bot_advice .append (new_issue_advice )
219+ else :
220+ # issue that no comment from language owner will also be treated as new issue
221+ comment_from_owner = set (comment .user .login for comment in self .issue_package .issue .get_comments ()
222+ if comment .user .login in self .language_owner )
223+ if not comment_from_owner :
224+ self .bot_advice .append (new_issue_advice )
225+
226+ def new_comment_policy (self ):
227+ if self .issue_package .issue .comments == 0 :
228+ return
208229 comments = [(comment .updated_at .timestamp (), comment .user .login ) for comment in
209230 self .issue_package .issue .get_comments ()]
210231 comments .sort ()
211- if comments :
212- latest_comments = comments [- 1 ][1 ]
213- if self .issue_package .issue .comments == 0 :
214- self .bot = 'new issue ! <br>'
215- elif latest_comments not in self .language_owner :
216- self .bot = 'new comment. <br>'
232+ latest_comments = comments [- 1 ][1 ]
233+ if latest_comments not in self .language_owner :
234+ self .bot_advice .append ('new comment.' )
235+
236+ def multi_link_policy (self ):
237+ if MULTI_LINK_LABEL in self .issue_package .labels_name :
238+ self .bot_advice .append ('multi readme link!' )
239+
240+ def remind_logic (self ) -> bool :
241+ return abs (self .date_from_target ) <= 2
242+
243+ def print_date_from_target_date (self ) -> str :
244+ return str (self .date_from_target ) if self .remind_logic () else ''
245+
246+ def date_remind_policy (self ):
247+ if self .remind_logic ():
248+ self .bot_advice .append ('close to release date. ' )
249+
250+ def auto_bot_advice (self ):
251+ self .new_issue_policy ()
252+ self .new_comment_policy ()
253+ self .multi_link_policy ()
254+ self .date_remind_policy ()
255+
256+ def get_target_date (self ):
257+ body = self .get_issue_body ()
258+ try :
259+ self .target_date = [line .split (':' )[- 1 ].strip () for line in body if 'Target release date' in line ][0 ]
260+ self .date_from_target = int ((time .mktime (time .strptime (self .target_date , '%Y-%m-%d' )) - time .time ()) / 3600 / 24 )
261+ except Exception :
262+ self .target_date = 'fail to get.'
263+ self .date_from_target = 1000 # make a ridiculous data to remind failure when error happens
217264
218265 def run (self ) -> None :
219266 # common part(don't change the order)
220267 self .auto_assign () # necessary flow
221268 self .auto_parse () # necessary flow
222- self .bot_advice ()
269+ self .get_target_date ()
270+ self .auto_bot_advice () # make sure this is the last step
223271
224272
225273class Common :
226- """ The class defines some function for all languages to reference """
274+ """ The class defines some function for all languages to reference
227275 issues_package = None # issues that need to handle
228276 request_repo_dict = {} # request repo instance generated by different token
229277 assignee_candidates = {} # assignee candidates who will be assigned to handle issue
230278 language_owner = {} # language owner who may handle issue
279+ result = []
280+ file_out_name = '' # file that storages issue status
281+ """
231282
232- def __init__ (self , issues : List [IssuePackage ], assignee_token : Dict [str , str ], language_owner : Set [str ]):
233- self .issues_package = issues
283+ def __init__ (self , issues_package : List [IssuePackage ], assignee_token : Dict [str , str ], language_owner : Set [str ]):
284+ self .issues_package = issues_package
234285 self .assignee_candidates = set (assignee_token .keys ())
235286 self .language_owner = language_owner
236287 # arguments add to language.md
237288 self .file_out_name = 'common.md'
238289 self .target_release_date = ''
239290 self .date_from_target = ''
240291 self .package_name = ''
292+ self .result = []
293+ self .request_repo_dict = {}
241294
242295 for assignee in assignee_token :
243296 self .request_repo_dict [assignee ] = Github (assignee_token [assignee ]).get_repo (REQUEST_REPO )
244297
245- def output_md (self , items ):
298+ def output (self ):
246299 with open (self .file_out_name , 'w' ) as file_out :
247- file_out .write (
248- '| issue | author | package | assignee | bot advice | created date of issue | target release date | date from target |\n ' )
300+ file_out .write ('| issue | author | package | assignee | bot advice | created date of issue | target release date | date from target |\n ' )
249301 file_out .write ('| ------ | ------ | ------ | ------ | ------ | ------ | ------ | :-----: |\n ' )
250- file_out .writelines ([self .output_python (item ) for item in items ])
302+ for item in self .result :
303+ try :
304+ item_status = Common .output_md (item )
305+ file_out .write (item_status )
306+ except Exception as e :
307+ _LOG .error (f'Error happened during output result of handled issue { item .issue_package .issue .number } : { e } ' )
251308
252- def output_python (self , item ):
309+ @staticmethod
310+ def output_md (item : IssueProcess ):
253311 create_date = str (date .fromtimestamp (item .issue_package .issue .created_at .timestamp ()).strftime ('%m-%d' ))
312+ target_date = str (datetime .strptime (item .target_date , "%Y-%m-%d" ).strftime ('%m-%d' ))
254313
255314 return '| [#{}]({}) | {} | {} | {} | {} | {} | {} | {} |\n ' .format (
256315 item .issue_package .issue .html_url .split ('/' )[- 1 ],
257316 item .issue_package .issue .html_url ,
258317 item .issue_package .issue .user .login ,
259- self .package_name ,
260- item .issue_package . issue . assignee . login ,
261- item .bot ,
318+ item .package_name ,
319+ item .assignee ,
320+ ' ' . join ( item .bot_advice ) ,
262321 create_date ,
263- self . target_release_date ,
264- self . date_from_target
322+ target_date ,
323+ item . print_date_from_target_date ()
265324 )
266325
267- @staticmethod
268- def push_md_to_storage ():
269- cmd_list = ['git add .' , 'git commit -m \" update excel\" ' , 'git push -f origin HEAD' ]
270- [sp .check_call (cmd , shell = True ) for cmd in cmd_list ]
271-
272326 def run (self ):
273327 items = []
274328 for item in self .issues_package :
275329 issue = IssueProcess (item , self .request_repo_dict , self .assignee_candidates , self .language_owner )
276330 try :
277331 issue .run ()
278- items .append (issue )
332+ self . result .append (issue )
279333 except Exception as e :
280334 _LOG .error (f'Error happened during handling issue { item .issue .number } : { e } ' )
281- self .output_md (items )
282-
335+ self .output ()
283336
284337
285338def common_process (issues : List [IssuePackage ]):
0 commit comments