class Round(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    round_num = db.Column(db.Integer)
    disputeID = db.Column(db.Integer,
                          db.ForeignKey("dispute.id"),
                          nullable=False)
    draws_in_round = db.Column(db.Integer)
    commits_in_round = db.Column(db.Integer)
    appeal_start = db.Column(db.Integer)
    appeal_end = db.Column(db.Integer)
    vote_lengths = db.Column(db.Integer)
    tokens_at_stake_per_juror = db.Column(db.Integer)
    total_fees_for_jurors = db.Column(db.Integer)
    votes_in_each_round = db.Column(db.Integer)
    repartitions_in_each_round = db.Column(db.Integer)
    penalties_in_each_round = db.Column(db.Integer)
    subcourtID = db.Column(db.Integer,
                           db.ForeignKey("court.id"),
                           nullable=False)

    def votes(self):
        return Vote.query.filter_by(round_id=self.id).order_by(
            Vote.account.asc()).all()

    def delete_recursive(self):
        votes = Vote.query.filter(Vote.round_id == self.id)
        for v in votes:
            logger.info("Deleting Vote %s" % v.id)
            db.session.delete(v)
        db.session.commit()
        logger.info("Deleting round %s" % self.id)
        db.session.delete(self)
        db.session.commit()

    @property
    def majority_reached(self):
        votes_cast = []
        votes_cast.append(
            Vote.query.filter(Vote.round_id == self.id).filter(
                Vote.vote == 1).filter(Vote.choice == 1).count())
        votes_cast.append(
            Vote.query.filter(Vote.round_id == self.id).filter(
                Vote.vote == 1).filter(Vote.choice == 2).count())
        votes_cast.append(
            Vote.query.filter(Vote.round_id == self.id).filter(
                Vote.vote == 1).filter(Vote.choice == 0).count())
        return any(x >= self.draws_in_round / 2 for x in votes_cast)

    @property
    def winning_choice(self):
        # votes = Vote.query.filter(Vote.round_id == self.id).count()
        votes_query = db.session.execute(
            "select choice,count(*) as num_votes from vote \
            where round_id = :round_id and vote=1 \
            group by choice order by num_votes desc", {
                'round_id': self.id
            }).first()
        return (votes_query[0])
Beispiel #2
0
class Deposit(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    address = db.Column(db.String(50))
    cdate = db.Column(db.DateTime)
    amount = db.Column(db.Float)
    txid = db.Column(db.String(50))
    court_id = db.Column(db.Integer, db.ForeignKey("court.id"), nullable=False)
    token_contract = db.Column(db.String(50))  # FIXME

    @classmethod
    def total(cls):
        return cls.query.with_entities(func.sum(cls.amount)).all()[0][0]
Beispiel #3
0
class Vote(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    round_id = db.Column(db.Integer, db.ForeignKey("round.id"), nullable=False)
    account = db.Column(db.String(50))
    commit = db.Column(db.Integer)
    choice = db.Column(db.Integer)
    vote = db.Column(db.Integer)
    date = db.Column(db.DateTime)

    def __str__(self):
        return f'Vote(juror={self.account}, choice={self.choice}, vote={self.vote}, commit={self.commit})'

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            attrs_match = ((self.account.lower() == other.account.lower())
                           and (self.commit == other.commit)
                           and (self.choice == other.choice)
                           and (self.vote == other.vote))
            return attrs_match
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    @property
    def is_winner(self):
        """
        Check if the vote is winner in its round
        """
        round = Round.query.get(self.round_id)
        if not round.majority_reached:
            return False
        return self.choice == round.winning_choice

    @property
    def vote_str(self):
        map_votes = {0: 'Refuse to Arbitrate', 1: 'Yes', 2: 'No', 3: 'Pending'}
        try:
            if self.vote == 1:
                return map_votes[self.choice]
            else:
                return map_votes[3]
        except KeyError:
            logger.error("Key error not found when mapping the vote string")
            logger.error(
                "The vote has the properties: ID:{}, Vote:{}, Choice{}".format(
                    self.id, self.vote, self.choice))
            return ''
Beispiel #4
0
class JurorStake(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    address = db.Column(db.String(50))
    subcourtID = db.Column(db.Integer,
                           db.ForeignKey("court.id"),
                           nullable=False)
    timestamp = db.Column(db.DateTime)
    setStake = db.Column(db.Float)
    txid = db.Column(db.String(100))
    blocknumber = db.Column(db.Integer)

    @staticmethod
    def last_blocknumber():
        return JurorStake.query.order_by(
            JurorStake.id.desc()).first().blocknumber
class Vote(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    round_id = db.Column(db.Integer, db.ForeignKey("round.id"), nullable=False)
    account = db.Column(db.String(50))
    commit = db.Column(db.Integer)
    choice = db.Column(db.Integer)
    vote = db.Column(db.Integer)
    date = db.Column(db.DateTime)

    @property
    def is_winner(self):
        round = Round.query.get(self.round_id)
        if not round.majority_reached:
            return False
        return self.choice == round.winning_choice
Beispiel #6
0
class Round(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    round_num = db.Column(db.Integer)
    disputeID = db.Column(db.Integer,
                          db.ForeignKey("dispute.id"),
                          nullable=False)
    draws_in_round = db.Column(db.Integer)
    commits_in_round = db.Column(db.Integer)
    appeal_start = db.Column(db.Integer)
    appeal_end = db.Column(db.Integer)
    vote_lengths = db.Column(db.Integer)
    tokens_at_stake_per_juror = db.Column(db.Float)
    total_fees_for_jurors = db.Column(db.Float)
    votes_in_each_round = db.Column(db.Integer)
    repartitions_in_each_round = db.Column(db.Float)
    penalties_in_each_round = db.Column(db.Float)
    subcourtID = db.Column(db.Integer,
                           db.ForeignKey("court.id"),
                           nullable=False)

    def __str__(self):
        return f'Round(court={self.subcourtID}, dispute_id={self.disputeID}, number={self.round_num}, drawns={self.draws_in_round}, commits={self.commits_in_round})'

    def __eq__(self, other):
        classes_match = isinstance(other, self.__class__)
        a, b = deepcopy(self.__dict__), deepcopy(other.__dict__)
        # compare based on equality our attributes, ignoring SQLAlchemy internal stuff
        avoid_fields = [
            '_sa_instance_state', 'id', 'appeal_start', 'appeal_end',
            'vote_lengths', 'votes_in_each_round'
        ]
        for field in avoid_fields:
            a.pop(field, None)
            b.pop(field, None)

        attrs_match = (a == b)
        return classes_match and attrs_match

    def __ne__(self, other):
        return not self.__eq__(other)

    def votes(self):
        return Vote.query.filter_by(round_id=self.id).order_by(
            Vote.account.asc()).all()

    def votes_drawn_order(self):
        return Vote.query.filter_by(round_id=self.id).order_by(
            Vote.id.asc()).all()

    def vote_count(self):
        count = {'Yes': 0, 'No': 0, 'Refuse': 0, 'Pending': 0}
        for v in self.votes():
            count[v.vote_str] += 1
        return count

    def delete_recursive(self):
        votes = Vote.query.filter(Vote.round_id == self.id)
        for v in votes:
            logger.info("Deleting Vote %s" % v.id)
            db.session.delete(v)
        db.session.commit()
        logger.info("Deleting round %s" % self.id)
        db.session.delete(self)
        db.session.commit()

    @property
    def majority_reached(self):
        votes_cast = []
        votes_cast.append(
            Vote.query.filter(Vote.round_id == self.id).filter(
                Vote.vote == 1).filter(Vote.choice == 1).count())
        votes_cast.append(
            Vote.query.filter(Vote.round_id == self.id).filter(
                Vote.vote == 1).filter(Vote.choice == 2).count())
        votes_cast.append(
            Vote.query.filter(Vote.round_id == self.id).filter(
                Vote.vote == 1).filter(Vote.choice == 0).count())
        return any(x >= self.draws_in_round / 2 for x in votes_cast)

    @property
    def winning_choice(self):
        # votes = Vote.query.filter(Vote.round_id == self.id).count()
        votes_query = db.session.execute(
            "select choice,count(*) as num_votes from vote \
            where round_id = :round_id and vote=1 \
            group by choice order by num_votes desc", {
                'round_id': self.id
            }).first()
        return (votes_query[0])
Beispiel #7
0
class Court(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))
    address = db.Column(db.String(50))
    parent = db.Column(db.Integer, db.ForeignKey("court.id"), nullable=True)
    minStake = db.Column(db.Float)
    feeForJuror = db.Column(db.Float)
    voteStake = db.Column(db.Integer)
    meanStaked = db.Column(db.Float)
    maxStaked = db.Column(db.Float)
    totalStaked = db.Column(db.Float)
    activeJurors = db.Column(db.Integer)
    disputesLast30days = db.Column(db.Integer)
    minStakeUSD = db.Column(db.Float)

    def disputes(self, days=None):
        """
        Return the disputes in the previous days. If days is None, return all
        the disputes in that Court

        Parameters
        ----------
        days : int, optional
            Number of days to count the disputes backwards.
            The default is None.

        Returns
        -------
        List
            List of all the Disputes

        """
        if days:
            filter_after = (datetime.now() - timedelta(days=days)).replace(
                hour=0, minute=0, second=0)
            return Dispute.query \
                .filter(Dispute.subcourtID == self.id, Dispute.timestamp >= filter_after) \
                .order_by(Dispute.id.desc()).all()
        else:
            return Dispute.query.filter(
                Dispute.subcourtID == self.id).order_by(
                    Dispute.id.desc()).all()

    def disputes_paginated(self, page=0, per_page=10):
        """
        Return the disputes of the court, paginated

        Parameters
        ----------
        page : int, optional
            Current page, by default is 0
        per_page : int, optional
            Amount of disputes per page. By default is 10

        Returns
        -------
        List
            List of all the Disputes

        """
        return Dispute.query.filter(Dispute.subcourtID == self.id).order_by(
            Dispute.id.desc()).paginate(page, per_page, error_out=False)

    @property
    def openCases(self):
        return Dispute.query.filter(Dispute.ruled == 0).filter(
            Dispute.subcourtID == self.id).count()

    @property
    def ruledCases(self):
        return Dispute.query.filter(Dispute.ruled == 1).filter(
            Dispute.subcourtID == self.id).count()

    def get_recursive_childs(self, node=0):
        query = f"""
                WITH RECURSIVE cte_court (id, name, parent) AS (
                    SELECT e.id, e.name, e.parent
                    FROM court e
                    WHERE e.id = {node}

                    UNION ALL

                    SELECT e.id, e.name, e.parent
                    FROM court e
                    JOIN cte_court c ON c.id = e.parent
                )

                SELECT * FROM cte_court ORDER BY cte_court.id;
        """
        return [{
            'id': child[0],
            'name': child[1]
        } for child in db.session.execute(query)]

    @property
    def children_ids(self):
        return [
            child.id for child in Court.query.filter(Court.parent == self.id)
        ]

    @property
    def children_names(self):
        return [
            child.name for child in Court.query.filter(Court.parent == self.id)
        ]

    @property
    def childrens(self):
        return [child for child in Court.query.filter(Court.parent == self.id)]

    @property
    def fees_paid(self):
        disputes = Dispute.query.filter_by(subcourtID=self.id)
        eth_fees_paid = 0
        pnk_distributed = 0
        for dispute in disputes:
            rounds = dispute.rounds()
            for r in rounds:
                eth_fees_paid += r.total_fees_for_jurors
                pnk_distributed += r.penalties_in_each_round
        return {'eth': eth_fees_paid, 'pnk': pnk_distributed}

    @staticmethod
    def getAllCourtChilds(courtID):
        childs = set(Court(id=courtID).children_ids)
        allChilds = []
        while childs:
            child = childs.pop()
            allChilds.append(child)
            childs.update(Court(id=child).children_ids)
        return allChilds

    @staticmethod
    def getParent(courtID):
        return Court.query.filter_by(id=courtID).first().parent

    @property
    def ncourts(self):
        return Court.query.count()

    @property
    def jurors(self):
        courts_childs = Court.getAllCourtChilds(self.id)
        courts_childs.append(self.id)
        if len(courts_childs) > 1:
            courts_id = tuple(courts_childs)
        else:
            courts_id = f'({courts_childs[0]})'
        query = f"""
            SELECT address, SUM(setStake) as staked
            FROM (
                SELECT address, setStake
                FROM juror_stake
                WHERE id IN (
                    SELECT MAX(id)
                    FROM juror_stake
                    WHERE subcourtID in {courts_id}
                    GROUP BY address, subcourtID
                    )
            ) as Jurors
            WHERE setStake > 0
            GROUP BY address
            ORDER BY setStake DESC
            """
        execute = db.session.execute(query).fetchall()
        jurors = {}
        for juror in execute:
            jurors[juror[0]] = juror[1]
        return jurors

    @property
    def map_name(self):
        if self.name:
            return self.name
        else:
            return self.query.filter(Court.id == self.id).first().name

    def yes_no_coherency(self):
        coherency = {'Refuse': 0, 'Yes': 0, 'No': 0}
        incoherents = {'Refuse': 0, 'Yes': 0, 'No': 0, 'Pending': 0}
        for d in self.disputes():
            data = d.yes_no_coherency()
            if data:
                for key, value in data['coherents'].items():
                    coherency[key] += value
                for key, value in data['incoherents'].items():
                    incoherents[key] += value
        totals = {'coherents': sum([value for value in coherency.values()])}
        totals['incoherents'] = sum([value for value in incoherents.values()])
        totals['percentage_coherents'] = totals['coherents'] / (
            totals['coherents'] + totals['incoherents'])
        if all([value == 0 for value in coherency.values()]):
            return None
        else:
            return {
                'coherents': coherency,
                'incoherents': incoherents,
                'totals': totals
            }

    def juror_stats(self):
        jurors = self.jurors
        try:
            court_mean = statistics.mean(jurors.values())
        except Exception as e:
            court_mean = 0
            logger.info(f"Could not get the mean value of the court {self.id}")
            logger.error(e)
        try:
            court_median = statistics.median(jurors.values())
        except Exception as e:
            court_median = 0
            logger.info(
                f"Could not get the median value of the court {self.id}")
            logger.error(e)
        try:
            court_max = max(jurors.values())
        except Exception as e:
            court_max = 0
            logger.info(f"Could not get the max value of the court {self.id}")
            logger.error(e)

        return {
            'length': len(jurors.values()),
            'mean': court_mean,
            'median': court_median,
            'max': court_max,
            'total': sum(jurors.values())
        }

    @staticmethod
    def updateStatsAllCourts():
        courts = db.session.query(Court.id).all()
        pnkPrice = float(Config.get('PNKprice'))
        for court in courts:
            c = Court.query.filter_by(id=court.id).first()
            stats = c.juror_stats()
            c.meanStaked = int(stats['mean'])
            c.maxStaked = int(stats['max'])
            c.totalStaked = int(stats['total'])
            c.activeJurors = stats['length']
            c.disputesLast30days = len(c.disputes(30))
            c.minStakeUSD = c.minStake * pnkPrice
            db.session.add(c)
        db.session.commit()
        logger.debug("Stats of Courts updated")
Beispiel #8
0
class Dispute(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    number_of_choices = db.Column(db.Integer)
    subcourtID = db.Column(db.Integer,
                           db.ForeignKey("court.id"),
                           nullable=False)
    status = db.Column(db.Integer)
    arbitrated = db.Column(db.String(50))
    current_ruling = db.Column(db.Integer)
    period = db.Column(db.Integer)
    last_period_change = db.Column(db.DateTime)
    ruled = db.Column(db.Boolean)
    creator = db.Column(db.String(50))
    txid = db.Column(db.String(100))
    timestamp = db.Column(db.DateTime)
    blocknumber = db.Column(db.Integer)

    def __eq__(self, other):
        classes_match = isinstance(other, self.__class__)
        a, b = deepcopy(self.__dict__), deepcopy(other.__dict__)
        # compare based on equality our attributes, ignoring SQLAlchemy internal stuff
        a.pop('_sa_instance_state', None)
        b.pop('_sa_instance_state', None)
        attrs_match = (a == b)
        return classes_match and attrs_match

    def __str__(self):
        return f'Dispute(id={self.id}, court={self.subcourtID}, period={self.period}, ruled={self.ruled}, current_ruling={self.current_ruling})'

    def __ne__(self, other):
        return not self.__eq__(other)

    def rounds(self):
        return Round.query.filter_by(disputeID=self.id).all()

    @property
    def court(self):
        return Court.query.get(self.subcourtID)

    @property
    def period_name(self):
        period_name = {
            0: "Evidence",
            1: "Commit",
            2: "Vote",
            3: "Appeal",
            4: "Execution",
        }
        return period_name[self.period]

    @property
    def winner_choice_str(self):
        choice_name = {
            0: 'Refuse to Arbitrate',
            1: 'Yes',
            2: 'No',
            3: 'Tie',
            4: 'Not Ruled yet'
        }
        if self.winning_choice is not None:
            return choice_name[self.winning_choice]
        else:
            return choice_name[4]

    def delete_recursive(self):
        rounds = Round.query.filter(Round.disputeID == self.id)
        for r in rounds:
            r.delete_recursive()
        logger.info("Deleting Dispute %s" % self.id)
        db.session.delete(self)
        db.session.commit()

    @property
    def openCases(self):
        return self.query.filter(Dispute.ruled == 0).count()

    @property
    def ruledCases(self):
        return self.query.filter(Dispute.ruled == 1).count()

    @staticmethod
    def mostActiveCourt(days=7):
        """
        Most active cour in the last days

        Parameters
        ----------
        days : int, optional
            DESCRIPTION. Last Days to filter. The default is 7.

        Returns
        -------
        Court object with the most active Court in the last days.

        """
        filter_after = datetime.today() - timedelta(days=days)

        disputes = Dispute.query.filter(
            Dispute.timestamp >= filter_after).all()
        counts = {}
        for dispute in disputes:
            try:
                counts[dispute.subcourtID] += 1
            except KeyError:
                counts[dispute.subcourtID] = 1
        try:
            mostActive = max(counts, key=counts.get)
            return {mostActive: counts[mostActive]}
        except Exception:
            return None

    @staticmethod
    def timeEvolution():
        """
        Return the timestamp and Dispute amounts
        """
        disputes = db.session.query(Dispute.id, Dispute.timestamp).all()
        allDisputes = []
        for dispute in disputes:
            allDisputes.append({
                'timestamp': dispute.timestamp,
                'id': dispute.id
            })
        return allDisputes

    @staticmethod
    def disputesCountByCourt():
        data = Dispute.query.with_entities(Dispute.subcourtID, func.count(Dispute.id)).\
            group_by(Dispute.subcourtID).all()
        result = {}
        for item in data:
            result[Court(id=item[0]).map_name] = item[1]
        return result

    @staticmethod
    def disputesCountByArbitrated():
        data = Dispute.query.with_entities(Dispute.arbitrated, func.count(Dispute.id)).\
            group_by(Dispute.arbitrated).all()
        result = {}
        for item in data:
            name = ContractMapper.searchName(item[0])
            if name in list(result.keys()):
                result[name] += item[1]
            else:
                result[name] = item[1]
        return result

    @staticmethod
    def disputesByArbitrated(address):
        disputes = Dispute.query.filter(
            func.lower(Dispute.arbitrated) == address.lower()).order_by(
                Dispute.id.desc()).all()
        return list(disputes)

    @staticmethod
    def disputesByCreator(address):
        disputes = Dispute.query.filter(
            func.lower(Dispute.creator) == address.lower()).order_by(
                Dispute.id.desc()).all()
        return list(disputes)

    @staticmethod
    def disputesByCreator_paginated(address, page=0, per_page=10):
        return Dispute.query.filter(
            func.lower(Dispute.creator) == address.lower()).order_by(
                Dispute.id.desc()).paginate(page, per_page, error_out=False)

    @property
    def winning_choice(self):
        max_round_id = self.rounds()[-1].id
        if Dispute.query.filter_by(id=self.id).first().ruled:
            votes_query = db.session.execute(
                "select choice,count(*) as num_votes from vote \
                where round_id = :round_id and vote=1 \
                group by choice order by num_votes desc", {
                    'round_id': max_round_id
                }).fetchall()
            num_of_votes = [count[1] for count in votes_query]
            if len(num_of_votes) == 0:
                # all the votes are still pending
                return None
            if (len(num_of_votes) > 1) and (all(count == num_of_votes[0]
                                                for count in num_of_votes)):
                # it's a tie
                return 3
            else:
                return votes_query[0].items()[0][1]
        else:
            return None

    def coherency(self):
        """
        Percentage of coherent votes with the final result
        """
        if Dispute.query.filter_by(id=self.id).first().ruled:
            coherent_votes = 0
            no_coherent_votes = 0
            rounds = self.rounds()
            for r in rounds:
                for vote in r.votes():
                    if self.winning_choice != 3:
                        # if it's not a tie
                        if (vote.vote) and (vote.choice
                                            == self.winning_choice):
                            coherent_votes += 1
                        elif not vote.vote:
                            # if didn't vote, don't count it
                            # TODO! review if this is fine
                            pass
                        else:
                            no_coherent_votes += 1
                    else:

                        # if it's a tie, if voted it's coherent, if not, isn't
                        if vote.vote:
                            coherent_votes += 1
                        else:
                            no_coherent_votes += 1
            if coherent_votes + no_coherent_votes:
                return coherent_votes / (coherent_votes + no_coherent_votes)
            else:
                return None
        else:
            return None

    def yes_no_coherency(self):
        """
        Number of votes for yes or no that are coherent with the final result
        """
        coherency = {'Refuse': 0, 'Yes': 0, 'No': 0}
        incoherents = {'Refuse': 0, 'Yes': 0, 'No': 0, 'Pending': 0}
        mapper = {0: 'Refuse', 1: 'Yes', 2: 'No'}
        if Dispute.query.filter_by(id=self.id).first().ruled:
            for r in self.rounds():
                for vote in r.votes():
                    if self.winning_choice != 3:
                        # if it's not a tie
                        if (vote.vote) and (vote.choice
                                            == self.winning_choice):
                            coherency[mapper[vote.choice]] += 1
                        elif not vote.vote:
                            incoherents['Pending'] += 1
                        else:
                            incoherents[mapper[vote.choice]] += 1
                    else:
                        # if it's a tie, if voted it's coherent, if not, isn't
                        if vote.vote:
                            coherency[mapper[vote.choice]] += 1
                        else:
                            incoherents['Pending'] += 1
            return {'coherents': coherency, 'incoherents': incoherents}
        else:
            return None
class Court(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))
    address = db.Column(db.String(50))
    parent = db.Column(db.Integer, db.ForeignKey("court.id"), nullable=True)
    minStake = db.Column(db.Float)
    feeForJuror = db.Column(db.Float)
    voteStake = db.Column(db.Integer)
    meanStaked = db.Column(db.Float)
    maxStaked = db.Column(db.Float)
    totalStaked = db.Column(db.Float)
    activeJurors = db.Column(db.Integer)
    disputesLast30days = db.Column(db.Integer)
    minStakeUSD = db.Column(db.Float)

    def disputes(self, days=None):
        """
        Return the disputes in the previous days. If days is None, return all
        the disputes in that Court

        Parameters
        ----------
        days : int, optional
            Number of days to count the disputes backwards.
            The default is None.

        Returns
        -------
        List
            List of all the Disputes

        """
        if days:
            filter_after = (datetime.now() - timedelta(days=days)).replace(
                hour=0, minute=0, second=0)
            return Dispute.query \
                .filter(Dispute.subcourtID == self.id, Dispute.timestamp >= filter_after) \
                .order_by(Dispute.id.desc()).all()
        else:
            return Dispute.query.filter(
                Dispute.subcourtID == self.id).order_by(
                    Dispute.id.desc()).all()

    def children_ids(self):
        children_ids = []
        children = Court.query.filter(Court.parent == self.id)
        for child in children:
            children_ids.append(child.id)
        return children_ids

    @staticmethod
    def getAllCourtChilds(courtID):
        childs = set(Court(id=courtID).children_ids())
        allChilds = []
        while childs:
            child = childs.pop()
            allChilds.append(child)
            childs.update(Court(id=child).children_ids())
        return allChilds

    @property
    def ncourts(self):
        return Court.query.count()

    @property
    def jurors(self):
        allStakes = db.session.execute(
            "SELECT id,address,setStake, subcourtID \
                FROM juror_stake \
                WHERE id IN ( \
                    SELECT MAX(id) \
                    FROM juror_stake \
                    GROUP BY address,subcourtID);")
        stakedJurors = {}
        courts_id = self.getAllCourtChilds(self.id)
        courts_id.append(self.id)
        for stake in allStakes:
            if stake.setStake > 0 and stake.subcourtID in courts_id:
                if stake.address.lower() in stakedJurors.keys():
                    stakedJurors[stake.address.lower()] += stake.setStake
                else:
                    stakedJurors[stake.address.lower()] = stake.setStake
        return stakedJurors

    @property
    def map_name(self):
        if self.name:
            return self.name
        else:
            return self.query.filter(Court.id == self.id).first().name

    def juror_stats(self):
        jurors = self.jurors
        try:
            court_mean = statistics.mean(jurors.values())
        except Exception as e:
            court_mean = 0
            logger.info(f"Could not get the mean value of the court {self.id}")
            logger.error(e)
        try:
            court_median = statistics.median(jurors.values())
        except Exception as e:
            court_median = 0
            logger.info(
                f"Could not get the median value of the court {self.id}")
            logger.error(e)
        try:
            court_max = max(jurors.values())
        except Exception as e:
            court_max = 0
            logger.info(f"Could not get the max value of the court {self.id}")
            logger.error(e)

        return {
            'length': len(jurors.values()),
            'mean': court_mean,
            'median': court_median,
            'max': court_max,
            'total': sum(jurors.values())
        }

    @staticmethod
    def updateStatsAllCourts():
        courts = db.session.query(Court.id).all()
        pnkPrice = float(Config.get('PNKprice'))
        for court in courts:
            c = Court.query.filter_by(id=court.id).first()
            stats = c.juror_stats()
            c.meanStaked = int(stats['mean'])
            c.maxStaked = int(stats['max'])
            c.totalStaked = int(stats['total'])
            c.activeJurors = stats['length']
            c.disputesLast30days = len(c.disputes(30))
            c.minStakeUSD = c.minStake * pnkPrice
            db.session.add(c)
        db.session.commit()
        logger.debug("Stats of Courts updated")
class Dispute(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    number_of_choices = db.Column(db.Integer)
    subcourtID = db.Column(db.Integer,
                           db.ForeignKey("court.id"),
                           nullable=False)
    status = db.Column(db.Integer)
    arbitrated = db.Column(db.String(50))
    current_ruling = db.Column(db.Integer)
    period = db.Column(db.Integer)
    last_period_change = db.Column(db.DateTime)
    ruled = db.Column(db.Boolean)
    creator = db.Column(db.String(50))
    txid = db.Column(db.String(100))
    timestamp = db.Column(db.DateTime)
    blocknumber = db.Column(db.Integer)

    def rounds(self):
        return Round.query.filter_by(disputeID=self.id).all()

    @property
    def court(self):
        return Court.query.get(self.subcourtID)

    @property
    def period_name(self):
        period_name = {
            0: "Evidence",
            1: "Commit",
            2: "Vote",
            3: "Appeal",
            4: "Execution",
        }
        return period_name[self.period]

    def delete_recursive(self):
        rounds = Round.query.filter(Round.disputeID == self.id)
        for r in rounds:
            r.delete_recursive()
        logger.info("Deleting Dispute %s" % self.id)
        db.session.delete(self)
        db.session.commit()

    @property
    def openCases(self):
        openCases = self.query.filter(Dispute.ruled == 0).all()
        return len(openCases)

    @property
    def ruledCases(self):
        ruledCases = self.query.filter(Dispute.ruled == 1).all()
        return len(ruledCases)

    @staticmethod
    def mostActiveCourt(days=7):
        """
        Most active cour in the last days

        Parameters
        ----------
        days : int, optional
            DESCRIPTION. Last Days to filter. The default is 7.

        Returns
        -------
        Court object with the most active Court in the last days.

        """
        filter_after = datetime.today() - timedelta(days=days)

        disputes = Dispute.query.filter(
            Dispute.timestamp >= filter_after).all()
        counts = {}
        for dispute in disputes:
            try:
                counts[dispute.subcourtID] += 1
            except KeyError:
                counts[dispute.subcourtID] = 1
        try:
            mostActive = max(counts, key=counts.get)
            return {mostActive: counts[mostActive]}
        except Exception:
            return None

    @staticmethod
    def timeEvolution():
        """
        Return the timestamp and Dispute amounts
        """
        disputes = db.session.query(Dispute.id, Dispute.timestamp).all()
        allDisputes = []
        for dispute in disputes:
            allDisputes.append({
                'timestamp': dispute.timestamp,
                'id': dispute.id
            })
        return allDisputes

    @staticmethod
    def disputesCountByCourt():
        data = Dispute.query.with_entities(Dispute.subcourtID, func.count(Dispute.id)).\
            group_by(Dispute.subcourtID).all()
        result = {}
        for item in data:
            result[Court(id=item[0]).map_name] = item[1]
        return result

    @staticmethod
    def disputesCountByCreator():
        data = Dispute.query.with_entities(Dispute.creator, func.count(Dispute.id)).\
            group_by(Dispute.creator).all()
        result = {}
        for item in data:
            name = ContractMapper.searchName(item[0])
            if name in list(result.keys()):
                result[name] += item[1]
            else:
                result[name] = item[1]
        return result