import json
import os
from datetime import timezone
from decimal import Decimal, ROUND_UP
from feedgen.feed import FeedGenerator
from flask import abort, current_app, g, jsonify, url_for
from tahrir.utils.avatar import hash_email
from tahrir.utils.badge import badge_json_generator, sort_badges_by_tag
from tahrir.utils.user import get_person
from ..defaults import TAHRIR_DISPLAY_TAGS
from . import blueprint as bp
def _get_user_badge_info(person):
"""Returns a dictionary of the user badge information"""
# Get user badges.
user_badges = [a.badge for a in person.assertions]
# Sort user badges by id.
user_badges = sorted(user_badges, key=lambda badge: badge.id)
# Get total number of unique badges in the system.
count_total_badges = g.tahrirdb.get_all_badges().count()
# Get percentage of badges earned.
try:
percent_earned = (float(len(user_badges)) / float(count_total_badges)) * 100
except ZeroDivisionError:
percent_earned = 0
# Get rank. (same code found in leaderboard view function)
rank = person.rank or 0
user_count = g.tahrirdb.get_all_persons().count()
try:
percentile = (
Decimal(float(rank) / float(user_count)).quantize(Decimal(".0001"), rounding=ROUND_UP)
* 100
)
except ZeroDivisionError:
percentile = 0
badges_by_tag, uncategorized_badges = sort_badges_by_tag(user_badges)
return dict(
user_badges=user_badges,
count_total_badges=count_total_badges,
percent_earned=percent_earned,
rank=rank,
user_count=user_count,
percentile=percentile,
badges_by_tag=badges_by_tag,
uncategorized_badges=uncategorized_badges,
)
def _user_json_generator(person):
"""Generates a json of user data"""
user_info = _get_user_badge_info(person)
assertions = sorted(person.assertions, key=lambda item: item.issued_on, reverse=True)
serialized = []
classified = {name: [] for name in TAHRIR_DISPLAY_TAGS}
try:
with open(os.path.join(current_app.static_folder, "rarities.json")) as file:
raredata = json.load(file)
except (FileNotFoundError, json.JSONDecodeError):
abort(500, "Mistaken or absent rarities file")
for indx, item in enumerate(assertions):
issued = {"issued": float(item.issued_on.strftime("%s"))}
reason = {"reason": item.issued_for or None}
rarity = {"rarity": raredata["badges"][item.badge.id]["rare"]}
badged = badge_json_generator(item.badge, withasserts=False)
serialized.append({**issued, **badged, **reason, **rarity})
for name in classified.keys():
if name in item.badge.tags:
classified[name].append(indx)
return {
"user": person.nickname,
"mail": hash_email(person.avatar),
"percent_earned": user_info["percent_earned"],
"classified": classified,
"serialized": serialized,
"percentile": str(user_info["percentile"]),
"rank": user_info["rank"],
"user_count": user_info["user_count"],
}
def _user_team_json_generator(team, person):
"""Generate the json of team data"""
team_id = team.id
badges = g.tahrirdb.get_badges_from_team(team_id)
badges_count = len(badges)
series = g.tahrirdb.get_series_from_team(team_id)
assertions = person.assertions
assertion_ids = set([assertion.badge_id for assertion in assertions])
series_info = []
for elem in series:
milestones_info = []
milestones = elem.milestone
for milestone in milestones:
milestones_info.append(
{
"milestone": milestone.as_dict(),
"series": elem.as_dict(),
"is_awarded": milestone.badge_id in assertion_ids,
}
)
series_info.append(milestones_info)
return {"badges_count": badges_count, "series_info": series_info}
[docs]
@bp.route("/user/<user_id>/team/<int:team_id>/json")
def user_team_json(user_id, team_id):
"""Render user team info as JSON dump."""
person = get_person(user_id)
team = g.tahrirdb.get_team(team_id=team_id)
if not person:
return {"error": "No such user exists."}, 404
if person.opt_out and person.email != g.oidc_user.email:
return {"error": "User has opted out."}, 404
return jsonify(_user_team_json_generator(team, person))
[docs]
@bp.route("/json/user/<user_id>")
def user_json(user_id):
"""Render user info JSON dump."""
# So, here they can use their 'id' or their 'nickname'.
# We'll try nickname first since we want to encourage that (or whatever)
# and fall back to id if that fails. If both fail, raise a 404.
person = get_person(user_id)
if not person:
return {"error": "No such user exists."}, 404
if person.opt_out and person.email != g.oidc_user.email:
return {"error": "User has opted out."}, 404
return jsonify(_user_json_generator(person))