from datetime import timedelta from django.utils.translation import gettext as _ from django.utils import timezone from django.utils.decorators import method_decorator from django.views.decorators.cache import cache_page from django.db.models import Sum from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.exceptions import NotFound from pingcap_sso.api.base import Endpoint from pingcap_sso.models import ( UserPointStat, PointProduct, UserAwardedPoint, PointDailyCheckIn, User ) from pingcap_sso.forms.point import ProductRedeemForm from pingcap_sso.lib.points import daily_checkin, redeem_product class UserInfoEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) def get(self, request, *args, **kwargs): stat = UserPointStat.get_stat(request.user) is_today_checked = bool(PointDailyCheckIn.objects.filter( user=request.user, date=timezone.localdate())) progress_at_current_level, min_exps, max_exps = stat.get_current_level_desc() return Response({ 'detail': _('success'), 'data': { 'current_level': stat.level, 'current_points': stat.points, 'current_exps': stat.experience, 'current_rank': stat.rank, 'is_today_checked': is_today_checked, 'level_desc': { 'min_exps': min_exps, 'max_exps': max_exps, 'progress': progress_at_current_level } } }) class UserPublicSummaryEndpoint(Endpoint): permission_classes = (AllowAny, ) def get(self, request, *args, **kwargs): username = kwargs['username'] try: user = User.objects.get(username=username) except Exception: raise NotFound stat = UserPointStat.get_stat(user) progress_at_current_level, min_exps, max_exps = stat.get_current_level_desc() return Response({ 'detail': _('success'), 'data': { 'user': { 'username': user.username, 'avatar_url': user.avatar_url }, 'current_level': stat.level, 'current_exps': stat.experience, 'current_points': stat.points, 'level_desc': { 'min_exps': min_exps, 'max_exps': max_exps, 'progress': progress_at_current_level } } }) class AwardedPointListEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) def get(self, request, *args, **kwargs): try: cur_page = int(request.GET.get('currentPage', 1)) per_page = int(request.GET.get('perPage', 10)) except Exception: return Response({ 'detail': _('wrong params') }, 400) records = request.user.awardedpoint_records.all().order_by('-created_at') total_num = records.count() records = records[(cur_page - 1) * per_page:cur_page * per_page] return Response({ 'detail': _('success'), 'data': [{ 'digest': r.digest, 'points': r.points, 'exps': r.experience, 'comment': r.comment, 'created_at': timezone.localtime(r.created_at).strftime('%Y-%m-%d') } for r in records], 'total_num': total_num, 'per_page': per_page, 'cur_page': cur_page }) class DailyCheckinEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) def post(self, request, *args, **kwargs): success, continues_checkin_count, points, tomorrow_points = daily_checkin(request.user, request) if not success: return Response({ 'detail': _('already checked in') }, status=409) return Response({ 'detail': _('success'), 'data': { 'continues_checkin_count': continues_checkin_count, 'points': points, 'tomorrow_points': tomorrow_points } }) class ShopProductListEndpoint(Endpoint): permission_classes = (AllowAny, ) def get(self, request, *args, **kwargs): return Response({ 'detail': _('success'), 'data': [{ 'id': p.id, 'name': p.name, 'cover_url': p.cover_url, 'points': p.points, 'remains': p.remains, } for p in PointProduct.objects.filter( deleted_by_user__isnull=True).order_by('sort_order')] }) class ShopRedeemEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) def post(self, request, *args, **kwargs): form = ProductRedeemForm(data=request.data) if not form.is_valid(): return Response({ 'detail': _('params error'), 'errors': form.errors }, status=400) record, comment = redeem_product( request, request.user, form.product, form.cleaned_data.get('consignee_name'), form.cleaned_data.get('consignee_phone'), form.cleaned_data.get('consignee_addrss'), form.cleaned_data.get('comment') ) if record: redeem_success, redeem_record_id = True, record.id else: redeem_success, redeem_record_id = False, None return Response({ 'detail': _('success'), 'data': { 'redeem_success': redeem_success, 'redeem_record_id': redeem_record_id, 'comment': comment, } }) class ShopRedeemRecordListEndpoint(Endpoint): permission_classes = (IsAuthenticated, ) def get(self, request, *args, **kwargs): records = request.user.pointredemptionrecords.all().order_by('-created_at') return Response({ 'detail': _('success'), 'data': [{ 'product_id': r.product.id, 'product_name': r.product.name, 'points': r.points, 'consignee_name': r.consignee_name, 'consignee_phone': r.consignee_phone, 'consignee_addrss': r.consignee_addrss, 'comment': r.comment, 'courier_company': r.courier_company, 'tracking_number': r.tracking_number, 'created_at': timezone.localtime(r.created_at).strftime('%Y-%m-%d/%H:%M') } for r in records] }) class TopListEndpoint(Endpoint): permission_classes = (AllowAny, ) @method_decorator(cache_page(timeout=60 * 10)) def get(self, request, *args, **kwargs): now = timezone.now() period_map = { 'all': now.replace(year=2000), 'weekly': now - timedelta(days=7), 'monthly': now - timedelta(days=31), 'yearly': now - timedelta(days=365), } try: limit = int(request.GET.get('limit', 50)) period = request.GET.get('period', 'all') if limit > 100 or limit < 1: raise Exception('limit should between 1 ~ 100') if period not in period_map: raise Exception('invalid period, it could be {}'.format( ', '.join(period_map.keys()))) except Exception as e: return Response({ 'detail': str(e) }, 400) exclude_user_ids = [ 545899, # TiDB社区小助手 605899, # 社区小助手 ] queryset = UserAwardedPoint.objects\ .select_related('target_user')\ .filter(created_at__gte=period_map[period])\ .exclude(target_user__id__in=exclude_user_ids)\ .values('target_user__username', 'target_user__avatar_url')\ .annotate(exps=Sum('experience'))\ .order_by('-exps')[:limit] data = [{ 'ranking': i + 1, 'user': { 'username': d['target_user__username'], 'avatar_url': d['target_user__avatar_url'], }, 'exps': d['exps'] } for i, d in enumerate(queryset)] return Response({ 'data': data, 'limit': limit, 'period': period })