HackAssistant/registration

View on GitHub
stats/views.py

Summary

Maintainability
F
6 days
Test Coverage
from django.conf import settings
from django.db.models import Count, Sum, F
from django.db.models.functions import TruncDate, TruncHour
from django.http import JsonResponse
from django.urls import reverse
from django.utils import timezone
from django_tables2 import SingleTableMixin

from app.views import TabsView
from applications.models import HackerApplication, APP_CONFIRMED, APP_ATTENDED, D_OTHER, \
    VolunteerApplication, MentorApplication, SponsorApplication, APP_DUBIOUS, APP_INVALID, APP_BLACKLISTED
from organizers.models import Vote
from stats.tables import CheckinRankingListTable, OrganizerRankingListTable
from user.mixins import is_organizer, IsOrganizerMixin
from user.models import User
from checkin.models import CheckIn

from collections import defaultdict


def stats_tabs():
    tabs = [('Hacker', reverse('app_stats'), False), ('Volunteer', reverse('volunteer_stats'), False),
            ('Mentor', reverse('mentor_stats'), False), ('Sponsor', reverse('sponsor_stats'), False),
            ('Users', reverse('users_stats'), False), ('Check-in', reverse('checkin_stats'), False),
            ('Organizers', reverse('organizer_stats'), False)]
    if getattr(settings, 'REIMBURSEMENT_ENABLED', False):
        tabs.append(('Reimbursements', reverse('reimb_stats'), False))
    return tabs


def get_stats(model):
    hacker = model == HackerApplication
    volunteer = model == VolunteerApplication
    mentor = model == MentorApplication
    sponsor = model == SponsorApplication
    # Status analysis
    applications = list(model.objects.all())
    status_count = defaultdict(int)
    gender_count = defaultdict(int)
    origin_count = defaultdict(int)
    origin_count_confirmed = defaultdict(int)
    university_count = defaultdict(int)
    university_count_confirmed = defaultdict(int)
    grad_year_count = defaultdict(int)
    grad_year_count_confirmed = defaultdict(int)
    degree_count = defaultdict(int)
    degree_count_confirmed = defaultdict(int)
    first_timer_count = defaultdict(int)
    first_timer_count_confirmed = defaultdict(int)
    shirt_count = defaultdict(int)
    shirt_count_confirmed = defaultdict(int)
    diet_count = defaultdict(int)
    diet_count_confirmed = defaultdict(int)
    lennyface_count = defaultdict(int)
    lennyface_count_confirmed = defaultdict(int)
    resume_count = defaultdict(int)
    resume_count_confirmed = defaultdict(int)
    study_work_count = defaultdict(int)
    study_work_count_confirmed = defaultdict(int)
    attendance_count = defaultdict(int)
    attendance_count_confirmed = defaultdict(int)
    other_diet = []
    for a in applications:
        status_count[a.get_status_display()] += 1
        shirt_count[a.get_tshirt_size_display()] += 1
        diet_count[a.get_diet_display()] += 1
        if hacker or volunteer or mentor:
            origin_count[a.origin] += 1
            gender_count[a.get_gender_display()] += 1
        if hacker or volunteer:
            university_count[a.university] += 1
            grad_year_count[a.graduation_year] += 1
            degree_count[a.degree] += 1
            first_timer_count[a.first_timer] += 1
        if hacker:
            lennyface_count[a.lennyface] += 1
            resume_count['Resume'] += a.resume != ""
            resume_count['No resume'] += a.resume == ""
        if mentor and a.study_work:
            study_work_count['Student'] += 1
        elif mentor:
            study_work_count['Worker'] += 1
        if sponsor or volunteer or mentor:
            for day in a.get_attendance_display().split(', '):
                attendance_count[day] += 1

        if not sponsor and a.status == APP_CONFIRMED:
            shirt_count_confirmed[a.get_tshirt_size_display()] += 1
            diet_count_confirmed[a.get_diet_display()] += 1
            if hacker or volunteer or mentor:
                origin_count_confirmed[a.origin] += 1
            if hacker or volunteer:
                university_count_confirmed[a.university] += 1
                grad_year_count_confirmed[a.graduation_year] += 1
                degree_count_confirmed[a.degree] += 1
                first_timer_count_confirmed[a.first_timer] += 1
            if hacker:
                lennyface_count_confirmed[a.lennyface] += 1
                resume_count_confirmed['Resume'] += a.resume != ""
                resume_count_confirmed['No resume'] += a.resume == ""
            if mentor and a.study_work:
                study_work_count_confirmed['Student'] += 1
            elif mentor:
                study_work_count_confirmed['Worker'] += 1
            if a.diet == D_OTHER and a.other_diet:
                other_diet.append(a.other_diet)
            if volunteer or mentor:
                for day in a.get_attendance_display().split(', '):
                    attendance_count_confirmed[day] += 1

    shirt_count = [{'tshirt': x, 'applications': v} for (x, v) in shirt_count.items()]
    shirt_count_confirmed = [{'tshirt': x, 'applications': v} for (x, v) in shirt_count_confirmed.items()]

    diet_count = [{'diet': x, 'applications': v} for (x, v) in diet_count.items()]
    diet_count_confirmed = [{'diet': x, 'applications': v} for (x, v) in diet_count_confirmed.items()]

    origin_count = [{'origin': x, 'applications': v} for (x, v) in sorted(origin_count.items(),
                                                                          key=lambda item: item[1])[-10:]]
    origin_count_confirmed = [{'origin': x, 'applications': v} for (x, v) in sorted(origin_count_confirmed.items(),
                                                                                    key=lambda item: item[1])[-10:]]

    university_count = [{'university': x, 'applications': v} for (x, v) in sorted(university_count.items(),
                                                                                  key=lambda item: item[1])[-10:]]
    university_count_confirmed = [{'university': x, 'applications': v} for (x, v) in
                                  sorted(university_count_confirmed.items(), key=lambda item: item[1])[-10:]]

    degree_count = [{'degree': x, 'applications': v} for (x, v) in
                    sorted(degree_count.items(), key=lambda item: item[1])[-10:]]
    degree_count_confirmed = [{'degree': x, 'applications': v} for (x, v) in
                              sorted(degree_count_confirmed.items(), key=lambda item: item[1])[-10:]]

    lennyface_count = [{'lennyface': x, 'applications': v} for (x, v) in
                       sorted(lennyface_count.items(), key=lambda item: item[1])[-5:]]
    lennyface_count_confirmed = [{'lennyface': x, 'applications': v} for (x, v) in
                                 sorted(lennyface_count_confirmed.items(), key=lambda item: item[1])[-5:]]

    attendance_count = [{'attendance': x, 'applications': v} for (x, v) in attendance_count.items()]
    attendance_count_confirmed = [{'attendance': x, 'applications': v} for (x, v) in
                                  attendance_count_confirmed.items()]

    timeseries = model.objects.all().annotate(date=TruncDate('submission_date')).values('date').annotate(
        applications=Count('date'))

    return JsonResponse(
        {
            'timeseries': list(timeseries),
            'update_time': timezone.now(),
            'app_count': len(applications),
            'status': status_count,
            'tshirt': shirt_count,
            'tshirt_confirmed': shirt_count_confirmed,
            'gender': gender_count,
            'university': university_count,
            'university_confirmed': university_count_confirmed,
            'graduation_year': grad_year_count,
            'graduation_year_confirmed': grad_year_count_confirmed,
            'degree': degree_count,
            'degree_confirmed': degree_count_confirmed,
            'first_timer': first_timer_count,
            'first_timer_confirmed': first_timer_count_confirmed,
            'origin': origin_count,
            'origin_confirmed': origin_count_confirmed,
            'diet': diet_count,
            'diet_confirmed': diet_count_confirmed,
            'lennyface': lennyface_count,
            'lennyface_confirmed': lennyface_count_confirmed,
            'resume': resume_count,
            'resume_confirmed': resume_count_confirmed,
            'other_diet': '<br>'.join([el for el in other_diet]),
            'study_work': study_work_count,
            'study_work_confirmed': study_work_count_confirmed,
            'attendance': attendance_count,
            'attendance_confirmed': attendance_count_confirmed,
        }
    )


@is_organizer
def reimb_stats_api(request):
    from reimbursement.models import Reimbursement, RE_STATUS, RE_DRAFT
    RE_STATUS_DICT = dict(RE_STATUS)
    # Status analysis
    status_count = Reimbursement.objects.all().values('status') \
        .annotate(reimbursements=Count('status'))
    status_count = map(lambda x: dict(status_name=RE_STATUS_DICT[x['status']], **x), status_count)

    total_apps = HackerApplication.objects.count()
    reimb_count = Reimbursement.objects.count()

    amounts = Reimbursement.objects.all().exclude(status=RE_DRAFT).values('status') \
        .annotate(final_amount=Sum('reimbursement_money'), max_amount=Sum('assigned_money'))
    amounts = map(lambda x: dict(status_name=RE_STATUS_DICT[x['status']], **x), amounts)

    return JsonResponse(
        {
            'update_time': timezone.now(),
            'reimb_count': reimb_count,
            'reimb_apps': {'Reimbursement needed': reimb_count, 'No reimbursement': total_apps - reimb_count},
            'status': list(status_count),
            'amounts': list(amounts),
        }
    )


@is_organizer
def app_stats_api(request):
    return get_stats(HackerApplication)


@is_organizer
def users_stats_api(request):
    users = list(User.objects.all())
    users_count = defaultdict(int)
    for u in users:
        users_count["Mentors"] += u.is_mentor()
        users_count["Sponsors"] += u.is_sponsor()
        users_count["Volunteers"] += u.is_volunteer()
        users_count["Directors"] += u.is_director
        users_count["Organizers"] += (u.is_organizer and not u.is_director)
        users_count["Hackers"] += u.is_hacker()

    users_count = [{'user_type': x, 'Users': v} for (x, v) in users_count.items()]

    return JsonResponse(
        {
            'update_time': timezone.now(),
            'users': users_count,
            'users_count': len(users),
        }
    )


def attrition_rate(application_type):
    applications = list(application_type.objects.all())
    attended = 0
    confirmed = 0
    for a in applications:
        if a.status == APP_CONFIRMED:
            confirmed += 1
        if a.status == APP_ATTENDED:
            attended += 1
    result = 0
    if confirmed or attended:
        result = attended * 100 / (confirmed + attended)
    return result


@is_organizer
def checkin_stats_api(request):
    timeseries = CheckIn.objects.all().annotate(hour=TruncHour('update_time')) \
        .values('hour').annotate(checkins=Count('hour'))
    checkin_count = len(CheckIn.objects.all())
    hacker_attrition_rate = {'Attrition rate': attrition_rate(HackerApplication)}
    volunteer_attrition_rate = {'Attrition rate': attrition_rate(VolunteerApplication)}
    mentor_attrition_rate = {'Attrition rate': attrition_rate(MentorApplication)}
    sponsor_attrition_rate = {'Attrition rate': attrition_rate(SponsorApplication)}
    return JsonResponse(
        {
            'update_time': timezone.now(),
            'timeseries': list(timeseries),
            'checkin_count': checkin_count,
            'hacker_attrition_rate': hacker_attrition_rate,
            'volunteer_attrition_rate': volunteer_attrition_rate,
            'mentor_attrition_rate': mentor_attrition_rate,
            'sponsor_attrition_rate': sponsor_attrition_rate,
        }
    )


@is_organizer
def volunteer_stats_api(request):
    return get_stats(VolunteerApplication)


@is_organizer
def mentor_stats_api(request):
    return get_stats(MentorApplication)


@is_organizer
def sponsor_stats_api(request):
    return get_stats(SponsorApplication)


class AppStats(IsOrganizerMixin, TabsView):
    template_name = 'application_stats.html'

    def get_current_tabs(self):
        return stats_tabs()


class ReimbStats(IsOrganizerMixin, TabsView):
    template_name = 'reimbursement_stats.html'

    def get_current_tabs(self):
        return stats_tabs()


class UsersStats(IsOrganizerMixin, TabsView):
    template_name = 'users_stats.html'

    def get_current_tabs(self):
        return stats_tabs()


class CheckinStats(IsOrganizerMixin, SingleTableMixin, TabsView):
    template_name = 'checkin_stats.html'
    table_class = CheckinRankingListTable

    def get_current_tabs(self):
        return stats_tabs()

    def get_queryset(self):
        return User.objects.annotate(
            checkin_count=Count('checkin')).exclude(checkin_count=0)


class VolunteerStats(IsOrganizerMixin, TabsView):
    template_name = 'volunteer_stats.html'

    def get_current_tabs(self):
        return stats_tabs()


class MentorStats(IsOrganizerMixin, TabsView):
    template_name = 'mentor_stats.html'

    def get_current_tabs(self):
        return stats_tabs()


class SponsorStats(IsOrganizerMixin, TabsView):
    template_name = 'sponsor_stats.html'

    def get_current_tabs(self):
        return stats_tabs()


class OrganizerStats(IsOrganizerMixin, SingleTableMixin, TabsView):
    template_name = 'ranking.html'
    table_class = OrganizerRankingListTable
    table_pagination = False

    def get_current_tabs(self):
        return stats_tabs()

    def get_queryset(self):
        return Vote.objects.exclude(application__status__in=[APP_DUBIOUS, APP_INVALID, APP_BLACKLISTED]) \
            .annotate(email=F('user__email')) \
            .values('email').annotate(total_count=Count('application'),
                                      skip_count=Count('application') - Count('calculated_vote'),
                                      vote_count=Count('calculated_vote')) \
            .exclude(vote_count=0)