from django.db import models

# Create your models here.

from django.db import models
from django.db.models import Model, Max, Min
import random
from django.db.models import F

class Student(models.Model):
    name = models.CharField(max_length=32)
    sid = models.CharField(max_length=20)
    credit = models.FloatField()
    class_name = models.CharField(max_length=32)
    probability = models.FloatField()
    max = models.FloatField()
    min = models.FloatField()


    def prob(self,class_name,credit):  #由积分求概率
        stu = Student.objects.filter(class_name=class_name)
        credits = [student.credit for student in stu]
        max_credit = max(credits)
        min_credit = max(credits)
        if max_credit == min_credit:
            return 1
        else:
            return (credit - min_credit)/(max_credit - min_credit)

    def callstu(self,class_name,num,mode):  #点名的函数，输入要点的人数，返回这些人的学号姓名，不重复,（基础模式）
        sts = Student.objects.filter(class_name=class_name)
        probabilities = []
        if mode == 1 or mode == 2: #基础模式和得分加倍的筛选概率不变
            probabilities = [student.probability for student in sts]
        elif mode == 3 : #三五成群
            for s1 in sts:
                if (int(s1.credit) % 3 == 0 or int(s1.credit) % 5 == 0) and s1.credit != 0:
                    probabilities.append(2*s1.probability)
                else:
                    probabilities.append(s1.probability)
        else:  #六六大顺
            for s1 in sts:
                if int(s1.credit) % 6 == 0 and s1.credit != 0:
                    probabilities.append(2*s1.probability)
                else:
                    probabilities.append(s1.probability)
        if probabilities:
            max_probability = max(probabilities)
            min_probability = min(probabilities)
        else:
            max_probability = -1
            min_probability = -1
        if num > sts.count():
            data = {'message':0}
            return data
        else:
            result =  {}
            if mode == 1 or mode == 2:
                time = 0  #筛选出一个学生需要生成的随机数的次数 （刚开始比较稀疏，要避免一直不能完成抽取条件的情况）设置上限为10
                while len(result) < num: #基础模式和得分加倍模式
                    if time == 10:  #当为了筛选一个人而生成了10次随机数后，由于太浪费时间，直接筛选剩下的人
                        flag = 0
                        while flag == 0:
                            s = sts.order_by('?').first()
                            if s.sid not in result:
                                result[s.name] = s.sid
                                time = 0
                                flag += 1
                    else:
                        random_probability = random.uniform(min_probability, max_probability)
                        time += 1
                        s = sts.filter(probability__gte = random_probability).order_by('?').first()
                        if s.sid not in result:
                            result[s.name] = s.sid
                            time = 0 #当筛选出一个的时候，为下一次的筛选置为0
            elif mode == 3: #三五成群
                time = 0  # 筛选出一个学生需要生成的随机数的次数 （刚开始比较稀疏，要避免一直不能完成抽取条件的情况）设置上限为10
                while len(result) < num:  #一个随机数就选出一个学生
                    if time == 10:
                        flag = 0
                        while flag == 0:
                            s = sts.order_by('?').first()
                            if s.sid not in result:
                                result[s.name] = s.sid
                                time = 0
                                flag += 1
                    else:
                        random_probability = random.uniform(min_probability, max_probability)
                    # print(random_probability)
                        ss1 = [student for student in sts if ( int(student.credit) % 5 == 0
                               or int(student.credit) % 3 == 0 )and student.probability > random_probability/2]
                        ss2 =[student for student in sts if student.credit > random_probability]
                        ss = list(set(ss1+ss2))
                        random_student = random.choice(ss)
                        if random_student.id not in result:
                            result[random_student.name] = random_student.sid
                            time = 0
                print(result)
            elif mode == 4: #六六大顺
                time = 0  # 筛选出一个学生需要生成的随机数的次数 （刚开始比较稀疏，要避免一直不能完成抽取条件的情况）设置上限为10
                while len(result) < num:  #一个随机数就选出一个学生
                    if time == 10:
                        flag = 0
                        while flag == 0:
                            s = sts.order_by('?').first()
                            if s.sid not in result:
                                result[s.name] = s.sid
                                time = 0
                                flag += 1
                    else:
                        random_probability = random.uniform(min_probability, max_probability)
                        ss1 = [student for student in sts if int(student.credit) % 6 == 0 and student.probability > random_probability/2]
                        ss2 = [student for student in sts if student.credit > random_probability]
                        ss = list(set(ss1+ss2))
                        random_student = random.choice(ss)
                        if random_student.id not in result:
                            result[random_student.name] = random_student.sid
                            time = 0
            data = {'message':1,
                    'result':result}
            return data

    def updateinfor (self,point,class_name,sid,style): #更新积分和概率
        if style == 1 :
            point = point
        if style == 2 :
            point = point * 2
        #先改变该学生的积分值
        current_s = Student.objects.filter(class_name=class_name, sid=sid).first()
        current_credit = current_s.credit
        current_s.credit = current_credit + point
        current_s.save()
        #查找最新的最大或最小值，与原来写在表中的上次更新的最大值和最小值做对比
        max_credit = Student.objects.filter(class_name=class_name).aggregate(max_credit=Max('credit'))['max_credit']
        min_credit = Student.objects.filter(class_name=class_name).aggregate(min_credit=Min('credit'))['min_credit']
        s = Student.objects.filter(class_name=class_name).first()  #找未更新的值
        max_credit0 = s.max
        min_credit0 = s.min
        fm = max_credit - min_credit  #最新的分母
        #如果不影响2个最值，仅改变单个学生的概率
        if max_credit == max_credit0 and min_credit == min_credit0:
            if fm == 0:
                prob = 1.0
            else:
                prob = 1 - (current_s.credit - min_credit)/fm
            current_s.probability = prob  # 直接设置字段的值
            current_s.save()  # 调用 save 方法保存更改

        #影响积分最大值和最小值其中的一个时，所有学生的概率都需要改变，先将最新的最大值和最小值写入数据库
        else:
            #将最新的最大值和最小值写入数据库
            sts = Student.objects.filter(class_name=class_name)
            sts.update(min = min_credit, max = max_credit)
            #更新所有学生的概率
            if fm == 0 :
                sts.update(probability = 1.0)
            else:
                sts.update(probability = (1 - (F('credit') - min_credit)/fm))

#耗时最大，性能改进前的代码
#    def updateinfor (self,point,class_name,sid,style): #更新积分和概率
#       if style == 1 :
#            point = point
#        if style == 2 :
#            point = point * 2
#        #先改变该学生的积分值
#        current_s = Student.objects.filter(class_name=class_name, sid=sid).first()
#        current_credit = current_s.credit
#        current_s.credit = current_credit + point
#        current_s.save()
#        #查找最新的最大或最小值，与原来写在表中的上次更新的最大值和最小值做对比
#        max_credit = Student.objects.filter(class_name=class_name).aggregate(max_credit=Max('credit'))['max_credit']
#        min_credit = Student.objects.filter(class_name=class_name).aggregate(min_credit=Min('credit'))['min_credit']
#        fm = max_credit - min_credit  #最新的分母
#        sts = Student.objects.filter(class_name=class_name)
#        sts.update(min=min_credit, max=max_credit)
#        # 更新所有学生的概率
#        if fm == 0:
#            sts.update(probability=1.0)
#        else:
#            sts.update(probability=(1 - (F('credit') - min_credit) / fm))