class Game(models.Model):
    name = models.CharField(max_length=256, db_index=True)
    abbreviation = models.CharField(max_length=64, db_index=True)
    sldb_name = models.CharField(max_length=64, db_index=True)
    developer = models.ManyToManyField(User, blank=True)

    def __unicode__(self):
        return "Game({}, {}, {})".format(self.pk, self.abbreviation, self.name[:70])

    def get_absolute_url(self):
        return reverse("srs/browse_archive", args=["game={}".format(self.pk)])

    class Meta:
        ordering = ["name"] class GameRelease(models.Model):
    name = models.CharField(max_length=256, db_index=True)
    version = models.CharField(max_length=64)
    game = models.ForeignKey(Game, on_delete=CASCADE)

    def __unicode__(self):
        return "GameRelease({}, {}, {}, {})".format(self.pk, self.name[:70], self.version, self.game)

    def get_absolute_url(self):
        return reverse("srs/gamerelease", args=[self.name])

    @property
    def replay_count(self):
        return Replay.objects.filter(gametype=self.name).count()

    class Meta:
        ordering = ["name", "version"] class Map(models.Model):
    name = models.CharField(max_length=128, db_index=True)
    startpos = models.CharField(max_length=1024, blank=True, null=True)
    height = models.IntegerField()
    width = models.IntegerField()
    metadata2 = JSONTextField(blank=True, null=True, default=None)

    def __repr__(self):
        return "Map({}, {})".format(self.pk, self.name)

    def __unicode__(self):
        return self.name

    def get_absolute_url(self):
        return reverse("srs/rmap", args=[self.name])

    @property
    def replay_count(self):
        return Replay.objects.filter(map_info=self).count()

    class Meta:
        ordering = ["name"] class Player(models.Model):
    account = models.ForeignKey(PlayerAccount, blank=True, null=True, on_delete=CASCADE)
    name = models.CharField(max_length=128, db_index=True)
    rank = models.SmallIntegerField()
    skill = models.CharField(max_length=16, blank=True)
    skilluncertainty = models.SmallIntegerField(default=-1, blank=True)
    spectator = models.BooleanField(default=False)
    team = models.ForeignKey("Team", blank=True, null=True, on_delete=CASCADE)
    replay = models.ForeignKey(Replay, on_delete=CASCADE)
    startposx = models.FloatField(blank=True, null=True)
    startposy = models.FloatField(blank=True, null=True)
    startposz = models.FloatField(blank=True, null=True)

    def __unicode__(self):
        return "Player({}, {})".format(self.id, self.name)

    def get_absolute_url(self):
        return reverse("srs/player", args=[self.account.accountid])

    class Meta:
        ordering = ["name"] class PlayerAccount(models.Model):
    accountid = models.IntegerField(unique=True)
    countrycode = models.CharField(max_length=2)
    preffered_name = models.CharField(max_length=128, db_index=True)
    sldb_privacy_mode = models.SmallIntegerField(default=1)

    def __unicode__(self):
        return "PlayerAccount({}: {})".format(
            self.accountid,
            reduce(lambda x, y: "{}|{}".format(x, y), self.get_names())[:40],
        )

    def get_absolute_url(self):
        return reverse("srs/player", args=[self.accountid])

    @property
    def replay_count(self):
        return Player.objects.filter(account__in=self).count()

    @property
    def spectator_count(self):
        return Player.objects.filter(account__in=self, spectator=True).count()

    def get_rating(self, game, match_type):
        try:
            rating, _ = Rating.objects.get_or_create(playeraccount=self, game=game, match_type=match_type)
            return rating
        except MultipleObjectsReturned:
            # this shouldn't happen, was a bug, now delete last object and return first
            ratings = Rating.objects.filter(playeraccount=self, game=game, match_type=match_type)
            ratings.last().delete()
            return ratings.first()

    def get_names(self):
        pls = list(Player.objects.filter(account=self).values_list("name", flat=True))
        if pls:
            try:
                pls.remove(self.preffered_name)
            except ValueError:
                pass  # Player with name "pref_name"  was removed from DB, but PlayerAccount stayed
            else:
                pls.insert(0, self.preffered_name)
            return uniqify_list(pls)
        else:
            return [self.preffered_name]

    def get_all_games(self):
        gametypes = Replay.objects.filter(player__account=self, player__spectator=False).values_list(
            "gametype", flat=True
        )
        return Game.objects.filter(gamerelease__name__in=uniqify_list(gametypes)).distinct()

    def get_all_games_no_bots(self):
        gametypes = (
            Replay.objects.filter(player__account=self, player__spectator=False)
            .exclude(tags=Tag.objects.get(name="Bot"))
            .values_list("gametype", flat=True)
        )
        return Game.objects.filter(gamerelease__name__in=uniqify_list(gametypes)).distinct()

    def get_user(self):
        try:
            return User.objects.get(userprofile__accountid=self.accountid)
        except ObjectDoesNotExist:
            return None

    @property
    def bawards(self):
        """
        Find number of won BA awards (for all award types).

        :return: dict: {'cowAward': 0, 'dmgRecAward': 2, 'ecoAward': 3, ...}
        """
        res = dict()
        field_names = [
            field.name for field in BAwards._meta.get_fields() if "Award" in field.name and "Score" not in field.name
        ]
        for award in field_names:
            kwargs = {"{}__account".format(award): self}
            res[award] = BAwards.objects.filter(**kwargs).count()
        return res

    @property
    def xtawards(self):
        """
        Find top 10 hero and "lost in service" units for this player. Sorted by headcount.

        :return: tuple: heroes, lost. each is a list of tuples:
            ([(u'Sumo', 9), (u'Light Laser Tower', 8), ...], [(), ..])
        """
        awards = XTAwards.objects.filter(player__account=self, isAlive=1).values_list("unit", flat=True)
        co = Counter(awards)
        heroes = co.most_common(10)
        awards = XTAwards.objects.filter(player__account=self, isAlive=0).values_list("unit", flat=True)
        co = Counter(awards)
        lost = co.most_common(10)
        return heroes, lost

    @property
    def cursed_awards(self):
        """
        Find number of won cursed awards (for all award types).

        :return: [{'type': 'air', 'name': '..', 'img': images/..', 'count': 12}, ...]
        """
        awards = CursedAwards.objects.filter(player__account=self)
        co = Counter()
        for award in awards:
            co.update(award.as_dict(True).keys())
        return [
            {
                "type": c[0],
                "name": CursedAwards.field_to_name[c[0]],
                "img": os.path.join("img/cursed_awards", CursedAwards.field_to_image[c[0]]),
                "count": c[1],
            }
            for c in co.most_common()
        ]

    class Meta:
        ordering = ["accountid"] class PlayerStats(models.Model):
    replay = models.ForeignKey(Replay, on_delete=CASCADE)
    stats = models.BinaryField(blank=True, null=True, default=None)

    @property
    def decompressed(self):
        return ujson.loads(zlib.decompress(self.stats))

    def __unicode__(self):
        return "PlayerStats(pk={}, replay.pk={}, {!r}...)".format(self.pk, self.replay.pk, self.stats[:4]) class Rating(RatingBase):
    pass class Replay(models.Model):
    versionString = models.CharField(max_length=128)
    gameID = models.CharField(max_length=32, unique=True)
    unixTime = models.DateTimeField(verbose_name="date of match", db_index=True)
    wallclockTime = models.CharField(max_length=32, verbose_name="length of match")
    autohostname = models.CharField(max_length=128, blank=True, null=True, db_index=True)
    gametype = models.CharField(max_length=256, db_index=True)
    startpostype = models.IntegerField(blank=True, null=True)
    title = models.CharField(max_length=256, db_index=True)
    short_text = models.CharField(max_length=50, db_index=True)
    long_text = models.CharField(max_length=513, db_index=True)
    notcomplete = models.BooleanField(default=True)
    map_info = models.ForeignKey(Map, blank=True, null=True, on_delete=CASCADE)
    map_img = models.ForeignKey(MapImg, blank=True, null=True, on_delete=CASCADE)
    tags = models.ManyToManyField(Tag)
    uploader = models.ForeignKey(User, on_delete=CASCADE)
    upload_date = models.DateTimeField(auto_now_add=True, db_index=True)
    filename = models.CharField(max_length=256)
    path = models.CharField(max_length=256)
    download_count = models.IntegerField(default=0)
    comment_count = models.IntegerField(default=0)
    rated = models.BooleanField(default=False)
    published = models.BooleanField(default=False)

    def __unicode__(self):
        return "Replay({}, {!r}, {!r})".format(self.pk, self.gameID, self.title)

    def to_dict(self):
        return {
            "id": self.id,
            "versionString": self.versionString,
            "gameID": self.gameID,
            "unixTime": self.unixTime,
            "wallclockTime": self.wallclockTime,
            "autohostname": self.autohostname,
            "gametype": self.gametype,
            "startpostype": self.startpostype,
            "title": self.title,
            "short_text": self.short_text,
            "long_text": self.long_text,
            "map_name": self.map_info.name,
            "map_startpos": self.map_info.startpos,
            "map_width": self.map_info.width,
            "map_height": self.map_info.height,
            "tags": ",".join(self.tags.all().values_list("name", flat=True)) if self.tags.exists() else "",
            "uploader": self.uploader.username,
            "upload_date": self.upload_date,
        }

    def get_absolute_url(self):
        return reverse("srs/replay", args=[str(self.gameID)])

    @property
    def was_succ_uploaded(self):
        return not UploadTmp.objects.filter(replay=self).exists()

    class Meta:
        get_latest_by = "upload_date"
        ordering = ["-upload_date"]

    @property
    def game(self):
        return self.game_release.game

    @property
    def game_release(self):
        try:
            gr = GameRelease.objects.get(name=self.gametype)
            return gr
        except ObjectDoesNotExist:
            # new game release
            pass

        # look for existing game name
        game = None
        for i in range(len(self.gametype)):
            _game = Game.objects.filter(name=self.gametype[: -1 * i])
            if _game.count() == 1:
                game = _game.first()
                break
            elif _game.count() > 1:
                msg = "Found %r Games with name %r: %r.\ngametype: %r\ngameID: %r\nmatch: %r." % (
                    _game.count(),
                    self.gametype[: -1 * i],
                    _game,
                    self.gametype,
                    self.gameID,
                    self.__unicode__(),
                )
                logger.error(msg)
                send_mail([x[1] for x in settings.ADMINS], "SRS: ambiguous game name", msg)
            else:
                pass
        if not game:
            logger.debug("not game")
            msg = "Unknown gametype: %r\ngameID: %r\nmatch: %r." % (
                self.gametype,
                self.gameID,
                self.__unicode__(),
            )
            logger.error(msg)
            send_mail([x[1] for x in settings.ADMINS], "SRS: Unknown game name", msg)

            game_name = str()
            version = str()
            in_version = False
            for gr_name_part in self.gametype.split():
                version_start = ["v", "V", "b", "("]
                version_start.extend(map(str, range(10)))
                if (
                    in_version
                    or gr_name_part.upper().startswith("TEST")
                    or gr_name_part.upper().startswith("RC")
                    or gr_name_part[0] in version_start
                ):
                    in_version = True
                    version += "{} ".format(gr_name_part)
                else:
                    game_name += "{} ".format(gr_name_part)

            game_name = game_name.rstrip()
            while game_name[-1] in [" ", "-"]:
                game_name = game_name[:-1]
        else:
            game_name = game.name
            version = self.gametype[len(game.name) :]
        version = version.strip()
        if not version:
            version = "n/a"

        if version[0].upper() == "V" or version[0] == ".":
            version = version[1:]

        game, _ = Game.objects.get_or_create(
            name=game_name,
            defaults={"abbreviation": reduce(lambda x, y: x + y, [gn[0].upper() for gn in game_name.split()])},
        )
        return GameRelease.objects.create(name=self.gametype, game=game, version=version)

    @property
    def match_type(self):
        """returns string (from searching through tags): 1v1 / Team / FFA / TeamFFA'"""
        # quick and dirty using tags
        try:
            tag = self.tags.filter(name__regex=r"^[0-9]v[0-9]$")[0]
            if tag.name == "1v1":
                return "1v1"
            else:
                return "Team"
        except IndexError:
            pass
        try:
            tag = self.tags.get(name__regex=r"^TeamFFA$")
            return tag.name
        except ObjectDoesNotExist:
            pass
        try:
            tag = self.tags.get(name__regex=r"^FFA$")
            return tag.name
        except ObjectDoesNotExist:
            pass

        # thoroughly using player/team count
        allyteams = Allyteam.objects.filter(replay=self)
        if allyteams.count() == 2:
            if PlayerAccount.objects.filter(player__team__allyteam__in=allyteams).exclude(accountid=0).count() == 2:
                return "1v1"
            else:
                return "Team"
        elif allyteams.count() > 2:
            if (
                PlayerAccount.objects.filter(player__team__allyteam__in=allyteams).exclude(accountid=0).count()
                == allyteams.count()
            ):
                return "FFA"
            else:
                return "TeamFFA"
        else:
            # this is kind of a broken match, but not returning anything breaks the web site
            return "1v1"

    @property
    def match_type_short(self):
        """returns string (from match_type()): 1 / T / F / G"""
        if self.match_type == "TeamFFA":
            return "G"
        else:
            return self.match_type[0]

    @property
    def num_players(self):
        """returns string (from counting players): 1v1 / 1v5 / 6v6 / 2v2v2v2 / ..."""
        try:
            allyteams = Allyteam.objects.filter(replay=self)
            at_sizes = [PlayerAccount.objects.filter(player__team__allyteam=at).count() for at in allyteams]
            at_sizes.sort()
            return str(reduce(lambda x, y: "{}v{}".format(x, y), at_sizes))
        except:
            logger.exception("FIXME: to broad exception handling.")
            return "?v?"

    @property
    def match_end(self):
        length = self.wallclockTime.split(":")
        try:
            length2 = datetime.timedelta(seconds=int(length[2]), minutes=int(length[1]), hours=int(length[0]))
        except KeyError:
            return self.unixTime
        return self.unixTime + length2

    @property
    def duration_ISO_8601(self):
        length = self.wallclockTime.split(":")
        try:
            return "PT{0}H{1}M{2}S".format(*length)
        except IndexError:
            return self.wallclockTime

    playername = "N/A"  # something must exists, or PlayersReplayTable will always have an empty column

    def _playername(self, playeraccount):
        player = Player.objects.filter(replay=self, account=playeraccount)[
            0
        ]  # not using get() for the case of a spec-cheater
        return player.name

    def _faction_result(self, playeraccount):
        """
        Create cached entries, so this function (and the DB queries) runs only once per line in PlayersReplayTable.
        """
        try:
            player = Player.objects.get(replay=self, account=playeraccount)
        except MultipleObjectsReturned:  # happens on Bots player page
            if playeraccount.accountid == 0:
                player = Player.objects.filter(replay=self, account=playeraccount)[0]
            else:
                player = Player.objects.get(replay=self, account=playeraccount, spectator=False)
        try:
            team = Team.objects.filter(replay=self, teamleader=player)[0]
            if team.allyteam.winner:
                self._result_cache = (playeraccount, "won")
            else:
                self._result_cache = (playeraccount, "lost")
            self._faction_cache = (playeraccount, team.side)
        except (ObjectDoesNotExist, IndexError):
            self._result_cache = (playeraccount, "spec")
            self._faction_cache = (playeraccount, "spec")

    faction = "N/A"  # something must exists, or PlayersReplayTable will always have an empty column

    def _faction(self, playeraccount):
        """
        This is used by PlayersReplayTable
        """
        if not hasattr(self, "_faction_cache") or not self._faction_cache[0] == playeraccount:
            self._faction_result(playeraccount)
        return self._faction_cache[1]

    result = "N/A"  # something must exists, or PlayersReplayTable will always have an empty column

    def _result(self, playeraccount):
        """
        This is used by PlayersReplayTable
        """
        if not hasattr(self, "_result_cache") or not self._result_cache[0] == playeraccount:
            self._faction_result(playeraccount)
        return self._result_cache[1]

    @property
    def bawards(self):
        try:
            return BAwards.objects.get(replay=self)
        except ObjectDoesNotExist:
            return None

    @property
    def cursed_awards(self):
        """
        :return: [('shell', {
            'players': [(<Player a>, 12), (<Player b>, 34)],
            'img': 'trophy_shell.png',
            'name': 'Turtle Shell'
            }),
        ...]
        """
        res = dict()
        ca_fields = CursedAwards.sorted_field_names()
        try:
            for ca in CursedAwards.objects.filter(replay=self):
                for fn in ca_fields:
                    cav = getattr(ca, fn)
                    if cav:
                        res.setdefault(fn, {})["name"] = CursedAwards.field_to_name[fn]
                        res[fn]["img"] = os.path.join("img/cursed_awards", CursedAwards.field_to_image[fn])
                        res[fn].setdefault("players", []).append((ca.player, cav))
                        if len(res[fn]["players"]) > 1:
                            res[fn]["players"] = sorted(
                                res[fn]["players"],
                                key=operator.itemgetter(1),
                                reverse=True,
                            )
        except ObjectDoesNotExist:
            return None
        return sorted(res.items(), key=operator.itemgetter(0)) class SldbLeaderboardPlayer(models.Model):
    leaderboard = models.ForeignKey(SldbLeaderboardGame, on_delete=CASCADE)
    account = models.ForeignKey(PlayerAccount, on_delete=CASCADE)
    rank = models.IntegerField()
    trusted_skill = models.FloatField()
    estimated_skill = models.FloatField()
    uncertainty = models.FloatField()
    inactivity = models.IntegerField()

    def __unicode__(self):
        return "SldbLeaderboardPlayer({}, {}, {}, {}, {})".format(
            self.pk, self.leaderboard, self.rank, self.account, self.trusted_skill
        )

    class Meta:
        ordering = ["rank"] class SldbPlayerTSGraphCache(models.Model):
    last_modified = models.DateTimeField(auto_now_add=True)
    account = models.ForeignKey(PlayerAccount, on_delete=CASCADE)
    game = models.ForeignKey(Game, on_delete=CASCADE)
    filepath_global = models.CharField(max_length=256)
    filepath_duel = models.CharField(max_length=256)
    filepath_ffa = models.CharField(max_length=256)
    filepath_team = models.CharField(max_length=256)
    filepath_teamffa = models.CharField(max_length=256)

    match_type2sldb_name = {
        "1": "Duel",
        "T": "Team",
        "F": "FFA",
        "G": "Global",
        "L": "TeamFFA",
    }

    def __unicode__(self):
        return "SldbPlayerTSGraphCache({}) {} | {} | {}".format(self.pk, self.account, self.game, self.filepath_global)

    def remove_files(self):
        for filepath in (
            self.filepath_global,
            self.filepath_duel,
            self.filepath_ffa,
            self.filepath_team,
            self.filepath_teamffa,
        ):
            if "static/img/" in filepath:
                # don't remove static error / info images
                continue
            else:
                try:
                    os.remove(filepath)
                except OSError as exc:
                    logger.warning(
                        "Cannot remove file '%s' of cache entry %d: %s",
                        filepath,
                        self.id,
                        exc,
                    )

    @staticmethod
    def purge_old():
        """
        Remove >= 30 days old entries
        """
        old_entries = SldbPlayerTSGraphCache.objects.filter(
            last_modified__lt=datetime.datetime.now(tz=Replay.objects.latest().unixTime.tzinfo)
            - datetime.timedelta(days=30)
        )
        if old_entries.exists():
            logger.debug("Deleting stale SldbPlayerTSGraphCache objects: %s", old_entries)
            for entry in old_entries:
                entry.remove_files()
            old_entries.delete()

    def as_dict(self):
        return {
            "Global": self.filepath_global,
            "Duel": self.filepath_duel,
            "FFA": self.filepath_ffa,
            "Team": self.filepath_team,
            "TeamFFA": self.filepath_teamffa,
        } class TeamStats(models.Model):
    replay = models.ForeignKey(Replay, on_delete=CASCADE)
    stat_type = models.CharField(max_length=32)
    stats = models.BinaryField(blank=True, null=True, default=None)

    graphid2label = OrderedDict(
        (
            ("metalUsed", "Metal Used"),
            ("metalProduced", "Metal Produced"),
            ("metalExcess", "Metal Excess"),
            ("metalReceived", "Metal Received"),
            ("metalSent", "Metal Sent"),
            ("energyUsed", "Energy Used"),
            ("energyProduced", "Energy Produced"),
            ("energyExcess", "Energy Excess"),
            ("energyReceived", "Energy Received"),
            ("energySent", "Energy Sent"),
            ("damageDealt", "Damage Dealt"),
            ("damageReceived", "Damage Received"),
            ("unitsProduced", "Units Produced"),
            ("unitsKilled", "Units Killed"),
            ("unitsDied", "Units Died"),
            ("unitsReceived", "Units Received"),
            ("unitsSent", "Units Sent"),
            ("unitsCaptured", "Units Captured"),
            ("unitsOutCaptured", "Units Stolen"),
        )
    )

    @property
    def decompressed(self):
        return ujson.loads(zlib.decompress(self.stats))

    @property
    def label(self):
        return self.graphid2label[self.stat_type]

    def __unicode__(self):
        return "TeamStats(pk={}, replay.pk={}, stat_type={}, {!r}...)".format(
            self.pk, self.replay.pk, self.stat_type, self.stats[:4]
        ) class SLDBError(Exception):
    pass def get_sldb_player_stats(game_abbr, accountid):
    """
    get_sldb_player_stats("BA", 130601) -> {'status': 0,
                                            'results': {'Duel': [12, 34, 5],
                                                        'TeamFFA': [67, 8, 9],
                                                        'FFA': [10, 11, 12],
                                                        'Team': [134, 567, 89]
                                                        }
                                            }
                                            or SLDBstatusError

    SLDB XmlRpc interface docu: https://github.com/Yaribz/SLDB/blob/master/XMLRPC#124
    """
    #     logger.debug("game_abbr: %s accountid: %d", game_abbr, accountid)
    return _query_sldb("getPlayerStats", game_abbr, accountid) def get_sldb_playerskill(game_abbr, accountids, user=None, privatize=True):
    """
    game_abbr: "BA", "ZK" etc from Game.sldb_name
    accountids: [1234, 567]  -- PlayerAccount for those must already exist!
    user: request.user
    privatize: privatize TS depending on privacyMode and logged in user,
               if False exact values are returned regardless of privacyMode
               and user

    If the overall request was OK, but one or more results are bad, no
    exception will be raised, but instead
    skills=[[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]] will
    be returned for that accountid.

    SLDB XmlRpc interface docu: https://github.com/Yaribz/SLDB/blob/master/XMLRPC#L1
    """
    #     logger.debug("game: %s accountids: %s user: %s privatize: %s", game_abbr, accountids, user, privatize)

    rpc_skills = _query_sldb("getSkills", game_abbr, accountids)

    for pa_result in rpc_skills:
        pa_result["account"] = _get_PlayerAccount(pa_result["accountId"], privacy_mode=-1)
        if pa_result["status"] != 0:
            pa_result["skills"] = [
                [0.0, 0.0],
                [0.0, 0.0],
                [0.0, 0.0],
                [0.0, 0.0],
                [0.0, 0.0],
            ]
            continue
        for i in range(5):
            mu = float(pa_result["skills"][i].split("|")[0])
            si = float(pa_result["skills"][i].split("|")[1])
            pa_result["skills"][i] = [0, 0]
            if privatize:
                if pa_result["privacyMode"] == 0:
                    do_priv = False
                else:
                    if user:
                        do_priv = user.userprofile.accountid != pa_result["accountId"]
                    else:
                        do_priv = True
            else:
                do_priv = False

            if do_priv:
                pa_result["skills"][i][0] = privatize_skill(mu)
            else:
                pa_result["skills"][i][0] = mu
            pa_result["skills"][i][1] = si

            #     logger.debug("returning: %s", rpc_skills)
    return rpc_skills
import datetime
import json
import logging
import MySQLdb
import requests
import ujson
from collections import OrderedDict
from functools import reduce
from operator import or_
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models import Q
from django.http import HttpResponse, HttpResponseNotFound
from django.shortcuts import get_object_or_404, render
from django.utils import timezone
from django_comments.models import Comment
from eztables.views import JSON_MIMETYPE, DatatablesView
from .models import (
    Game,
    GameRelease,
    Map,
    Player,
    PlayerAccount,
    PlayerStats,
    Rating,
    Replay,
    SldbLeaderboardPlayer,
    SldbPlayerTSGraphCache,
    TeamStats,
)
from .sldb import SLDBError, get_sldb_player_stats, get_sldb_playerskill
# This file is part of the "spring relay site / srs" program. It is published
# under the GPLv3.
#
# Copyright (C) 2016-2020 Daniel Troeder (daniel #at# admin-box #dot# com)
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.




logger = logging.getLogger(__name__)


def ajax_player_lookup(request, name):
    pa = (

        PlayerAccount.objects.exclude(accountid=0)