diff --git a/eessi_bot_event_handler.py b/eessi_bot_event_handler.py index 7cd45a57..66cb6977 100644 --- a/eessi_bot_event_handler.py +++ b/eessi_bot_event_handler.py @@ -592,6 +592,55 @@ def handle_bot_command_status(self, event_info, bot_command): pr_number = event_info['raw_request_body']['issue']['number'] status_table = request_bot_build_issue_comments(repo_name, pr_number) + if 'last_build' in bot_command.general_args: + # If the bot command is something like 'bot:status =last_build', then only retain the last build for each + # architecture in the status_table + # To do this, we first insert a timestamp to facilitate sorting by time + # Then, we obtain sorting indices that first sort by architecture, then by build time + # Then, we reverse the sorting, so that the last build (highest timestamp) for each archictecture occurs + # first. + # Finally, we copy the table, but each time we encounter an entry for an architecture that we've already + # copied, we ignore it, since - as a result of the sorting - the second entry is always older than the + # first + dates = status_table['date'] + timestamps = [] + for date in dates: + date_object = datetime.strptime(date, "%b %d %X %Z %Y") + timestamps.append(int(date_object.timestamp())) + status_table['timestamp'] = timestamps + + # Figure out the sorting indices, so that things are sorted first by the 'for arch', and then by 'date' + sorted_indices = sorted( + range(len(status_table['for arch'])), + key=lambda x: (status_table['for arch'][x], status_table['timestamp'][x]) + ) + # Reverse, so that the newest builds are first + sorted_indices.reverse() + # Apply the sorted indices to get a sorted table + sorted_table = {key: [status_table[key][i] for i in sorted_indices] for key in status_table} + self.log(f"Sorted status table: {sorted_table}") + + # Keep only the first entry for each 'for arch', as that is now the newest + status_table_last = { + 'on arch': [], 'for arch': [], 'for repo': [], 'date': [], 'status': [], 'url': [], 'result': [] + } + for x in range(0, len(sorted_table['date'])): + if sorted_table['for arch'][x] not in status_table_last['for arch']: + self.log(f"arch: {sorted_table['for arch'][x]} not yet in status_table_last") + for key in status_table_last: + self.log(f"Adding to '{key}' and the value {sorted_table[key][x]}") + status_table_last[key].append(sorted_table[key][x]) + + # Re-sort, now only on 'for arch', for nicer viewing + sorted_indices = sorted( + range(len(status_table_last['for arch'])), + key=lambda x: status_table_last['for arch'][x] + ) + sorted_table_last = {key: [status_table_last[key][i] for i in sorted_indices] for key in status_table_last} + + # overwrite the original status_table + status_table = sorted_table_last + comment_status = '' comment_status += "\nThis is the status of all the `bot: build` commands:" comment_status += "\n|on|for|repo|result|date|status|url|" diff --git a/tools/commands.py b/tools/commands.py index fe45d079..bd80e339 100644 --- a/tools/commands.py +++ b/tools/commands.py @@ -87,6 +87,8 @@ def __init__(self, cmd_str): # TODO add function name to log messages cmd_as_list = cmd_str.split() self.command = cmd_as_list[0] # E.g. 'build' or 'help' + self.general_args = [] + self.action_filters = None self.build_params = None # TODO always init self.action_filters with empty EESSIBotActionFilter? @@ -110,9 +112,14 @@ def __init__(self, cmd_str): # according to the expected argument format for 'for:' self.build_params = EESSIBotBuildParams(build_params) else: - # Anything that is not 'on:' or 'for:' should just be passed on as normal - # No further parsing of the value is needed - other_filter_args.extend([arg]) + # Anything that is not 'on:' or 'for:' + # Check if it's a filter argument, if so, pass it on to other_filter_args witout further parsing + # If it's not a filter argument, it is a general argument - just store it so any other function + # can read it + if ':' in arg: + other_filter_args.extend([arg]) + else: + self.general_args.append(arg) # If no 'on:' is found in the argument list, everything that follows the 'for:' argument # (until the next space) is considered the argument list for the action filters @@ -140,6 +147,7 @@ def __init__(self, cmd_str): # so no special parsing needed there log(f"Extracted filter arguments related to hardware target: {normalized_filters}") log(f"Other extracted filter arguments: {other_filter_args}") + log(f"Other general arguments: {self.general_args}") normalized_filters += other_filter_args # Finally, change into a space-separated string, as expected by EESSIBotActionFilter @@ -170,5 +178,8 @@ def to_string(self): Returns: string: the string representation created by the method """ - action_filters_str = self.action_filters.to_string() - return f"{' '.join([self.command, action_filters_str]).rstrip()}" + if self.action_filters is None: + return "" + else: + action_filters_str = self.action_filters.to_string() + return f"{' '.join([self.command, action_filters_str]).rstrip()}"