Skip to content

Commit

Permalink
Merge branch 'develop' into feature/158-disable-recaptcha-4-admin
Browse files Browse the repository at this point in the history
  • Loading branch information
Cj-bc authored Sep 9, 2018
2 parents 434f946 + bd30bac commit 5c09599
Show file tree
Hide file tree
Showing 12 changed files with 358 additions and 85 deletions.
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pytest = "*"
watchdog = "*"
pillow = "*"
qrcode = "*"
numpy = "*"

[dev-packages]
"flake8" = "*"
Expand Down
126 changes: 81 additions & 45 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

91 changes: 79 additions & 12 deletions api/draw.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@
from flask import current_app
from api.models import Lottery, Application, db
from itertools import chain
from numpy.random import choice


class GroupAdvantage:
@staticmethod
def minimum(group):
return min(app.advantage for app in group)

@staticmethod
def average(group):
group = list(group)
return sum(app.advantage for app in group) / len(group)

@staticmethod
def rep(group):
return next(filter(lambda app: app.is_rep, group)).advantage


group_advantage_calculation = GroupAdvantage.average


def draw_one(lottery):
Expand All @@ -22,6 +41,12 @@ def draw_one(lottery):
if len(applications) == 0:
winners = []
else:
for app in applications:
# set a new field
app.advantage = calc_advantage(
app.user.win_count, app.user.lose_count)
set_group_advantage(applications)

winners_num = current_app.config['WINNERS_NUM']

won_group_members = draw_one_group_members(applications, winners_num)
Expand Down Expand Up @@ -50,16 +75,20 @@ def draw_one_group_members(applications, winners_num):
loser_reps = []

def set_group_result(rep, is_won):
status, to_apps, to_reps = \
("won", winner_apps, winner_reps) if is_won \
else ("lose", loser_apps, loser_reps)
status, to_apps, to_reps, win, lose = \
("won", winner_apps, winner_reps, 1, 0) if is_won \
else ("lose", loser_apps, loser_reps, 0, 1)

rep.status = status
rep.user.win_count += win
rep.user.lose_count += lose
to_apps.append(rep)
to_reps.append(rep)

for member in rep.group_members:
member.own_application.status = status
member.own_application.user.win_count += win
member.own_application.user.lose_count += lose
to_apps.append(member.own_application)

def unset_group_result(rep, from_apps, from_reps):
Expand All @@ -68,15 +97,17 @@ def unset_group_result(rep, from_apps, from_reps):
for member in rep.group_members:
from_apps.remove(member.own_application)

reps = [app for app in applications if app.is_rep]
reps_with_index = [(i, app)
for i, app in enumerate(applications) if app.is_rep]

# How likely is a rep to win
probability = min(winners_num / len(applications), 1)
all_probabilities = calc_probabilities(applications)

for rep in reps:
set_group_result(rep, random.random() < probability)
for i, rep in reps_with_index:
set_group_result(rep,
random.random() < all_probabilities[i] * winners_num)

n_group_members = sum(len(rep_app.group_members) + 1 for rep_app in reps)
n_group_members = sum(len(rep_app.group_members) + 1
for _, rep_app in reps_with_index)
n_normal_users = len(applications) - n_group_members

# when too few groups accidentally won
Expand All @@ -103,11 +134,15 @@ def draw_one_normal_users(applications, winners_num):
add applications to the session
"""
normal_users = [app for app in applications if app.status == "pending"]
try:
winner_apps = random.sample(normal_users, winners_num)
except ValueError:

if len(applications) <= winners_num or not normal_users:
# if applications are less than winners_num, all applications win
winner_apps = normal_users
else:
winner_apps = choice(
normal_users, winners_num,
replace=False, # no duplication
p=calc_probabilities(normal_users))

for application in normal_users:
application.status = "won" if application in winner_apps else "lose"
Expand Down Expand Up @@ -135,3 +170,35 @@ def draw_all_at_index(index):
db.session.commit()

return winners


def calc_probabilities(applications):
"""
calculate the probability of each application
return list of the weight of each application showing how likely
the application is to be chosen in comparison with others
the sum of the list is 1
"""
sum_advantage = sum(app.advantage for app in applications)
return [app.advantage / sum_advantage for app in applications]


def calc_advantage(win_count, lose_count):
"""
returns multiplier indicating how more likely
the application is to win
"""
if lose_count == 0:
return 1
else:
return max(1, lose_count - win_count)


def set_group_advantage(apps):
group_apps = (chain([rep], (member.own_application
for member in rep.group_members))
for rep in apps if rep.is_rep)
for group in group_apps:
advantage = group_advantage_calculation(group)
for app in group:
app.advantage = advantage
4 changes: 4 additions & 0 deletions api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ class User(db.Model):
DB contents:
public_id (int): public id.
secret_id (int): secret id.
win_count (int): how many times the user won
lose_count (int): how many times the user lost
"""
id = db.Column(db.Integer, primary_key=True)
public_id = db.Column(db.Integer, unique=True)
secret_id = db.Column(db.String(40), unique=True)
authority = db.Column(db.String(20))
win_count = db.Column(db.Integer, default=0)
lose_count = db.Column(db.Integer, default=0)
kind = db.Column(db.String(30))
first_access = db.Column(db.Date, default=None)

Expand Down
Loading

0 comments on commit 5c09599

Please sign in to comment.