def get(self, challenge_id): response = [] challenge = Challenges.query.filter_by(id=challenge_id).first_or_404() # TODO: Need a generic challenge visibility call. # However, it should be stated that a solve on a gated challenge is not considered private. if challenge.state == 'hidden' and is_admin() is False: abort(404) Model = get_model() solves = Solves.query.join(Model, Solves.account_id == Model.id)\ .filter(Solves.challenge_id == challenge_id, Model.banned == False, Model.hidden == False)\ .order_by(Solves.date.asc()) for solve in solves: response.append({ 'account_id': solve.account_id, 'name': solve.account.name, 'date': isoformat(solve.date) }) return { 'success': True, 'data': response }
def submissions_listing(submission_type): filters = {} if submission_type: filters["type"] = submission_type curr_page = abs(int(request.args.get("page", 1, type=int))) results_per_page = 50 page_start = results_per_page * (curr_page - 1) page_end = results_per_page * (curr_page - 1) + results_per_page sub_count = Submissions.query.filter_by(**filters).count() page_count = int( sub_count / results_per_page) + (sub_count % results_per_page > 0) Model = get_model() submissions = (Submissions.query.add_columns( Submissions.id, Submissions.type, Submissions.challenge_id, Submissions.provided, Submissions.account_id, Submissions.date, Challenges.name.label("challenge_name"), Model.name.label("team_name"), ).filter_by(**filters).join(Challenges).join(Model).order_by( Submissions.date.desc()).slice(page_start, page_end).all()) return render_template( "admin/submissions.html", submissions=submissions, page_count=page_count, curr_page=curr_page, type=submission_type, )
def solve(user, team, challenge, request): """ This method is used to insert Solves into the database in order to mark a challenge as solved. :param team: The Team object from the database :param chal: The Challenge object from the database :param request: The request the user submitted :return: """ chal = multiChallenge.query.filter_by(id=challenge.id).first() data = request.form or request.get_json() submission = data["submission"].strip() Model = get_model() solve = Solves( user_id=user.id, team_id=team.id if team else None, challenge_id=challenge.id, ip=get_ip(req=request), provided=submission, ) db.session.add(solve) solve_count = (Solves.query.join( Model, Solves.account_id == Model.id).filter( Solves.challenge_id == challenge.id, Model.hidden == False, Model.banned == False, ).count()) db.session.commit() db.session.close()
def calculate_value(cls, challenge): Model = get_model() solve_count = ( Solves.query.join(Model, Solves.account_id == Model.id) .filter( Solves.challenge_id == challenge.id, Model.hidden == False, Model.banned == False, ) .count() ) # If the solve count is 0 we shouldn't manipulate the solve count to # let the math update back to normal if solve_count != 0: # We subtract -1 to allow the first solver to get max point value solve_count -= 1 # It is important that this calculation takes into account floats. # Hence this file uses from __future__ import division value = ( ((challenge.minimum - challenge.initial) / (challenge.decay ** 2)) * (solve_count ** 2) ) + challenge.initial value = math.ceil(value) if value < challenge.minimum: value = challenge.minimum challenge.value = value db.session.commit() return challenge
def recalculate_awards(cls, challenge): """ Recalculate all of the awards after challenge has been edited or solves/users were removed You have to call db.session.commit() manually after this! """ Model = get_model() solves = (Solves.query.join( Model, Solves.account_id == Model.id).filter( Solves.challenge_id == challenge.id).order_by(Solves.id).all()) i = 0 for solve in solves: award = FirstBloodAward.query.filter( FirstBloodAward.solve_id == solve.id).first() if FirstBloodValueChallenge._can_get_award(challenge, solve): award_data = FirstBloodValueChallenge._gen_award_data( challenge, solve, i + 1) i += 1 else: award_data = None if award_data is not None: if award is not None: for k, v in award_data.items(): setattr(award, k, v) else: award = FirstBloodAward(**award_data) db.session.add(award) else: if award: db.session.delete(award)
def update(challenge, request): """ This method is used to update the information associated with a challenge. This should be kept strictly to the Challenges table and any child tables. :param challenge: :param request: :return: """ data = request.form or request.get_json() for attr, value in data.items(): setattr(challenge, attr, value) Model = get_model() solve_count = ( Solves.query.join(Model, Solves.account_id == Model.id) .filter( Solves.challenge_id == challenge.id, Model.hidden == False, Model.banned == False, ) .count() ) db.session.commit() return challenge
def calculate_value(cls, challenge): Model = get_model() solve_count = (Solves.query.join( Model, Solves.account_id == Model.id).filter( Solves.challenge_id == challenge.id, Model.hidden == False, Model.banned == False, ).count()) if solve_count == 0: solve_count = 1 # It is important that this calculation takes into account floats. # Hence this file uses from __future__ import division value = challenge.initial * ( 1.0 + math.log10(solve_count)**2 / 2.079)**(-1.5) value = math.ceil(value) if value < challenge.minimum: value = challenge.minimum challenge.value = value db.session.commit() return challenge
def solve(cls, user, team, challenge, request): super().solve(user, team, challenge, request) # Get the Solve object that was just inserted by solve() (that method should really return it :<) solve = Solves.query.filter(Solves.challenge_id == challenge.id) if user is not None: solve = solve.filter(Solves.user_id == user.id) if team is not None: solve = solve.filter(Solves.team_id == team.id) solve = solve.first() if FirstBloodValueChallenge._can_get_award(challenge, solve): # Figure out the solve number Model = get_model() solve_count = (Solves.query.join( Model, Solves.account_id == Model.id).filter( Solves.id <= solve.id, Solves.challenge_id == challenge.id, Model.hidden == False, Model.banned == False, ).count()) # Insert the award into the database award_data = FirstBloodValueChallenge._gen_award_data( challenge, solve, solve_count) if award_data is not None: award = FirstBloodAward(**award_data) db.session.add(award) db.session.commit()
def solve(user, team, challenge, request): chal = DynICPCModel.query.filter_by(id=challenge.id).first() Model = get_model() solve = Solves(user_id=user.id, team_id=team.id if team else None, challenge_id=challenge.id, ip=get_ip(request), provided=request.judge_result['submission_id']) db.session.add(solve) solve_count = (Solves.query.join( Model, Solves.account_id == Model.id).filter( Solves.challenge_id == challenge.id, not Model.hidden, not Model.banned, ).count()) # We subtract -1 to allow the first solver to get max point value solve_count -= 1 # It is important that this calculation takes into account floats. # Hence this file uses from __future__ import division value = (((chal.minimum - chal.initial) / (chal.decay**2)) * (solve_count**2)) + chal.initial value = math.ceil(value) if value < chal.minimum: value = chal.minimum chal.value = value db.session.commit() db.session.close()
def get(self, challenge_id): response = [] challenge = Challenges.query.filter_by(id=challenge_id).first_or_404() # TODO: Need a generic challenge visibility call. # However, it should be stated that a solve on a gated challenge is not considered private. if challenge.state == 'hidden' and is_admin() is False: abort(404) Model = get_model() solves = Solves.query.join(Model, Solves.account_id == Model.id)\ .filter(Solves.challenge_id == challenge_id, Model.banned == False, Model.hidden == False)\ .order_by(Solves.date.asc()) endpoint = None if get_config('user_mode') == TEAMS_MODE: endpoint = 'teams.public' arg = 'team_id' elif get_config('user_mode') == USERS_MODE: endpoint = 'users.public' arg = 'user_id' for solve in solves: response.append({ 'account_id': solve.account_id, 'name': solve.account.name, 'date': isoformat(solve.date), 'account_url': url_for(endpoint, **{arg: solve.account_id}) }) return { 'success': True, 'data': response }
def wrapper(user, team, challenge, request): chal_solve_func(user, team, challenge, request) notifier = get_configured_notifier() if notifier and bool(get_config('notifier_send_solves')): if get_mode_as_word() == TEAMS_MODE: solver = team solver_url = url_for("teams.public", team_id=solver.account_id, _external=True) else: solver = user solver_url = url_for("users.public", user_id=solver.account_id, _external=True) challenge_url = url_for('challenges.listing', _external=True, _anchor='{challenge.name}-{challenge.id}'.format(challenge=challenge)) Model = get_model() solve_count = ( db.session.query( db.func.count(Solves.id) ) .filter(Solves.challenge_id == challenge.id) .join(Model, Solves.account_id == Model.id) .filter(Model.banned == False, Model.hidden == False) .scalar() ) max_solves = get_config('notifier_solve_count') max_solves = int(max_solves) if max_solves is not None else None if max_solves is None or solve_count <= max_solves: notifier.notify_solve(get_config('notifier_solve_msg', '{solver} solved {challenge} ({solve_num} solve)'), solver.name, solver_url, challenge.name, challenge_url, solve_count)
def _build_solves_query(extra_filters=(), admin_view=False): # This can return None (unauth) if visibility is set to public user = get_current_user() # We only set a condition for matching user solves if there is a user and # they have an account ID (user mode or in a team in teams mode) if user is not None and user.account_id is not None: user_solved_cond = Solves.account_id == user.account_id else: user_solved_cond = false() # We have to filter solves to exclude any made after the current freeze # time unless we're in an admin view as determined by the caller. freeze = get_config("freeze") if freeze and not admin_view: freeze_cond = Solves.date < unix_time_to_utc(freeze) else: freeze_cond = true() # Finally, we never count solves made by hidden or banned users/teams, even # if we are an admin. This is to match the challenge detail API. AccountModel = get_model() exclude_solves_cond = and_( AccountModel.banned == false(), AccountModel.hidden == false(), ) # This query counts the number of solves per challenge, as well as the sum # of correct solves made by the current user per the condition above (which # should probably only be 0 or 1!) solves_q = (db.session.query( Solves.challenge_id, sa_func.count(Solves.challenge_id), sa_func.sum(cast(user_solved_cond, sa_types.Integer)), ).join(AccountModel).filter(*extra_filters, freeze_cond, exclude_solves_cond).group_by( Solves.challenge_id)) return solves_q
def attempt(challenge, request): """ This method is used to check whether a given input is right or wrong. It does not make any changes and should return a boolean for correctness and a string to be shown to the user. It is also in charge of parsing the user's input from the request itself. :param challenge: The Challenge object from the database :param request: The request the user submitted :return: (boolean, string) """ chal = SteamChallengeModel.query.filter_by(id=challenge.id).first() data = request.form or request.get_json() submission = data["submission"].strip() flags = Flags.query.filter_by(challenge_id=challenge.id).all() for flag in flags: if get_flag_class(flag.type).compare(flag, submission): Model = get_model() solve_count = ( Solves.query.join(Model, Solves.account_id == Model.id) .filter( Solves.challenge_id == challenge.id, Model.hidden == False, Model.banned == False, ) .count() ) if solve_count == 0: return True, "Congratulations - you were first to solve this, check the Steam Keys page for your key!" return True, "Correct" return False, "Incorrect"
def calculate_value(cls, challenge): Model = get_model() solve_count = (Solves.query.join( Model, Solves.account_id == Model.id).filter( Solves.challenge_id == challenge.id, Model.hidden == False, Model.banned == False, ).count()) # If the solve count is 0 we should manipulate the solve count to # let the math update back to normal if solve_count == 0: # Use 1 instead solve_count = 1 # It is important that this calculation takes into account floats. # Hence this file uses from __future__ import division value = ((1 + (math.log10(solve_count)**2) / challenge.decrease) **(-challenge.slope)) * challenge.initial value = math.ceil(value) challenge.value = value db.session.commit() return challenge
def get(self): challenges = Challenges.query\ .add_columns('id', 'name', 'state', 'max_attempts')\ .order_by(Challenges.value).all() Model = get_model() teams_with_points = db.session.query(Solves.account_id) \ .join(Model) \ .filter(Model.banned == False, Model.hidden == False) \ .group_by(Solves.account_id) \ .count() percentage_data = [] for challenge in challenges: solve_count = Solves.query.join(Model, Solves.account_id == Model.id) \ .filter(Solves.challenge_id == challenge.id, Model.banned == False, Model.hidden == False) \ .count() if teams_with_points > 0: percentage = (float(solve_count) / float(teams_with_points)) else: percentage = 0.0 percentage_data.append({ 'id': challenge.id, 'name': challenge.name, 'percentage': percentage, }) response = sorted(percentage_data, key=lambda x: x['percentage'], reverse=True) return {'success': True, 'data': response}
def statistics(): update_check() Model = get_model() teams_registered = Teams.query.count() users_registered = Users.query.count() wrong_count = (Fails.query.join(Model, Fails.account_id == Model.id).filter( Model.banned == False, Model.hidden == False).count()) solve_count = (Solves.query.join(Model, Solves.account_id == Model.id).filter( Model.banned == False, Model.hidden == False).count()) challenge_count = Challenges.query.count() ip_count = Tracking.query.with_entities(Tracking.ip).distinct().count() solves_sub = (db.session.query( Solves.challenge_id, db.func.count(Solves.challenge_id).label("solves_cnt")).join( Model, Solves.account_id == Model.id).filter( Model.banned == False, Model.hidden == False).group_by( Solves.challenge_id).subquery()) solves = (db.session.query( solves_sub.columns.challenge_id, solves_sub.columns.solves_cnt, Challenges.name, ).join(Challenges, solves_sub.columns.challenge_id == Challenges.id).all()) solve_data = {} for chal, count, name in solves: solve_data[name] = count most_solved = None least_solved = None if len(solve_data): most_solved = max(solve_data, key=solve_data.get) least_solved = min(solve_data, key=solve_data.get) db.session.close() return render_template( "admin/statistics.html", user_count=users_registered, team_count=teams_registered, ip_count=ip_count, wrong_count=wrong_count, solve_count=solve_count, challenge_count=challenge_count, solve_data=solve_data, most_solved=most_solved, least_solved=least_solved, )
def solve(user, team, challenge, request): """ This method is used to insert Solves into the database in order to mark a challenge as solved. :param team: The Team object from the database :param chal: The Challenge object from the database :param request: The request the user submitted :return: """ chal = DynamicChallenge.query.filter_by(id=challenge.id).first() data = request.form or request.get_json() submission = data['submission'].strip() Model = get_model() solve_count = Solves.query \ .join(Model, Solves.account_id == Model.id) \ .filter(Solves.challenge_id == challenge.id, Model.hidden == False, Model.banned == False) \ .count() # It is important that this calculation takes into account floats. # Hence this file uses from __future__ import division # value = ( # ( # (chal.minimum - chal.initial) / (chal.decay**2) # ) * (solve_count**2) # ) + chal.initial if (team and team.hidden == True) or (not team and user.hidden == True): pass else: value = chal.initial * 0.03 + ( (chal.initial * 0.97) / (1 + (max(0, solve_count) / 4.92201)**3.206069)) value = math.ceil(value) if value < chal.minimum: value = chal.minimum if chal.decay != 0 and solve_count >= chal.decay: value = chal.minimum chal.value = value solve = Solves(user_id=user.id, team_id=team.id if team else None, challenge_id=challenge.id, ip=get_ip(req=request), provided=submission) db.session.add(solve) db.session.commit() db.session.close()
def before_flush(session, flush_context, instances): Model = get_model() for instance in session.deleted: if isinstance(instance, Solves): # A solve has been deleted - delete any awards associated with this solve award_ids = FirstBloodAward.query.with_entities( FirstBloodAward.id).filter( FirstBloodAward.solve_id == instance.id).subquery() rowcount = Awards.query.filter( Awards.id.in_(award_ids)).delete(synchronize_session='fetch') if rowcount > 0: # Mark the awards for this challenge for recalculation if not hasattr(session, 'requires_award_recalculation'): session.requires_award_recalculation = set() session.requires_award_recalculation.add( Challenges.query.get(instance.challenge_id)) if isinstance(instance, Users): # A user has been deleted - mark all challenges where this user had awards for recalculation # NOTE: This doesn't seem to be used by CTFd - see after_bulk_delete for award in FirstBloodAward.query.join( Users, FirstBloodAward.user_id == Users.id).filter( Users.id == instance.id).all(): if not hasattr(session, 'requires_award_recalculation'): session.requires_award_recalculation = set() session.requires_award_recalculation.add(award.solve.challenge) session.delete(award) if isinstance(instance, Teams): # A team has been deleted - mark all challenges where this team had awards for recalculation for award in FirstBloodAward.query.join( Teams, FirstBloodAward.team_id == Teams.id).filter( Teams.id == instance.id).all(): if not hasattr(session, 'requires_award_recalculation'): session.requires_award_recalculation = set() session.requires_award_recalculation.add(award.solve.challenge) session.delete(award) for instance in session.dirty: if session.is_modified(instance): if isinstance(instance, Model): if get_history(instance, "hidden").has_changes() or get_history( instance, "banned").has_changes(): # The user/team hidden state has changed - update awards on all challenges this user has solved for solve in Solves.query.join( Challenges, Solves.challenge_id == Challenges.id).filter( Solves.account_id == instance.id, Challenges.type == "firstblood"): if not hasattr(session, 'requires_award_recalculation'): session.requires_award_recalculation = set() session.requires_award_recalculation.add( solve.challenge)
def solve(user, team, challenge, request): """ This method is used to insert Solves into the database in order to mark a challenge as solved. :param team: The Team object from the database :param chal: The Challenge object from the database :param request: The request the user submitted :return: """ challenge = GuessPenaltyChallenge.query.filter_by( id=challenge.id).first() data = request.form or request.get_json() submission = data["submission"].strip() Model = get_model() solve = Solves( user_id=user.id, team_id=team.id if team else None, challenge_id=challenge.id, ip=get_ip(req=request), provided=submission, ) # Issue penalty award fail_count = (Fails.query.join(Model, Fails.account_id == Model.id).filter( Fails.challenge_id == challenge.id, Model.hidden == False, Model.banned == False, ).count()) value = (((challenge.minimum - challenge.initial) / (challenge.decay**2)) * (fail_count**2)) + challenge.initial value = math.ceil(value) if value < challenge.minimum: value = challenge.minimum value = value - challenge.initial penalty = Awards(user_id=user.id, team_id=team.id if team else None, name="FAIL Penalty: %s" % challenge.name, description="Penalty for incorrect attempts", value=value, category=challenge.category, icon="") # Commit to database db.session.add(solve) db.session.add(penalty) db.session.commit()
def _can_get_award(cls, challenge, solve): # No awards for hidden challenges if challenge.state != 'visible': return False # No awards for hidden users Model = get_model() solver = Model.query.filter_by(id=solve.account_id).first() if solver.hidden or solver.banned: return False return True
def submissions_listing(submission_type): filters_by = {} if submission_type: filters_by["type"] = submission_type filters = [] q = request.args.get("q") field = request.args.get("field") page = abs(request.args.get("page", 1, type=int)) filters = build_model_filters( model=Submissions, query=q, field=field, extra_columns={ "challenge_name": Challenges.name, "account_id": Submissions.account_id, }, ) Model = get_model() submissions = ( Submissions.query.filter_by(**filters_by) .filter(*filters) .join(Challenges) .join(Model) .order_by(Submissions.date.desc()) .paginate(page=page, per_page=50) ) args = dict(request.args) args.pop("page", 1) return render_template( "admin/submissions.html", submissions=submissions, prev_page=url_for( request.endpoint, submission_type=submission_type, page=submissions.prev_num, **args ), next_page=url_for( request.endpoint, submission_type=submission_type, page=submissions.next_num, **args ), type=submission_type, q=q, field=field, )
def solve(user, team, challenge, request): """ This method is used to insert Solves into the database in order to mark a challenge as solved. :param team: The Team object from the database :param chal: The Challenge object from the database :param request: The request the user submitted :return: """ chal = AliyunChallenge.query.filter_by(id=challenge.id).first() data = request.form or request.get_json() submission = data["submission"].strip() Model = get_model() solve = Solves( user_id=user.id, team_id=team.id if team else None, challenge_id=challenge.id, ip=get_ip(req=request), provided=submission, ) db.session.add(solve) solve_count = ( Solves.query.join(Model, Solves.account_id == Model.id) .filter( Solves.challenge_id == challenge.id, Model.hidden == False, Model.banned == False, ) .count() ) # We subtract -1 to allow the first solver to get max point value solve_count -= 1 # It is important that this calculation takes into account floats. # Hence this file uses from __future__ import division value = ( ((chal.minimum - chal.initial) / (chal.decay ** 2)) * (solve_count ** 2) ) + chal.initial value = math.ceil(value) if value < chal.minimum: value = chal.minimum chal.value = value db.session.commit() db.session.close()
def _build_solves_query(extra_filters=(), admin_view=False): """Returns queries and data that that are used for showing an account's solves. It returns a tuple of - SQLAlchemy query with (challenge_id, solve_count_for_challenge_id) - Current user's solved challenge IDs """ # This can return None (unauth) if visibility is set to public user = get_current_user() # We only set a condition for matching user solves if there is a user and # they have an account ID (user mode or in a team in teams mode) AccountModel = get_model() if user is not None and user.account_id is not None: user_solved_cond = Solves.account_id == user.account_id else: user_solved_cond = false() # We have to filter solves to exclude any made after the current freeze # time unless we're in an admin view as determined by the caller. freeze = get_config("freeze") if freeze and not admin_view: freeze_cond = Solves.date < unix_time_to_utc(freeze) else: freeze_cond = true() # Finally, we never count solves made by hidden or banned users/teams, even # if we are an admin. This is to match the challenge detail API. exclude_solves_cond = and_( AccountModel.banned == false(), AccountModel.hidden == false(), ) # This query counts the number of solves per challenge, as well as the sum # of correct solves made by the current user per the condition above (which # should probably only be 0 or 1!) solves_q = ( db.session.query(Solves.challenge_id, sa_func.count(Solves.challenge_id),) .join(AccountModel) .filter(*extra_filters, freeze_cond, exclude_solves_cond) .group_by(Solves.challenge_id) ) # Also gather the user's solve items which can be different from above query # For example, even if we are a hidden user, we should see that we have solved a challenge # however as a hidden user we are not included in the count of the above query if admin_view: # If we're an admin we should show all challenges as solved to break through any requirements challenges = Challenges.query.all() solve_ids = {challenge.id for challenge in challenges} else: # If not an admin we calculate solves as normal solve_ids = ( Solves.query.with_entities(Solves.challenge_id) .filter(user_solved_cond) .all() ) solve_ids = {value for value, in solve_ids} return solves_q, solve_ids
def get(self, challenge_id): if is_admin(): chal = Challenges.query.filter(Challenges.id == challenge_id).first_or_404() else: chal = Challenges.query.filter( Challenges.id == challenge_id, and_(Challenges.state != 'hidden', Challenges.state != 'locked') ).first_or_404() chal_class = get_chal_class(chal.type) tags = [ tag['value'] for tag in TagSchema( "user", many=True).dump( chal.tags).data] files = [f.location for f in chal.files] unlocked_hints = set() hints = [] if authed(): user = get_current_user() unlocked_hints = set([u.target for u in HintUnlocks.query.filter_by( type='hints', account_id=user.account_id)]) for hint in Hints.query.filter_by(challenge_id=chal.id).all(): if hint.id in unlocked_hints or ctf_ended(): hints.append({'id': hint.id, 'cost': hint.cost, 'content': hint.content}) else: hints.append({'id': hint.id, 'cost': hint.cost}) response = chal_class.read(challenge=chal) Model = get_model() if scores_visible() is True and accounts_visible() is True: solves = Solves.query\ .join(Model, Solves.account_id == Model.id)\ .filter(Solves.challenge_id == chal.id, Model.banned == False, Model.hidden == False)\ .count() response['solves'] = solves else: response['solves'] = None response['files'] = files response['tags'] = tags response['hints'] = hints db.session.close() return { 'success': True, 'data': response }
def update(challenge, request): data = request.form or request.get_json() for attr, value in data.items(): if attr in ("initial", "minimum", "decay"): value = float(value) if attr in [ 'id', 'name', 'value', 'description', 'category', 'state', 'max_attempts', 'type', 'type_data', 'max_cpu_time', 'max_real_time', 'max_memory', 'max_process_number', 'max_output_size', 'max_stack' ]: setattr(challenge, attr, value) if challenge.problem_id != -1 and api.challenge_prepared( challenge.problem_id): try: api.update_problem(challenge.problem_id, limits={ i: int(data[i]) for i in [ 'max_cpu_time', 'max_real_time', 'max_memory', 'max_process_number', 'max_output_size', 'max_stack' ] }) except AssertionError: log('programming', '[{date}] update problem error') Model = get_model() solve_count = (Solves.query.join( Model, Solves.account_id == Model.id).filter( Solves.challenge_id == challenge.id, not Model.hidden, not Model.banned, ).count()) # It is important that this calculation takes into account floats. # Hence this file uses from __future__ import division value = (((challenge.minimum - challenge.initial) / (challenge.decay**2)) * (solve_count**2)) + challenge.initial value = math.ceil(value) if value < challenge.minimum: value = challenge.minimum challenge.value = value db.session.commit() return challenge
def get_unmatched_standings(count=None, admin=False, fields=[]): """ Get standings as a list of tuples containing account_id, name, and score e.g. [(account_id, team_name, score)]. Ties are broken by who reached a given score first based on the solve ID. Two users can have the same score but one user will have a solve ID that is before the others. That user will be considered the tie-winner. Challenges & Awards with a value of zero are filtered out of the calculations to avoid incorrect tie breaks. """ if fields is None: fields = [] Model = get_model() team_ids = get_team_ids() sumscores = get_scores(admin) """ Admins can see scores for all users but the public cannot see banned users. Filters out banned users. Properly resolves value ties by ID. Different databases treat time precision differently so resolve by the row ID instead. """ if admin: standings_query = (db.session.query( Model.id.label("account_id"), Model.oauth_id.label("oauth_id"), Model.name.label("name"), Model.hidden, Model.banned, sumscores.columns.score, *fields, ).join(sumscores, Model.id == sumscores.columns.account_id).filter( Model.id.notin_(team_ids)).order_by(sumscores.columns.score.desc(), sumscores.columns.id)) else: standings_query = (db.session.query( Model.id.label("account_id"), Model.oauth_id.label("oauth_id"), Model.name.label("name"), sumscores.columns.score, *fields, ).join(sumscores, Model.id == sumscores.columns.account_id).filter( Model.banned == False, Model.hidden == False).filter(Model.id.notin_(team_ids)).order_by( sumscores.columns.score.desc(), sumscores.columns.id)) """ Only select a certain amount of users if asked. """ if count is None: standings = standings_query.all() else: standings = standings_query.limit(count).all() return standings
def solve(user, team, challenge, request): """ This method is used to insert Solves into the database in order to mark a challenge as solved. :param team: The Team object from the database :param chal: The Challenge object from the database :param request: The request the user submitted :return: """ chal = ADAChallenge.query.filter_by(id=challenge.id).first() data = request.form or request.get_json() submission = data["submission"].strip() Model = get_model() if Model == Users: attacker = GlowwormContainers.query.filter_by(user_id=user.id, challenge_id=challenge.id).first() attacker_name = user.name victim = GlowwormContainers.query.filter_by(flag=submission).first() victim_name = Users.query.filter_by(id=victim.user_id).first() team_id = None else: attacker = GlowwormContainers.query.filter_by(user_id=team.id, challenge_id=challenge.id).first() attacker_name = team.name victim = GlowwormContainers.query.filter_by(flag=submission).first() victim_name = Teams.query.filter_by(id=victim.user_id).first() team_id = victim_name.team_id attack = GlowwormAttacks( attack_id = attacker.user_id, attack_name = attacker_name, victim_id = victim.user_id, victim_name = victim_name.name, docker_id = victim.docker_id, envname = victim.docker_id.split("_",1)[1], flag = submission, round = get_round() ) attack_log = GlowwormAttackLog( user_id=user.id, team_id=team.id if team else None, victim_user_id=victim_name.id, victim_team_id=team_id, challenge_id=challenge.id, ip=get_ip(req=request), provided=submission, ) db.session.add(attack) db.session.add(attack_log) db.session.commit() db.session.close()
def submissions_listing(submission_type): filters_by = {} if submission_type: filters_by["type"] = submission_type filters = [] q = request.args.get("q") field = request.args.get("field") page = abs(request.args.get("page", 1, type=int)) if q: submissions = [] if Submissions.__mapper__.has_property( field): # The field exists as an exposed column filters.append(getattr(Submissions, field).like("%{}%".format(q))) Model = get_model() submissions = (Submissions.query.add_columns( Submissions.id, Submissions.type, Submissions.challenge_id, Submissions.provided, Submissions.account_id, Submissions.date, Challenges.name.label("challenge_name"), Model.name.label("team_name"), ).filter_by(**filters_by).filter( *filters).join(Challenges).join(Model).order_by( Submissions.date.desc()).paginate(page=page, per_page=50)) args = dict(request.args) args.pop("page", 1) return render_template( "admin/submissions.html", submissions=submissions, prev_page=url_for(request.endpoint, submission_type=submission_type, page=submissions.prev_num, **args), next_page=url_for(request.endpoint, submission_type=submission_type, page=submissions.next_num, **args), type=submission_type, q=q, field=field, )
def update(challenge, request): """ This method is used to update the information associated with a challenge. This should be kept strictly to the Challenges table and any child tables. :param challenge: :param request: :return: """ data = request.form or request.get_json() for attr, value in data.items(): # We need to set these to floats so that the next operations don't operate on strings if attr in ('initial', 'minimum', 'decay'): value = float(value) setattr(challenge, attr, value) Model = get_model() solve_count = Solves.query \ .join(Model, Solves.account_id == Model.id) \ .filter(Solves.challenge_id == challenge.id, Model.hidden == False, Model.banned == False) \ .count() # It is important that this calculation takes into account floats. # Hence this file uses from __future__ import division # value = (((challenge.minimum - challenge.initial) / (challenge.decay ** 2)) * (solve_count ** 2)) + challenge.initial # value = math.ceil(value) # if value < challenge.minimum: # value = challenge.minimum value = challenge.initial * 0.03 + ( # designed for 20 team at most (challenge.initial * 0.97) / (1 + (max(0, solve_count - 1) / 4.92201)**3.206069)) value = math.ceil(value) if value < challenge.minimum: value = challenge.minimum if challenge.decay != 0 and solve_count >= challenge.decay: value = challenge.minimum challenge.value = value db.session.commit() return challenge
def manual_list_to_grade(): Model = get_model() submissions = (Pending.query.add_columns( Pending.id, Pending.type, Pending.challenge_id, Pending.provided, Pending.account_id, Pending.date, Challenges.name.label("challenge_name"), Model.name.label("team_name"), ).join(Challenges).join(Model).order_by(Pending.date.desc())).all() return render_template('submissions.html', submissions=submissions)