def index(request, event=None): event = viewutil.get_event(event) eventParams = {} if event.id: eventParams['event'] = event.id agg = Donation.objects.filter( transactionstate='COMPLETED', testdonation=False, **eventParams ).aggregate( amount=Cast(Coalesce(Sum('amount'), 0), output_field=FloatField()), count=Count('amount'), max=Cast(Coalesce(Max('amount'), 0), output_field=FloatField()), avg=Cast(Coalesce(Avg('amount'), 0), output_field=FloatField()), ) agg['target'] = float(event.targetamount) count = { 'runs': filters.run_model_query('run', eventParams).count(), 'prizes': filters.run_model_query('prize', eventParams).count(), 'bids': filters.run_model_query('bid', eventParams).count(), 'donors': filters.run_model_query('donorcache', eventParams) .values('donor') .distinct() .count(), } if 'json' in request.GET: return HttpResponse( json.dumps({'count': count, 'agg': agg}, ensure_ascii=False,), content_type='application/json;charset=utf-8', ) return views_common.tracker_response( request, 'tracker/index.html', {'agg': agg, 'count': count, 'event': event} )
def run_detail(request, pk): try: run = SpeedRun.objects.get(pk=pk) runners = run.runners.all() event = run.event bids = filters.run_model_query('bid', {'run': pk}) bids = (viewutil.get_tree_queryset_descendants( Bid, bids, include_self=True).select_related( 'speedrun', 'event', 'parent').prefetch_related('options')) topLevelBids = [bid for bid in bids if bid.parent is None] return views_common.tracker_response( request, 'tracker/run.html', { 'event': event, 'run': run, 'runners': runners, 'bids': topLevelBids }, ) except SpeedRun.DoesNotExist: return views_common.tracker_response(request, template='tracker/badobject.html', status=404)
def draw_prize_winners(request): currentEvent = viewutil.get_selected_event(request) params = {'feed': 'todraw'} if currentEvent is not None: params['event'] = currentEvent.id prizes = search_filters.run_model_query('prize', params, user=request.user) if request.method == 'POST': form = forms.DrawPrizeWinnersForm(prizes=prizes, data=request.POST) if form.is_valid(): for prize in form.cleaned_data['prizes']: status = True while status and not prize.maxed_winners(): status, data = prizeutil.draw_prize( prize, seed=form.cleaned_data['seed']) prize.error = data['error'] if not status else '' logutil.change(request, prize, 'Prize Drawing') return render( request, 'admin/draw_prize_winners_post.html', {'prizes': form.cleaned_data['prizes']}, ) else: form = forms.DrawPrizeWinnersForm(prizes=prizes) return render(request, 'admin/draw_prize_winners.html', {'form': form})
def runindex(request, event=None): event = viewutil.get_event(event) if not event.id: return views_common.tracker_response( request, 'tracker/eventlist.html', { 'pattern': 'tracker:runindex', 'subheading': 'Runs' }, ) searchParams = {} searchParams['event'] = event.id runs = filters.run_model_query('run', searchParams) runs = runs.annotate(hasbids=Sum('bids')) return views_common.tracker_response( request, 'tracker/runindex.html', { 'runs': runs, 'event': event }, )
def get_query(self, q, request): params = {'q': q} params.update(self.get_extra_params(request)) model = getattr(self, 'modelName', self.model) if self.useLock and not request.user.has_perm('tracker.can_edit_locked_events'): params['locked'] = False return filters.run_model_query(model, params, request.user)
def user_index(request): eventSet = {} for futureEvent in filters.run_model_query('event', {'feed': 'future'}): if not futureEvent.locked: eventDict = eventSet.setdefault(futureEvent, {'event': futureEvent}) eventDict['submission'] = futureEvent for prize in models.Prize.objects.filter(handler=request.user): eventDict = eventSet.setdefault(prize.event, {'event': prize.event}) prizeList = eventDict.setdefault('prizes', []) prizeList.append(prize) eventList = [] for key, value in eventSet.items(): value['eventname'] = value['event'].name value['eventid'] = value['event'].id value.setdefault('submission', False) eventList.append(value) eventList.sort(key=lambda x: x['event'].date) return views_common.tracker_response(request, 'tracker/user_index.html', { 'eventList': eventList, })
def test_hidden_states_with_permission(self): for state in ['PENDING', 'HIDDEN', 'DENIED']: actual = run_model_query( 'allbids', {'feed': 'all', 'state': state}, self.hidden_user ) expected = self.query.filter(state=state) self.assertSetEqual(set(actual), set(expected))
def post_donation_to_postbacks(donation): event_donations = filters.run_model_query('donation', {'event': donation.event.id}) total = event_donations.aggregate(amount=Sum('amount'))['amount'] data = { 'id': donation.id, 'timereceived': str(donation.timereceived), 'comment': donation.comment, 'amount': donation.amount, 'donor__visibility': donation.donor.visibility, 'donor__visiblename': donation.donor.visible_name(), 'new_total': total, 'domain': donation.domain, } try: data_json = json.dumps( data, ensure_ascii=False, cls=serializers.json.DjangoJSONEncoder).encode('utf-8') postbacks = models.PostbackURL.objects.filter(event=donation.event) for postback in postbacks: opener = urllib.request.build_opener() req = urllib.request.Request( postback.url, data_json, headers={'Content-Type': 'application/json; charset=utf-8'}, ) opener.open(req, timeout=5) except Exception: viewutil.tracker_log('postback_url', traceback.format_exc(), event=donation.event)
def get_queryset(self, request): params = {} if not request.user.has_perm('tracker.can_edit_locked_events'): params['locked'] = False return search_filters.run_model_query('prizewinner', params, user=request.user)
def prizeindex(request, event=None): if not getattr(settings, 'SWEEPSTAKES_URL', None): raise Http404 event = viewutil.get_event(event) if not event.id: return views_common.tracker_response( request, 'tracker/eventlist.html', { 'pattern': 'tracker:prizeindex', 'subheading': 'Prizes' }, ) searchParams = {} searchParams['event'] = event.id prizes = filters.run_model_query('prize', searchParams) prizes = prizes.select_related( 'startrun', 'endrun', 'category').prefetch_related('prizewinner_set') return views_common.tracker_response( request, 'tracker/prizeindex.html', { 'prizes': prizes, 'event': event }, )
def get_queryset(self, request): params = {} if not request.user.has_perm('tracker.can_edit_locked_events'): params['locked'] = False if request.user.has_perm('tracker.view_pending_donation'): params['feed'] = 'all' return search_filters.run_model_query('donation', params, user=request.user)
def get_queryset(self, request): event = viewutil.get_selected_event(request) params = {} if not request.user.has_perm('tracker.can_edit_locked_events'): params['locked'] = False if event: params['event'] = event.id return search_filters.run_model_query('run', params, user=request.user)
def get_queryset(self, request): params = {} if request.user.has_perm('tracker.view_hidden_bid'): params['feed'] = 'all' if not request.user.has_perm('tracker.can_edit_locked_events'): params['locked'] = False return search_filters.run_model_query('allbids', params, user=request.user)
def runindex(request, event=None): event = viewutil.get_event(event) if not event.id: return HttpResponseRedirect( reverse('tracker:runindex', args=(Event.objects.latest().short,)) ) searchParams = {} searchParams['event'] = event.id runs = filters.run_model_query('run', searchParams) runs = runs.annotate(hasbids=Sum('bids')) return views_common.tracker_response( request, 'tracker/runindex.html', {'runs': runs, 'event': event}, )
def get_donation_prize_info(donation): """ Attempts to find a list of all prizes this donation gives the donor eligibility for. Does _not_ attempt to relate this information to any _past_ eligibility. Returns the set as a list of {'prize','amount'} dictionaries. """ prizeList = [] for timeprize in search_filters.run_model_query( 'prize', { 'feed': 'current', 'time': donation.timereceived, 'noslice': True, }, ): contribAmount = get_donation_prize_contribution(timeprize, donation) if contribAmount is not None: prizeList.append({'prize': timeprize, 'amount': contribAmount}) return prizeList
def prizeindex(request, event=None): event = viewutil.get_event(event) if not event.id: return HttpResponseRedirect( reverse('tracker:prizeindex', args=(Event.objects.latest().short,)) ) searchParams = {} searchParams['event'] = event.id prizes = filters.run_model_query('prize', searchParams) prizes = prizes.select_related('startrun', 'endrun', 'category').prefetch_related( 'prizewinner_set' ) return views_common.tracker_response( request, 'tracker/prizeindex.html', {'prizes': prizes, 'event': event}, )
def submit_prize(request, event): event = viewutil.get_event(event) # TODO: locked events should 404 here if request.method == 'POST': prizeForm = forms.PrizeSubmissionForm(data=request.POST) if prizeForm.is_valid(): prize = prizeForm.save(event, request.user) return views_common.tracker_response( request, 'tracker/submit_prize_success.html', {'prize': prize}) else: prizeForm = forms.PrizeSubmissionForm() runs = filters.run_model_query('run', {'event': event}, request.user) def run_info(run): return { 'id': run.id, 'name': run.name, 'description': run.description, 'runners': run.deprecated_runners, 'starttime': run.starttime.isoformat(), 'endtime': run.endtime.isoformat(), } dumpArray = [run_info(o) for o in runs.all()] runsJson = json.dumps(dumpArray) return views_common.tracker_response( request, 'tracker/submit_prize.html', { 'event': event, 'form': prizeForm, 'runs': runsJson }, )
def search(request): search_params = QueryDict.copy(request.GET) search_type = single(search_params, 'type') queries = present(search_params, 'queries') donor_names = present(search_params, 'donor_names') all_comments = present(search_params, 'all_comments') tech_notes = present(search_params, 'tech_notes') Model = modelmap.get(search_type, None) if Model is None: raise KeyError('%s is not a recognized model type' % search_type) if queries and not request.user.has_perm('tracker.view_queries'): raise PermissionDenied # TODO: move these to a lookup table? if donor_names: if search_type != 'donor': raise KeyError('"donor_names" can only be applied to donor searches') if not request.user.has_perm('tracker.view_usernames'): raise PermissionDenied if all_comments: if search_type != 'donation': raise KeyError('"all_comments" can only be applied to donation searches') if not request.user.has_perm('tracker.view_comments'): raise PermissionDenied if tech_notes: if search_type != 'run': raise KeyError('"tech_notes" can only be applied to run searches') if not request.user.has_perm('tracker.can_view_tech_notes'): raise PermissionDenied offset = int(single(search_params, 'offset', 0)) limit = getattr(settings, 'TRACKER_PAGINATION_LIMIT', DEFAULT_PAGINATION_LIMIT) limit_param = int(single(search_params, 'limit', limit)) if limit_param > limit: raise ValueError('limit can not be above %d' % limit) if limit_param < 1: raise ValueError('limit must be at least 1') limit = min(limit, limit_param) qs = search_filters.run_model_query(search_type, search_params, request.user,) qs = qs[offset : (offset + limit)] # Django 3.1 doesn't like Model.Meta.ordering when combined with annotations, so this guarantees the # correct subset when using annotations, even if it does result in an extra query if search_type in annotations: qs = ( Model.objects.filter(pk__in=(m.pk for m in qs)) .annotate(**annotations[search_type]) .order_by() ) if search_type in related: qs = qs.select_related(*related[search_type]) if search_type in prefetch: qs = qs.prefetch_related(*prefetch[search_type]) include_fields = included_fields.get(search_type, {}) result = TrackerSerializer(Model, request).serialize( qs, fields=include_fields.get('__self__', None) ) objs = {o.id: o for o in qs} related_cache = {} for obj in result: base_obj = objs[int(obj['pk'])] if hasattr(base_obj, 'visible_name'): obj['fields']['public'] = base_obj.visible_name() else: obj['fields']['public'] = str(base_obj) for a in annotations.get(search_type, {}): func = annotation_coercions.get(search_type, {}).get(a, str) obj['fields'][a] = func(getattr(base_obj, a)) for prefetched_field in prefetch.get(search_type, []): if '__' in prefetched_field: continue obj['fields'][prefetched_field] = [ po.id for po in getattr(base_obj, prefetched_field).all() ] for related_field in related.get(search_type, []): related_object = base_obj for field in related_field.split('__'): if not related_object: break if not related_object._meta.get_field(field).serialize: related_object = None else: related_object = getattr(related_object, field) if not related_object: continue if related_object not in related_cache: related_cache[related_object] = ( TrackerSerializer(type(related_object), request).serialize( [related_object], fields=include_fields.get(related_field, None) ) )[0] related_data = related_cache[related_object] for field, values in related_data['fields'].items(): if field.endswith('id'): continue obj['fields'][related_field + '__' + field] = values if hasattr(related_object, 'visible_name'): obj['fields'][ related_field + '__public' ] = related_object.visible_name() else: obj['fields'][related_field + '__public'] = str(related_object) if search_type == 'donor' and not donor_names: donor_privacy_filter(obj['fields']) elif search_type == 'donation' and not all_comments: donation_privacy_filter(obj['fields']) elif search_type == 'run' and not tech_notes: run_privacy_filter(obj['fields']) resp = HttpResponse( json.dumps(result, ensure_ascii=False, cls=DjangoJSONEncoder), content_type='application/json;charset=utf-8', ) if queries: return HttpResponse( json.dumps(connection.queries, ensure_ascii=False, indent=1), content_type='application/json;charset=utf-8', ) # TODO: cache control for certain kinds of searches return resp
def donate(request, event): event = viewutil.get_event(event) if event.locked or not event.allow_donations: raise Http404 bundle = webpack_manifest.load( os.path.abspath( os.path.join(os.path.dirname(__file__), '../ui-tracker.manifest.json')), settings.STATIC_URL, debug=settings.DEBUG, timeout=60, read_retry=None, ) commentform, bidsform = process_form(request, event) if not bidsform: # redirect return commentform def bid_parent_info(bid): if bid is not None: return { 'id': bid.id, 'name': bid.name, 'description': bid.description, 'parent': bid_parent_info(bid.parent), 'custom': bid.allowuseroptions, } else: return None def bid_info(bid): result = { 'id': bid.id, 'name': bid.name, 'description': bid.description, 'label': bid.full_label(not bid.allowuseroptions), 'count': bid.count, 'amount': bid.total, 'goal': Decimal(bid.goal or '0.00'), 'parent': bid_parent_info(bid.parent), } if bid.speedrun: result['runname'] = bid.speedrun.name result['order'] = bid.speedrun.order else: result['runname'] = 'Event Wide' result['order'] = 0 if bid.allowuseroptions: result['custom'] = True result['maxlength'] = bid.option_max_length return result bids = search_filters.run_model_query('allbids', { 'state': 'OPENED', 'event': event.id }).select_related('parent', 'speedrun') prizes = search_filters.run_model_query('prize', { 'feed': 'current', 'event': event.id }) bidsArray = [bid_info(o) for o in bids] def prize_info(prize): result = { 'id': prize.id, 'name': prize.name, 'description': prize.description, 'minimumbid': prize.minimumbid, 'sumdonations': prize.sumdonations, 'url': reverse('tracker:prize', args=(prize.id, )), 'image': prize.image, } return result prizesArray = [prize_info(o) for o in prizes.all()] def to_json(value): if hasattr(value, 'id'): return value.id return value initialForm = { k: to_json(commentform.cleaned_data[k]) for k, v in list(commentform.fields.items()) if commentform.is_bound and k in commentform.cleaned_data } pickedIncentives = [{ k: to_json(form.cleaned_data[k]) for k, v in list(form.fields.items()) if k in form.cleaned_data } for form in bidsform.forms if form.is_bound] return render( request, 'ui/index.html', { 'event': event, 'events': Event.objects.all(), 'bundle': bundle.tracker, 'CONSTANTS': mark_safe(json.dumps(constants())), 'ROOT_PATH': reverse('tracker:ui:index'), 'app': 'TrackerApp', 'title': 'Donation Tracker', 'forms': { 'bidsform': bidsform }, 'form_errors': mark_safe( json.dumps({ 'commentform': json.loads(commentform.errors.as_json()), 'bidsform': bidsform.errors, })), 'props': mark_safe( json.dumps( { 'event': json.loads(serializers.serialize( 'json', [event]))[0]['fields'], 'minimumDonation': float(event.minimumdonation), 'prizes': prizesArray, 'incentives': bidsArray, 'initialForm': initialForm, 'initialIncentives': pickedIncentives, 'donateUrl': request.get_full_path(), 'prizesUrl': request.build_absolute_uri( reverse('tracker:prizeindex', args=(event.id, ))), 'steamLogin': request.build_absolute_uri( reverse( 'tracker:social:begin', args=["steam"], )) + '?next=' + request.get_full_path(), 'steamDisconnect': request.build_absolute_uri( reverse('tracker:disconnect_steam')) + '?next=' + request.get_full_path(), 'steamID': request.session.get('uid'), 'totalDonated': request.session['steam_donation_total'], }, ensure_ascii=False, cls=serializers.json.DjangoJSONEncoder, )), }, )
def donate(request, event): event = viewutil.get_event(event) if event.locked or not event.allow_donations: raise Http404 bundle = webpack_manifest.load( os.path.abspath( os.path.join(os.path.dirname(__file__), '../ui-tracker.manifest.json')), settings.STATIC_URL, debug=settings.DEBUG, timeout=60, read_retry=None, ) commentform, bidsform = process_form(request, event) if not bidsform: # redirect return commentform def bid_parent_info(bid): if bid is not None: return { 'id': bid.id, 'name': bid.name, 'description': bid.description, 'parent': bid_parent_info(bid.parent), 'custom': bid.allowuseroptions, } else: return None def bid_info(bid): result = { 'id': bid.id, 'name': bid.name, 'description': bid.description, 'label': bid.full_label(not bid.allowuseroptions), 'count': bid.count, 'amount': bid.total, 'goal': Decimal(bid.goal or '0.00'), 'parent': bid_parent_info(bid.parent), } if bid.speedrun: result['runname'] = bid.speedrun.name result['order'] = bid.speedrun.order else: result['runname'] = 'Event Wide' result['order'] = 0 if bid.allowuseroptions: result['custom'] = True result['maxlength'] = bid.option_max_length return result bids = search_filters.run_model_query('allbids', { 'state': 'OPENED', 'event': event.id }).select_related('parent', 'speedrun') prizes = search_filters.run_model_query('prize', { 'feed': 'current', 'event': event.id }) # You have to try really hard to get into this state so it's reasonable to blow up spectacularly when it happens if prizes and not getattr(settings, 'SWEEPSTAKES_URL', None): raise ImproperlyConfigured( 'There are prizes available but no SWEEPSTAKES_URL is set') bidsArray = [bid_info(o) for o in bids] def prize_info(prize): result = { 'id': prize.id, 'name': prize.name, 'description': prize.description, 'minimumbid': prize.minimumbid, 'sumdonations': prize.sumdonations, 'url': reverse('tracker:prize', args=(prize.id, )), 'image': prize.image, } return result prizesArray = [prize_info(o) for o in prizes.all()] def to_json(value): if hasattr(value, 'id'): return value.id return value initialForm = { k: to_json(commentform.cleaned_data[k]) for k, v in commentform.fields.items() if commentform.is_bound and k in commentform.cleaned_data } pickedIncentives = [{ k: to_json(form.cleaned_data[k]) for k, v in form.fields.items() if k in form.cleaned_data } for form in bidsform.forms if form.is_bound] return render( request, 'ui/index.html', { 'event': event, 'events': Event.objects.all(), 'bundle': bundle.tracker, 'CONSTANTS': constants(), 'ROOT_PATH': reverse('tracker:ui:index'), 'app_name': 'TrackerApp', 'title': 'Donation Tracker', 'forms': { 'bidsform': bidsform }, 'form_errors': { 'commentform': json.loads(commentform.errors.as_json()), 'bidsform': bidsform.errors, }, 'props': { 'event': json.loads(serializers.serialize('json', [event]))[0]['fields'], 'minimumDonation': float(event.minimumdonation), 'prizes': prizesArray, 'incentives': bidsArray, 'initialForm': initialForm, 'initialIncentives': pickedIncentives, 'donateUrl': request.get_full_path(), 'prizesUrl': request.build_absolute_uri( reverse('tracker:prizeindex', args=(event.id, ))), }, }, )
def donate(request, event): event = viewutil.get_event(event) if event.locked or not event.allow_donations: raise Http404 commentform, bidsform = process_form(request, event) if not bidsform: # redirect return commentform def bid_parent_info(bid): if bid is not None: return { 'name': bid.name, 'description': bid.description, 'parent': bid_parent_info(bid.parent), } else: return None def bid_info(bid): result = { 'id': bid.id, 'name': bid.name, 'description': bid.description, 'label': bid.full_label(not bid.allowuseroptions), 'count': bid.count, 'amount': bid.total, 'goal': Decimal(bid.goal or '0.00'), 'parent': bid_parent_info(bid.parent), } if bid.speedrun: result['runname'] = bid.speedrun.name if bid.suggestions.exists(): result['suggested'] = list([x.name for x in bid.suggestions.all()]) if bid.allowuseroptions: result['custom'] = ['custom'] result[ 'label'] += ' (select and add a name next to "New Option Name")' return result bids = (filters.run_model_query( 'bidtarget', { 'state': 'OPENED', 'event': event.id }, user=request.user).distinct().select_related( 'parent').prefetch_related('suggestions')) prizes = filters.run_model_query('prize', { 'feed': 'current', 'event': event.id }) dumpArray = [bid_info(o) for o in bids] bidsJson = json.dumps(dumpArray, ensure_ascii=False, cls=serializers.json.DjangoJSONEncoder) def prize_info(prize): result = { 'id': prize.id, 'name': prize.name, 'description': prize.description, 'minimumbid': prize.minimumbid, 'maximumbid': prize.maximumbid, 'sumdonations': prize.sumdonations, } return result return views_common.tracker_response( request, 'tracker/donate.html', { 'event': event, 'bidsform': bidsform, 'commentform': commentform, 'hasBids': bids.count() > 0, 'bidsJson': bidsJson, 'prizes': prizes, }, )
def test_hidden_states_without_permission(self): for state in ['PENDING', 'HIDDEN', 'DENIED']: with self.assertRaises(PermissionDenied): run_model_query('allbids', {'state': state})
def test_public_states(self): for state in ['OPENED', 'CLOSED']: actual = run_model_query('allbids', {'state': state}) expected = self.query.filter(state=state) self.assertSetEqual(set(actual), set(expected))