class Stat(models.Model): class Meta: verbose_name_plural = verbose_name = "统计" paper = models.OneToOneField(Paper, verbose_name=Paper._meta.verbose_name, related_name="stat", on_delete=models.PROTECT) detail = modelutils.JSONField("详情", help_text="") def __str__(self): return "统计<%s>" % self.paper def add_answer(self, answer): d = self.detail or {} from . import helper questions = d.setdefault('questions', {}) score_level = str(answer.performance.get('stdScore', 0) / 5 * 5) helper.distrib_count(d.setdefault('scores', {}), score_level) seconds_level = str((answer.seconds / 30 + 1) * 30) helper.distrib_count(d.setdefault('seconds', {}), seconds_level, reverse=True) ad = answer.detail for a in ad: num = str(a.get('number')) questions[num] = questions.setdefault( num, 0) + (a.get('right') is False and 1 or 0) d['answer_user_count'] = self.paper.answers.values( 'user').distinct().count() self.detail = d
class Paper(models.Model): class Meta: verbose_name_plural = verbose_name = "试卷" ordering = ( '-is_active', 'order_number', '-create_time', ) user = models.ForeignKey(User, verbose_name=User._meta.verbose_name, related_name="exam_papers", on_delete=models.PROTECT) title = models.CharField("标题", max_length=255, blank=False) content = models.TextField( "内容", blank=True, null=True, help_text= "编辑指南:\n首行为标题.\n题型用中文数字加点号开头.\n题目用阿拉伯数字加点号开头.\n答案选项用英文字母加点号开头.\n正确答案用'答案:'开头" ) content_object = modelutils.JSONField("内容对象", blank=True, null=True, help_text="") create_time = models.DateTimeField("创建时间", auto_now_add=True) is_active = models.BooleanField("有效", blank=False, default=True) questions_count = models.PositiveSmallIntegerField("题数", blank=True, default=0) is_break_through = models.BooleanField("闯关", blank=True, default=True) tags = models.CharField('标签', max_length=256, blank=True, default='') order_number = models.PositiveIntegerField('序号', default=0, blank=True, help_text='按数字从小到大排序') owner_type = models.ForeignKey('contenttypes.ContentType', verbose_name='归类', null=True, blank=True, on_delete=models.PROTECT) owner_id = models.PositiveIntegerField(verbose_name='属主编号', null=True, blank=True, db_index=True) owner = GenericForeignKey('owner_type', 'owner_id') def __str__(self): return self.title def save(self, **kwargs): if not self.title: self.title = "试卷" if self.is_break_through is None: self.is_break_through = True data = self.content_object if data: self.questions_count = data.get("questionCount", 0) return super(Paper, self).save(**kwargs)
class Video(models.Model): class Meta: verbose_name_plural = verbose_name = "视频" ordering = ('owner_type', 'owner_id', 'name') user = models.ForeignKey(User, verbose_name=User._meta.verbose_name, related_name="media_videos", on_delete=models.PROTECT) owner_type = models.ForeignKey('contenttypes.ContentType', verbose_name='归类', null=True, blank=True, on_delete=models.PROTECT) owner_id = models.PositiveIntegerField(verbose_name='属主编号', null=True, blank=True, db_index=True) owner = GenericForeignKey('owner_type', 'owner_id') name = models.CharField("名称", max_length=255) description = models.CharField("描述", max_length=255, null=True, blank=True, default='') url = models.URLField("网址") cover_url = models.URLField('封面', blank=True, null=True) duration = models.PositiveSmallIntegerField('时长', blank=True, default=0, help_text='单位: 秒.', editable=False) size = models.PositiveIntegerField('大小', blank=True, default=0, help_text='单位: 比特Byte', editable=False) context = modelutils.JSONField("详情", blank=True, default={}) outline = models.TextField("大纲", blank=True, default='', help_text='可在每一行最后加上时间点, 格式样例: [03:12]') status = models.PositiveSmallIntegerField("状态", choices=choices.STATUS, blank=True, default=choices.STATUS_PROCESS) is_active = models.BooleanField("有效", blank=False, default=False) lecturer = models.ForeignKey(Lecturer, verbose_name=Lecturer._meta.verbose_name, blank=True, null=True, related_name="videos", on_delete=models.PROTECT) create_time = models.DateTimeField("创建时间", auto_now_add=True, db_index=True) def __str__(self): return self.name
class Fault(models.Model): class Meta: verbose_name_plural = verbose_name = "错题" unique_together = ('user', 'paper', 'question_id') ordering = ('-create_time', ) user = models.ForeignKey(User, verbose_name=User._meta.verbose_name, related_name="exam_faults", on_delete=models.PROTECT) paper = models.ForeignKey(Paper, verbose_name=Paper._meta.verbose_name, related_name="faults", on_delete=models.PROTECT) question_id = models.CharField("题号", max_length=16) question_type = models.PositiveSmallIntegerField( "类别", choices=choices.CHOICES_QUESTION_TYPE, default=choices.QUESTION_TYPE_TEXTAREA, db_index=True) question = modelutils.JSONField("题目") times = models.PositiveSmallIntegerField("次数", default=1) detail = modelutils.JSONField("详情") correct_straight_times = models.PositiveSmallIntegerField("连续答对次数", default=0) corrected = models.BooleanField("已订正", default=False) is_active = models.BooleanField("有效", blank=False, default=True) create_time = models.DateTimeField("创建时间", auto_now_add=True, db_index=True) update_time = models.DateTimeField("更新时间", auto_now=True) def __str__(self): return "%s@%s by %s" % (self.question_id, self.paper, self.user) def save(self, **kwargs): from . import helper self.correct_straight_times = helper.cal_correct_straight_times( self.detail.get('result_list', [])) return super(Fault, self).save(**kwargs)
class App(models.Model): class Meta: verbose_name_plural = verbose_name = "应用" unique_together = ('tenant', 'name') tenant = models.ForeignKey(Tenant, verbose_name=Tenant._meta.verbose_name, related_name="apps") name = models.CharField("名字", max_length=64, db_index=True, choices=choices.CHOICES_APPS) status = models.PositiveSmallIntegerField("状态", choices=choices.CHOICES_APP_STATUS, default=choices.APP_STATUS_INSTALL) settings = modelutils.JSONField('配置', default={}) create_time = models.DateTimeField("创建时间", auto_now_add=True) modify_time = models.DateTimeField("修改时间", auto_now=True) def __unicode__(self): return "%s 应用 %s" % (self.tenant, self.name)
class DailyLog(models.Model): class Meta: verbose_name_plural = verbose_name = "日志" unique_together = ('the_date', 'user') the_date = models.DateField('日期', db_index=True) user = models.ForeignKey(User, verbose_name=User._meta.verbose_name, related_name="dailylog_dailylogs", on_delete=models.PROTECT) context = modelutils.JSONField("详情", blank=True, default={}) create_time = models.DateTimeField("创建时间", auto_now_add=True, db_index=True) update_time = models.DateTimeField("创建时间", auto_now=True) def __unicode__(self): return '%s dailylog @ %s' % (self.user, self.the_date.isoformat())
class User(models.Model): class Meta: verbose_name_plural = verbose_name = '用户' ordering = ('-subscribe_time',) user = models.OneToOneField(SiteUser, verbose_name="网站用户", null=True, on_delete=models.PROTECT, related_name="as_wechat_user") openid = models.CharField("openId", max_length=64, primary_key=True) unionid = models.CharField("unionId", max_length=64, null=True, blank=True) nickname = models.CharField("昵称", max_length=64, null=True, blank=True) headimgurl = models.CharField("头像", max_length=255, null=True, blank=True) city = models.CharField("城市", max_length=128, null=True, blank=True) province = models.CharField("省份", max_length=128, null=True, blank=True) country = models.CharField("国家", max_length=128, blank=True, default="中国") sex = models.CharField("性别", max_length=2, null=True, blank=True, choices=choices.CHOICES_GENDER) longitude = models.FloatField("经度", null=True, blank=True) latitude = models.FloatField("纬度", null=True, blank=True) subscribe = models.BooleanField("订阅", blank=True, default=False) subscribe_time = models.DateTimeField("订阅时间", blank=True, null=True, db_index=True) subscribe_scene = models.CharField("关注渠道", max_length=32, blank=True, default="ADD_SCENE_QR_CODE") qr_scene = models.PositiveIntegerField("扫码场景", blank=True, null=True) qr_scene_str = models.CharField("扫码场景描述", max_length=32, blank=True, default="") remark = models.CharField("备注", max_length=255, blank=True, default="") tagid_list = modelutils.JSONField("标签ID列表", blank=True, default=[]) groupid = models.PositiveIntegerField("分组ID", blank=True, default=0) language = models.CharField("语言", max_length=16, blank=True, default="zh_CN") create_time = models.DateTimeField("创建时间", editable=False, auto_now_add=True, db_index=True) def __str__(self): return self.nickname or self.openid def save(self, **kwargs): from xyz_util.datautils import filter_emoji if self.nickname: self.nickname = filter_emoji(self.nickname) if self.user is None: from django.utils.crypto import get_random_string user_name = "%s@wechat" % self.openid[-10:] self.user, created = SiteUser.objects.get_or_create(username=user_name, defaults=dict(email="", first_name=self.nickname)) return super(User, self).save(**kwargs)
class Authority(models.Model): class Meta: verbose_name_plural = verbose_name = "权力" user = models.OneToOneField('auth.user', related_name='xauth_authority', on_delete=models.PROTECT) user_name = models.CharField("姓名", max_length=64, unique=True) roles = modelutils.JSONField("角色", blank=True, default={}) create_time = models.DateTimeField("创建时间", auto_now_add=True) modify_time = models.DateTimeField("修改时间", auto_now=True) def __str__(self): return self.user_name @property def active_roles(self): rs = {} for k, v in self.roles.items(): if v.get('is_active'): rs[k] = v return rs def save(self, **kwargs): from .helper import get_roles from django.contrib.contenttypes.models import ContentType user = self.user roles = {} for rf in get_roles(): r = getattr(user, rf.name, None) if not r: continue roles[rf.name] = dict( id=r.pk, name=rf.name, content_type_id=ContentType.objects.get_for_model(r), is_active=getattr(r, 'is_active', True)) self.roles = roles self.user_name = user.get_full_name() super(Authority, self).save(**kwargs)
class Performance(models.Model): class Meta: verbose_name_plural = verbose_name = "表现" ordering = ('-update_time', ) unique_together = ('owner_type', 'owner_id', 'user') owner_type = models.ForeignKey('contenttypes.ContentType', verbose_name='归类', null=True, blank=True, on_delete=models.PROTECT) owner_id = models.PositiveIntegerField(verbose_name='属主编号', null=True, blank=True, db_index=True) owner = GenericForeignKey('owner_type', 'owner_id') owner_name = models.CharField('属主名称', max_length=256, blank=True, default='') owner_group = models.CharField('属主分组', max_length=256, blank=True, default='') user = models.ForeignKey(User, verbose_name=User._meta.verbose_name, related_name="dailylog_performances", on_delete=models.PROTECT) user_name = models.CharField('用户姓名', max_length=256, blank=True, default='') user_group = models.CharField('用户分组', max_length=256, blank=True, default='') detail = modelutils.JSONField('详情', default={}, blank=True) target = models.PositiveIntegerField("目标", default=0, blank=True) accomplish = models.PositiveIntegerField("完成", default=0, blank=True) accumulate = models.PositiveIntegerField("累计完成", default=0, blank=True) percent = models.PositiveSmallIntegerField('完成百分比', default=0) times = models.PositiveSmallIntegerField('次数', default=0) score = models.PositiveSmallIntegerField("得分", default=0, blank=True, null=True) create_time = models.DateTimeField("创建时间", auto_now_add=True) update_time = models.DateTimeField("更新时间", auto_now=True, db_index=True) def __unicode__(self): return "%s by %s" % (self.owner_name, self.user_name) def save(self, **kwargs): self.user_name = self.user.get_full_name() if hasattr(self.user, 'as_school_student'): student = self.user.as_school_student self.user_group = unicode(student.classes.first()) self.detail['user_number'] = student.number self.owner_name = unicode(self.owner) self.owner_group = unicode(self.owner.owner) if hasattr( self.owner, 'owner') else '' d = self.detail self.target = d.get('target', 0) ps = d.get('parts', []) self.accomplish = len(set(ps)) self.accumulate = len(ps) self.percent = int(self.accomplish * 100 / self.target) if self.target else 0 self.score = d.get('score') self.times = d.get('times', 0) return super(Performance, self).save(**kwargs)
class Performance(models.Model): class Meta: verbose_name_plural = verbose_name = "用户成绩" unique_together = ('user', 'paper') ordering = ('-create_time', ) user = models.ForeignKey(User, verbose_name=User._meta.verbose_name, related_name="exam_performances", on_delete=models.PROTECT) paper = models.ForeignKey(Paper, verbose_name=Paper._meta.verbose_name, related_name="performances", on_delete=models.PROTECT) user_name = models.CharField("用户名", max_length=255, null=False, blank=True, default='') paper_name = models.CharField("试卷名称", max_length=255, null=False, blank=True, default='') score = models.PositiveSmallIntegerField("得分", default=0, blank=True, null=True) detail = modelutils.JSONField("详情", blank=True, null=True, help_text="") create_time = models.DateTimeField("创建时间", auto_now_add=True) update_time = models.DateTimeField("更新时间", auto_now=True) def __str__(self): return "%s by %s" % (self.paper_name, self.user_name) @cached_property def is_passed(self): return self.detail.get( 'maxScore' ) >= EXAM_MIN_PASS_SCORE if self.paper.is_break_through else True def cal_performance(self): answers = self.paper.answers.filter(user=self.user) scs = [a.performance.get('stdScore', 0) for a in answers] lastAnswer = answers.first() times = len(scs) return dict(maxScore=max(scs), minScore=min(scs), avgScore=sum(scs) / times, lastScore=scs[0], times=times, scores=scs, lastTime=lastAnswer and lastAnswer.create_time.isoformat()) def get_user_info(self): u = self.user if hasattr(u, 'as_school_student'): s = u.as_school_student return {'number': s.number, 'group': text_type(s.classes.first())} return {} def save(self, **kwargs): p = self.cal_performance() p['userInfo'] = self.get_user_info() p['paperInfo'] = {'group': text_type(self.paper.owner)} self.paper_name = self.paper.title self.user_name = self.user.get_full_name() self.detail = p self.score = p.get('maxScore', 0) return super(Performance, self).save(**kwargs)
class Answer(models.Model): class Meta: verbose_name_plural = verbose_name = "用户答卷" ordering = ('-create_time', ) user = models.ForeignKey(User, verbose_name=User._meta.verbose_name, related_name="exam_answers", on_delete=models.PROTECT) paper = models.ForeignKey(Paper, verbose_name=Paper._meta.verbose_name, related_name="answers", on_delete=models.PROTECT) detail = modelutils.JSONField("详情", help_text="") grade_detail = modelutils.JSONField("批卷", blank=True, help_text="") pictures = modelutils.JSONField("图片", blank=True, default={}) seconds = models.PositiveSmallIntegerField("用时", default=0, blank=True, null=True, help_text="单位(秒)") std_score = models.PositiveSmallIntegerField("分数", default=0, blank=True, null=True) performance = modelutils.JSONField("成绩表现", blank=True, null=True, help_text="") create_time = models.DateTimeField("创建时间", auto_now_add=True, db_index=True) def __str__(self): return "%s by %s" % (self.paper, self.user.get_full_name()) def save(self, **kwargs): if not self.grade_detail: self.grade_detail = {} self.performance = self.cal_performance() self.std_score = self.performance.get('stdScore') return super(Answer, self).save(**kwargs) def get_question_subjective_map(self): p = self.paper.content_object m = {} for g in p['groups']: is_subjective = False for q in g['questions']: if q.get('type') in ['textarea', 'sentence']: is_subjective = True break for q in g['questions']: m[q['number']] = is_subjective return m def cal_performance(self): wc = 0 rc = 0 score = 0 score_subjective = 0 score_objective = 0 fsc = 0 sm = self.get_question_subjective_map() for a in self.detail: if a['right'] is True: rc += 1 else: wc += 1 is_subjective = sm.get(a['number']) sc = a['userScore'] if is_subjective: score_subjective += sc else: score_objective += sc score += sc fsc += a['score'] stdScore = score * 100 / fsc if fsc > 0 else 0 ms = self.cal_merge_score() format = lambda a: int(round(a)) # float('%.1f' % a) return dict(wrongCount=wc, rightCount=rc, fullScore=fsc, score=format(score), stdScore=format(stdScore), scoreSubjective=format(score_subjective), scoreObjective=format(score_objective), isPassed=stdScore >= EXAM_MIN_PASS_SCORE, scoreMerge=format(ms), stdScoreMerge=format(ms * 100 / fsc if fsc > 0 else 0)) def cal_group_score(self): p = self.paper.content_object m = {} d = self.detail if not d: return m ld = len(d) for g in p['groups']: gs = 0 for q in g['questions']: n = q['number'] if n > ld: continue gs += d[n - 1]['userScore'] m[g['id']] = gs return m def merge_group_score(self): m = self.cal_group_score() gm = deepcopy(self.grade_detail) for k, v in m.items(): if k in gm: continue gm[k] = dict(score=m[k]) return gm def cal_merge_score(self): gm = self.merge_group_score() return sum([v['score'] for k, v in gm.items()])