async def reasons(self, ctx: Context, karma: clean_content): karma_stripped = karma.lstrip("@") # Get the karma from the database karma_item = (db_session.query(KarmaModel).filter( func.lower(KarmaModel.name) == func.lower(karma_stripped)).first()) if karma_item: # Set the karma item's name to be the same as in the database karma_stripped = karma_item.name # Get all of the changes that have some reason karma_changes = (db_session.query(KarmaChange).filter( KarmaChange.karma_id == karma_item.id, KarmaChange.reason is not None, ).all()) # Flatten the reasons into a single list and sort it alphabetically reasons = sorted( (r for r in (change.reason for change in karma_changes) if r is not None), key=str.casefold, ) # If there's at least one reason if reasons: reasons_plural = pluralise(reasons, "reason") bullet_points = "\n".join(f" • {reason}\n" for reason in reasons) result = f'The {reasons_plural} for "{karma_stripped}" are as follows:\n\n{bullet_points}' else: result = f"There are no reasons down for that karma topic! :frowning:" file = None fp = None if len(result) > 2000: # Create a new temporary file that isn't deleted until we say with tempfile.NamedTemporaryFile(delete=False) as fp: fp.write(result.encode("utf8")) # Wait for the file to close before accessing it (Windows NT limitation) file = File(fp.name, filename=f"{karma_stripped}.txt") result = f'There are too many reasons for "{karma_stripped}" to fit in a Discord message!' await ctx.send(result, file=file) # Delete temporary file (if any) after message is sent if fp: os.remove(fp.name) else: # The item hasn't been karma'd result = f"\"{karma_stripped}\" hasn't been karma'd yet. :cry:" await ctx.send(result)
async def reasons(self, ctx: Context, karma: clean_content): karma_stripped = karma.lstrip("@") # Get the karma from the database karma_item = ( db_session.query(KarmaModel) .filter(func.lower(KarmaModel.name) == func.lower(karma_stripped)) .first() ) if karma_item: # Get all of the changes that have some reason karma_changes = ( db_session.query(KarmaChange) .filter( KarmaChange.karma_id == karma_item.id, KarmaChange.reasons != [] ) .all() ) # Flatten the reasons into a single list and sort it alphabetically reasons = [ reason for sublist in [change.reasons for change in karma_changes] for reason in sublist ] reasons = sorted(reasons, key=str.lower) # If there's at least one reason if reasons: # Handle the plurality of reason(s) plural = "" if len(reasons) > 1: plural = "s" result = ( f'The reason{plural} for "{karma_stripped}" are as follows:\n\n' ) for reason in reasons: result += f" • {reason}\n" else: result = f"There are no reasons down for that karma topic! :frowning:" await ctx.send(result) else: # The item hasn't been karma'd result = f"\"{karma_stripped}\" hasn't been karma'd yet. :cry:" await ctx.send(result)
async def info(self, ctx: Context, karma: clean_content): await ctx.trigger_typing() t_start = current_milli_time() # Strip any leading @s and get the item from the DB karma_stripped = karma.lstrip("@") karma_item = (db_session.query(KarmaModel).filter( func.lower(KarmaModel.name) == func.lower(karma_stripped)).first()) # If the item doesn't exist then raise an error if not karma_item: raise KarmaError( message=f"\"{karma_stripped}\" hasn't been karma'd yet. :cry:") # Get the changes and plot the graph filename, path = await plot_karma({karma_stripped: karma_item.changes}) # Get the user with the most karma # I'd use a group_by sql statement here but it seems to not terminate all_changes = (db_session.query(KarmaChange).filter( KarmaChange.karma_id == karma_item.id).order_by( KarmaChange.created_at.asc()).all()) user_changes = defaultdict(list) for change in all_changes: user_changes[change.user].append(change) most_karma = max(user_changes.items(), key=lambda item: len(item[1])) # Calculate the approval rating of the karma approval = 100 * ((karma_item.pluses - karma_item.minuses) / (karma_item.pluses + karma_item.minuses)) mins_per_karma = (all_changes[-1].local_time - all_changes[0].local_time).total_seconds() / ( 60 * len(all_changes)) time_taken = (current_milli_time() - t_start) / 1000 # Attach the file as an image for dev purposes if CONFIG.DEBUG: # Attach the file as an image for dev purposes plot_image = open(path, mode="rb") plot = File(plot_image) await ctx.send( f'Here\'s the karma trend for "{karma_stripped}" over time', file=plot) else: # Construct the embed generated_at = datetime.strftime( utc.localize(datetime.utcnow()).astimezone( timezone("Europe/London")), "%H:%M %d %b %Y", ) embed_colour = Color.from_rgb(61, 83, 255) embed_title = f'Statistics for "{karma_stripped}"' embed_description = f'"{karma_stripped}" has been karma\'d {len(all_changes)} {pluralise(all_changes, "time")} by {len(user_changes.keys())} {pluralise(user_changes.keys(), "user")}.' embed = Embed(title=embed_title, description=embed_description, color=embed_colour) embed.add_field( name="Most karma'd", value= f'"{karma_stripped}" has been karma\'d the most by <@{most_karma[0].user_uid}> with a total of {len(most_karma[1])} {pluralise(most_karma[1], "change")}.', ) embed.add_field( name="Approval rating", value= f'The approval rating of "{karma_stripped}" is {approval:.1f}% ({karma_item.pluses} positive to {karma_item.minuses} negative karma and {karma_item.neutrals} neutral karma).', ) embed.add_field( name="Karma timeline", value= f'"{karma_stripped}" was first karma\'d on {datetime.strftime(all_changes[0].local_time, "%d %b %Y at %H:%M")} and has been karma\'d approximately every {mins_per_karma:.1f} minutes.', ) embed.set_footer( text= f"Statistics generated at {generated_at} in {time_taken:.3f} seconds." ) embed.set_image(url="{host}/{filename}".format( host=CONFIG.FIG_HOST_URL, filename=filename)) display_name = get_name_string(ctx.message) await ctx.send(f"Here you go, {display_name}! :page_facing_up:", embed=embed)