class ControlledView(View):
    http_method_names = ['post', 'get']
    access_manager = acl.AccessManager()

    def get(self, request):
        return self.pre_process(request, request.GET)

    def post(self, request):
        return self.pre_process(request, request.POST)

    def pre_process(self, request, input_data: dict):
        """ Validates input and attempts to preform work() logic. Returns the correct JsonResponse """

        # Authenticate Access
        if not self.authenticate(request):
            error_response = self.return_error_response(request)
            if error_response is None:
                raise Http404()
            return error_response
        try:
            return self.process(request, input_data)
        except Exception as e:
            error = InternalServerError(e)
            request_info = self.format_request_info(request, input_data)
            logging.error(request_info)
            error.log()

            if not settings.DEBUG:
                error.email_log_to_dev(request_info=request_info,
                                       user=request.user)

            return error.json_response(include_message=False)

    def authenticate(self, request):
        """ To be overridden if necessary. Should still be called with super """
        return self.access_manager.check_user(request.user)

    def return_error_response(self, request) -> HttpResponse:
        pass

    def process(self, request, input_data):
        pass

    @staticmethod
    def format_request_info(request, input_data):
        return '\tRequest: {}\n\tRaw Input: {}'.format(str(request),
                                                       str(input_data))
Example #2
0
class DefendStartView(ApiView):
    request_form_class = RequestForm
    access_manager = acl.AccessManager(acl_accept=[acl.groups.USER])

    def work(self, request, req: dict, res: dict):
        # Get the attempt
        attempt = StealAttempt.objects.filter(
            id=req['steal_attempt_id'],
            status=StealAttempt.Status.WAITING_FOR_DEFENSE_START,
            victim__player__user=request.user).all()
        if len(attempt) == 0:
            raise ValidationError('Invalid steal_attempt_id')
        attempt = attempt[0]

        # Update the status
        attempt.status = StealAttempt.Status.WAITING_FOR_DEFENSE_END
        attempt.save()
Example #3
0
class AttackEndView(ApiView):
    request_form_class = RequestForm
    access_manager = acl.AccessManager(acl_accept=[acl.groups.USER])

    def work(self, request, req: dict, res: dict):
        # Get the attempt
        attempt = StealAttempt.objects.filter(
            id=req['steal_attempt_id'],
            status=StealAttempt.Status.WAITING_FOR_THIEF_END,
            thief__player__user=request.user).all()
        if len(attempt) == 0:
            raise ValidationError('Invalid steal_attempt_id')
        attempt = attempt[0]

        # Save the results
        attempt.thief_score = req['score']
        attempt.status = StealAttempt.Status.WAITING_FOR_DEFENSE_START
        attempt.save()
class PageView(View):
    template_name = None
    context = None
    access_manager = acl.AccessManager()
    allowed_after_current_hackathon_ends = True

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.args = list()
        self.kwargs = dict()

    def get(self, request, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs

        # Authenticate Access
        if not self.authenticate(request):
            # Access denied
            return redirect(self.get_access_denied_redirect_url(request))

        self.work(request)
        return render(request, template_name=self.template_name, context=None)

    def work(self, request):
        """
            To be overridden. Preform any extra logic here. Fill context here for rendering.

            If any context data could be added to the template later using the API with javascript, do that instead
            of grabbing it here. This should only be needed when it determines a major page layout feature that would
            look weird not initialized on html load.
        """
        pass

    def authenticate(self, request):
        """ To be overridden if necessary. Should still be called with super """
        return self.access_manager.check_user(request.user)

    @staticmethod
    def get_access_denied_redirect_url(request):
        """ May be overwritten if desired """
        if request.user.is_authenticated:
            return '/user/profile/?accessDenied=true'
        else:
            return '/user/login/?accessDenied=true&path=' + request.path
Example #5
0
class StartView(ApiView):
    request_form_class = RequestForm
    access_manager = acl.AccessManager(acl_accept=[acl.groups.USER])

    def work(self, request: HttpRequest, req: dict, res: dict):
        # Retrieve the game
        game = Game.objects.filter(id=req['game_id'],
                                   creator__user=request.user,
                                   status=Game.Status.START_PENDING).all()
        if len(game) == 0:
            raise ValidationError('Invalid game_id')
        game = game[0]

        # Start the game
        game.status = Game.Status.ACTIVE
        game.start_time = timezone.now()
        game.end_time = game.start_time + timedelta(
            minutes=game.duration_in_minutes)
        game.save()
Example #6
0
class DefendEndView(ApiView):
    request_form_class = RequestForm
    access_manager = acl.AccessManager(acl_accept=[acl.groups.USER])

    def work(self, request, req: dict, res: dict):
        # Get the attempt
        attempt = StealAttempt.objects.filter(
            id=req['steal_attempt_id'],
            status=StealAttempt.Status.WAITING_FOR_DEFENSE_END,
            victim__player__user=request.user).all()
        if len(attempt) == 0:
            raise ValidationError('Invalid steal_attempt_id')
        attempt = attempt[0]

        # Finalize the attempt
        attempt.victim_score = req['score']
        attempt.coins_stolen = attempt.calculate_coins_stolen()
        attempt.save()
        finalize_steal_attempt(attempt)
Example #7
0
class PlayersView(ApiView):
    request_form_class = RequestForm
    response_form_class = ResponseForm
    access_manager = acl.AccessManager(acl_accept=[acl.groups.USER])

    def work(self, request, req: dict, res: dict):
        # Get the game
        game = Game.objects.filter(id=req['game_id']).all()
        if len(game) == 0:
            raise ValidationError('Invalid game_id')
        game = game[0]

        player_instances = []
        for pi in PlayerInstance.objects.filter(game=game).all():
            player_instances.append({
                'id': pi.id,
                'coins': pi.coins,
                'username': pi.player.user.username
            })

        res['player_instances'] = player_instances
Example #8
0
class GoalView(ApiView):
    request_form_class = RequestForm
    response_form_class = ResponseForm
    access_manager = acl.AccessManager(acl_accept=[acl.groups.USER])

    def work(self, request, req: dict, res: dict):
        goal = Goal.objects.filter(id=req['goal_id'], user=request.user).all()
        if len(goal) == 0:
            raise ValidationError('Invalid goal_id')
        goal = goal[0]

        # Breadth-first sum of child goals' tasks
        total_child_tasks = 0
        total_child_tasks_completed = 0
        goal_q = Queue()
        goal_q.put(goal)

        while not goal_q.empty():
            curr_goal = goal_q.get()

            # Add counts for this goal
            tasks = Task.objects.filter(parent_goal=curr_goal)
            total_child_tasks += tasks.count()
            total_child_tasks_completed += tasks.filter(completed=True).count()

            # Add any sub goals to queue
            for new_goal in Goal.objects.filter(parent_goal=curr_goal).all():
                goal_q.put(new_goal)

        # Make sure completed is accurate
        completed = total_child_tasks == 0 or total_child_tasks_completed /total_child_tasks == 1
        if goal.completed is not completed:
            goal.completed = completed
            goal.save()

        # Set response
        res['title'] = goal.title
        res['completed'] = completed
        res['total_child_tasks'] = total_child_tasks
        res['total_child_tasks_completed'] = total_child_tasks_completed
class GoalView(ApiView):
    request_form_class = RequestForm
    response_form_class = ResponseForm
    access_manager = acl.AccessManager(acl_accept=[acl.groups.USER])

    def work(self, request, req: dict, res: dict):
        parent_goal = None
        if req['parent_goal_id'] is not None:
            parent_goal = Goal.objects.filter(id=req['parent_goal_id'],
                                              user=request.user).all()
            if len(parent_goal) == 0:
                raise ValidationError('Invalid parent_goal_id')
            parent_goal = parent_goal[0]

        goal = Goal.objects.create(parent_goal=parent_goal,
                                   user=request.user,
                                   title=req['title'],
                                   description=req['description'],
                                   default_location=req['default_location'],
                                   default_priority=req['default_priority'])

        res['goal_id'] = goal.id
Example #10
0
class AttackStartView(ApiView):
    request_form_class = RequestForm
    response_form_class = ResponseForm
    access_manager = acl.AccessManager(acl_accept=[acl.groups.USER])

    def work(self, request, req: dict, res: dict):
        # Get the game and players
        game = get_valid_game_with_status(game_id=req['game_id'],
                                          status=Game.Status.ACTIVE)
        victim = get_valid_player_instance(game, req['victim_instance_id'],
                                           'Invalid victim_instance_id')
        thief = get_valid_player_instance_from_user(
            game, request.user, 'Invalid thief, player not in game')

        # Make sure not self
        if victim.id == thief.id:
            raise ValidationError('Cannot steal from self self')

        # Make sure victim is in range of attacker
        dist = location.distance_in_meters(thief.player, victim.player)
        if dist > game.maximum_steal_distance_in_meters:
            raise ValidationError('Victim out of steal range')

        # Make sure thief hasn't already attacked them recently

        if has_attacked_player_recently(game=game, thief=thief, victim=victim):
            raise ValidationError(
                'Cannot steal from player, you have already attempted a steal from them recently'
            )

        # All good, start the steal
        attempt = StealAttempt.objects.create(game=game,
                                              thief=thief,
                                              victim=victim)

        res['steal_attempt_id'] = attempt.id
class LogOutView(ApiView):
    http_method_names = ['post', 'get']
    access_manager = acl.AccessManager(acl_accept=[acl.groups.USER])

    def work(self, request, req, res):
        logout(request=request)
class ApiView(View):
    http_method_names = ['post']  # Override to allow GET
    request_form_class = forms.Form  # Override each time
    response_form_class = forms.Form  # Override each time
    access_manager = acl.AccessManager()
    allowed_after_current_hackathon_ends = True
    validate_response = False

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.kwargs = list()
        self.args = list()

    def get(self, request: HttpRequest, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
        return self.process(request, request.GET)

    def post(self, request: HttpRequest, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
        return self.process(request, request.POST)

    def process(self, request: HttpRequest, input_data: dict):
        """ Validates input and attempts to preform work() logic. Returns the correct JsonResponse """

        # Authenticate Access
        if not self.authenticate(request):
            return JsonResponse({'cause': _('Unauthorized')}, status=401)

        req = None
        res = None

        # Preform request
        try:
            # Validate & clean request
            request_form = self.request_form_class(input_data, request.FILES)
            if not request_form.is_valid():
                raise ExternalUserError(
                    ValidationError(request_form.errors.as_data()))
            req = request_form.cleaned_data
            res = {}

            # Preform desired api task and populate response (res) object
            try:
                self.work(request, req, res)
            except ValidationError as e:
                raise ExternalUserError(e)

            # Validate response
            response_form = self.response_form_class(res)
            if not response_form.is_valid() and self.validate_response:
                raise InternalServerError(
                    ValidationError(response_form.errors.as_data()))
            res = response_form.cleaned_data

            # Successful api call!
            return JsonResponse(res)

        except ExternalUserError as error:
            if settings.DEBUG:
                logging.error(
                    format_request_info(request, input_data, res, req))
                error.log()
            return error.json_response()
        except InternalServerError as error:
            request_info = format_request_info(request, input_data, res, req)
            logging.error(request_info)
            error.log()

            if not settings.DEBUG:
                error.email_log_to_dev(request_info=request_info,
                                       user=request.user)

            return error.json_response(include_message=False)
        except Exception as e:
            error = InternalServerError(e)
            request_info = format_request_info(request, input_data, res, req)
            logging.error(request_info)
            error.log()

            if not settings.DEBUG:
                error.email_log_to_dev(request_info=request_info,
                                       user=request.user)

            return error.json_response(include_message=False)

    def work(self, request: HttpRequest, req: dict, res: dict):
        """
            Preforms api request logic
            :param request Django request object (only use this if necessary)
            :param req cleaned and valid request data
            :param res response data to be checked after this call
        """
        pass

    def authenticate(self, request):
        """ To be overridden if necessary. Should still be called with super """
        return self.access_manager.check_user(request.user)
class FindNearbyPlayersView(ApiView):
    request_form_class = RequestForm
    response_form_class = ResponseForm
    access_manager = acl.AccessManager(acl_accept=[acl.groups.USER])

    def work(self, request, req: dict, res: dict):
        # Get the game
        game = Game.objects.filter(id=req['game_id']).all()
        if len(game) == 0:
            raise ValidationError('Invalid game_id')
        game = game[0]
        if game.status != Game.Status.ACTIVE:
            raise ValidationError('Game not active')

        # Get player & update location
        player = Player.objects.get(user=request.user)
        player.location_lat = req['location_lat']
        player.location_lng = req['location_lng']
        player.location_updated_at = timezone.now()
        player.save()

        # Get pi
        thief_instance = PlayerInstance.objects.filter(game=game,
                                                       player=player).all()
        if len(thief_instance) == 0:
            raise ValidationError('Invalid game, not a player in game ' +
                                  game.name)
        thief_instance = thief_instance[0]

        mvd = game.max_view_distance_in_meters()
        msd = game.maximum_steal_distance_in_meters

        # Only pull nearby players from database, then check each more precisely
        spread = location.CardinalSpread(origin_lat=float(player.location_lat),
                                         origin_lng=float(player.location_lng),
                                         radius_in_meters=float(mvd))
        nearby_pis = PlayerInstance.objects.filter(
            game=game,
            player__location_lat__lt=spread.north.lat,
            player__location_lat__gt=spread.south.lat,
            player__location_lng__lt=spread.east.lng,
            player__location_lng__gt=spread.west.lng).all()

        pis_in_steal_range = []
        pis_in_view_range = []
        pis_in_cool_down = []
        for pi_obj in nearby_pis:
            if not pi_obj.player.has_valid_location():
                continue

            dist = location.distance_in_meters(player, pi_obj.player)
            if dist > mvd:
                continue

            pi = {
                'distance_in_meters': round(dist, 3),
                'game_id': pi_obj.game.id,
                'player_instance_id': pi_obj.id,
                'username': pi_obj.player.user.username,
                'location_lat': float(pi_obj.player.location_lat),
                'location_lng': float(pi_obj.player.location_lng)
            }

            if dist <= msd:
                # Check for a cool down
                # min_recent_steal_time = timezone.now() - timedelta(seconds=game.steal_cool_down_seconds)
                # recent_attempts = StealAttempt.objects.filter(
                #     game=game, thief__player=player, victim=pi, created__gte=min_recent_steal_time
                # ).all()
                # if len(recent_attempts) > 0:
                #     cde = recent_attempts[0].created + timedelta(seconds=game.steal_cool_down_seconds)
                #     pi['cool_down_end_time'] = cde.isoformat()
                #     pis_in_cool_down.append(pi)
                # else:
                pis_in_steal_range.append(pi)

            else:
                pis_in_view_range.append(pi)

        res['pis_in_steal_range'] = pis_in_steal_range
        res['pis_in_view_range'] = pis_in_view_range
        res['steal_radius_in_meters'] = msd
        res['cardinal_spread'] = spread.to_dict()