first commit

This commit is contained in:
renjianbo
2026-01-09 18:28:10 +08:00
parent 8ca573e948
commit 5cc088a210
134 changed files with 8730 additions and 0 deletions

0
competition/__init__.py Normal file
View File

86
competition/admin.py Normal file
View File

@@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
from django.contrib import admin
from competition.models import (BankInfo, ChoiceInfo, CompetitionKindInfo,
CompetitionQAInfo, FillInBlankInfo)
class CompetitionKindInfoAdmin(admin.ModelAdmin):
"""
比赛信息后台
"""
list_display = ('kind_id', 'account_id', 'app_id', 'bank_id', 'kind_name', 'total_score', 'question_num', 'total_partin_num', 'status', 'created_at', 'updated_at')
list_filter = ('account_id', 'status')
search_fields = ('kind_name', 'kind_id', 'app_id', 'account_id',)
readonly_fields = ('kind_id', 'total_partin_num',)
def save_model(self, request, obj, form, change):
obj.save()
def delete_model(self, request, obj):
obj.delete()
def get_readonly_fields(self, request, obj=None):
return self.readonly_fields
class BankInfoAdmin(admin.ModelAdmin):
"""
题库后台配置
"""
list_display = ('bank_id', 'bank_type', 'kind_num', 'choice_num', 'fillinblank_num', 'partin_num')
list_filter = ('bank_type', 'bank_id',)
search_fields = ('bank_id',)
readonly_fields = ('bank_id', 'choice_num', 'fillinblank_num', 'kind_num', 'partin_num')
def save_model(self, request, obj, form, change):
obj.choice_num = ChoiceInfo.objects.filter(bank_id=obj.bank_id).count()
obj.fillinblank_num = FillInBlankInfo.objects.filter(bank_id=obj.bank_id).count()
obj.save()
class ChoiceInfoAdmin(admin.ModelAdmin):
"""
选择题配置后台
"""
list_display = ('bank_id', 'question', 'answer', 'item1', 'item2', 'item3', 'item4', 'source', 'status', 'created_at', 'updated_at')
list_filter = ('bank_id', 'status')
search_fields = ('bank_id', 'question', 'answer', 'item1', 'item2', 'item3', 'item4')
def save_model(self, request, obj, form, change):
obj.save()
def delete_model(self, request, obj):
obj.delete()
class FillInBlankInfoAdmin(admin.ModelAdmin):
"""
填空题配置后台
"""
list_display = ('bank_id', 'question', 'answer', 'source', 'status', 'created_at', 'updated_at')
list_filter = ('bank_id', 'status')
search_fields = ('bank_id', 'question', 'answer')
class CompetitionQAInfoAdmin(admin.ModelAdmin):
"""
答题记录信息后台
"""
list_display = ('kind_id', 'status', 'uid', 'qa_id', 'score', 'created_at', 'updated_at')
list_filter = ('kind_id', 'uid', 'qa_id', 'started', 'finished', 'status')
search_fields = ('uid', 'kind_id', )
readonly_fields = ('qa_id',)
admin.site.register(CompetitionKindInfo, CompetitionKindInfoAdmin)
admin.site.register(CompetitionQAInfo, CompetitionQAInfoAdmin)
admin.site.register(ChoiceInfo, ChoiceInfoAdmin)
admin.site.register(FillInBlankInfo, FillInBlankInfoAdmin)
admin.site.register(BankInfo, BankInfoAdmin)

5
competition/apps.py Normal file
View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class CompetitionConfig(AppConfig):
name = 'competition'

287
competition/cop_render.py Normal file
View File

@@ -0,0 +1,287 @@
# -*- coding: utf-8 -*-
import collections
import json
from django.shortcuts import render
from account.models import Profile
from competition.models import BankInfo, CompetitionKindInfo, CompetitionQAInfo
from utils.decorators import check_copstatus, check_login
from utils.errors import (BankInfoNotFound, CompetitionNotFound,
ProfileNotFound, QuestionLogNotFound,
QuestionNotSufficient)
from utils.redis.rpageconfig import get_pageconfig, get_form_regex
from utils.redis.rprofile import get_enter_userinfo
from utils.redis.rrank import get_rank, get_rank_data
import datetime
def home(request):
"""
比赛首页首页视图
:param request: 请求对象
:return: 渲染视图: user_info: 用户信息; kind_info: 比赛信息;is_show_userinfo: 是否展示用户信息表单;user_info_has_entered: 是否已经录入表单;
userinfo_fields: 表单字段;option_fields: 表单字段中呈现为下拉框的字段;
"""
uid = request.GET.get('uid', '') # 获取uid
kind_id = request.GET.get('kind_id', '') # 获取kind_id
created = request.GET.get('created', '0') # 获取标志位,以后会用到
try: # 获取比赛数据
kind_info = CompetitionKindInfo.objects.get(kind_id=kind_id)
except CompetitionKindInfo.DoesNotExist: # 不存在渲染错误视图
return render(request, 'err.html', CompetitionNotFound)
try: # 获取题库数据
bank_info = BankInfo.objects.get(bank_id=kind_info.bank_id)
except BankInfo.DoesNotExist: # 不存在渲染错误视图
return render(request, 'err.html', BankInfoNotFound)
try: # 获取用户数据
profile = Profile.objects.get(uid=uid)
except Profile.DoesNotExist: # 不存在渲染错误视图
return render(request, 'err.html', ProfileNotFound)
if kind_info.question_num > bank_info.total_question_num: # 比赛出题数量是否小于题库总大小
return render(request, 'err.html', QuestionNotSufficient)
show_info = get_pageconfig(kind_info.app_id).get('show_info', {}) # 从redis获取页面配置信息
is_show_userinfo = show_info.get('is_show_userinfo', False) # 页面配置信息,用来控制答题前是否展示一张表单
form_fields = collections.OrderedDict() # 生成一个有序的用来保存表单字段的字典
form_regexes = [] # 生成一个空的正则表达式列表
if is_show_userinfo:
userinfo_fields = show_info.get('userinfo_fields', '').split('#') # 从页面配置中获取userinfo_fields
for i in userinfo_fields: # 将页面配置的每个正则表达式取出来放入正则表达式列表
form_regexes.append(get_form_regex(i))
userinfo_field_names = show_info.get('userinfo_field_names', '').split('#')
for i in range(len(userinfo_fields)): # 将每个表单字段信息保存到有序的表单字段字典中
form_fields.update({userinfo_fields[i]: userinfo_field_names[i]})
return render(request, 'competition/index.html', { # 渲染页面
'user_info': profile.data,
'kind_info': kind_info.data,
'bank_info': bank_info.data,
'is_show_userinfo': 'true' if is_show_userinfo else 'false',
'userinfo_has_enterd': 'true' if get_enter_userinfo(kind_id, uid) else 'false',
'userinfo_fields': json.dumps(form_fields) if form_fields else '{}',
'option_fields': json.dumps(show_info.get('option_fields', '')),
'field_regexes': form_regexes,
'created': created
})
def games(request, s):
"""
获取所有比赛接口
:param request: 请求对象
:param s: 请求关键字
:return: 返回该请求关键字对应的所有比赛类别
"""
if s == 'hot':
# 筛选条件: 完成时间大于当前时间;根据参与人数降序排序;根据创建时间降序排序;筛选10个
kinds = CompetitionKindInfo.objects.filter(
cop_finishat__gt=datetime.datetime.now(tz=datetime.timezone.utc),
).order_by('-total_partin_num').order_by('-created_at')[:10]
elif s == 'tech':
kinds = CompetitionKindInfo.objects.filter(
kind_type=CompetitionKindInfo.IT_ISSUE,
cop_finishat__gt=datetime.datetime.now(tz=datetime.timezone.utc)
).order_by('-total_partin_num').order_by('-created_at')
elif s == 'edu':
kinds = CompetitionKindInfo.objects.filter(
kind_type=CompetitionKindInfo.EDUCATION,
cop_finishat__gt=datetime.datetime.now(tz=datetime.timezone.utc)
).order_by('-total_partin_num').order_by('-created_at')
elif s == 'culture':
kinds = CompetitionKindInfo.objects.filter(
kind_type=CompetitionKindInfo.CULTURE,
cop_finishat__gt=datetime.datetime.now(tz=datetime.timezone.utc)
).order_by('-total_partin_num').order_by('-created_at')
elif s == 'sport':
kinds = CompetitionKindInfo.objects.filter(
kind_type=CompetitionKindInfo.SPORT,
cop_finishat__gt=datetime.datetime.now(tz=datetime.timezone.utc)
).order_by('-total_partin_num').order_by('-created_at')
elif s == 'general':
kinds = CompetitionKindInfo.objects.filter(
kind_type=CompetitionKindInfo.GENERAL,
cop_finishat__gt=datetime.datetime.now(tz=datetime.timezone.utc)
).order_by('-total_partin_num').order_by('-created_at')
elif s == 'interview':
kinds = CompetitionKindInfo.objects.filter(
kind_type=CompetitionKindInfo.INTERVIEW,
cop_finishat__gt=datetime.datetime.now(tz=datetime.timezone.utc)
).order_by('-total_partin_num').order_by('-created_at')
else:
kinds = None
return render(request, 'competition/games.html', {
'kinds': kinds,
})
@check_login
@check_copstatus
def game(request):
"""
返回比赛题目信息的视图
:param request: 请求对象
:return: 渲染视图: user_info: 用户信息;kind_id: 比赛唯一标识;kind_name: 比赛名称;cop_finishat: 比赛结束时间;rule_text: 大赛规则;
"""
uid = request.GET.get('uid', '') # 获取uid
kind_id = request.GET.get('kind_id', '') # 获取kind_id
try: # 获取比赛信息
kind_info = CompetitionKindInfo.objects.get(kind_id=kind_id)
except CompetitionKindInfo.DoesNotExist: # 未获取到渲染错误视图
return render(request, 'err.html', CompetitionNotFound)
try: # 获取题库信息
bank_info = BankInfo.objects.get(bank_id=kind_info.bank_id)
except BankInfo.DoesNotExist: # 未获取到,渲染错误视图
return render(request, 'err.html', BankInfoNotFound)
try: # 获取用户信息
profile = Profile.objects.get(uid=uid)
except Profile.DoesNotExist: # 未获取到,渲染错误视图
return render(request, 'err.html', ProfileNotFound)
if kind_info.question_num > bank_info.total_question_num: # 检查题库大小
return render(request, 'err.html', QuestionNotSufficient)
pageconfig = get_pageconfig(kind_info.app_id) # 获取页面配置信息
return render(request, 'competition/game.html', { # 渲染视图信息
'user_info': profile.data,
'kind_id': kind_info.kind_id,
'kind_name': kind_info.kind_name,
'cop_finishat': kind_info.cop_finishat,
'period_time': kind_info.period_time,
'rule_text': pageconfig.get('text_info', {}).get('rule_text', '')
})
@check_login
def result(request):
"""
比赛结果和排行榜的视图
:param request: 请求对象
:return: 渲染视图: qa_info: 答题记录数据;user_info: 用户信息数据;kind_info: 比赛信息数据;rank: 该用户当前比赛排名
"""
uid = request.GET.get('uid', '')
kind_id = request.GET.get('kind_id', '')
qa_id = request.GET.get('qa_id', '')
try:
profile = Profile.objects.get(uid=uid)
except Profile.DoesNotExist:
return render(request, 'err.html', ProfileNotFound)
try:
kind_info = CompetitionKindInfo.objects.get(kind_id=kind_id)
except CompetitionKindInfo.DoesNotExist:
return render(request, 'err.html', CompetitionNotFound)
try:
qa_info = CompetitionQAInfo.objects.get(qa_id=qa_id, uid=uid)
except CompetitionQAInfo.DoesNotExist:
return render(request, 'err.html', QuestionLogNotFound)
return render(request, 'competition/result.html', {
'qa_info': qa_info.detail,
'user_info': profile.data,
'kind_info': kind_info.data,
'rank': get_rank(kind_id, uid)
})
@check_login
def rank(request):
"""
排行榜数据视图
:param request: 请求对象
:return: 渲染视图: user_info: 用户信息;kind_info: 比赛信息; rank: 所有比赛排名;
"""
uid = request.GET.get('uid', '')
kind_id = request.GET.get('kind_id', '')
try:
profile = Profile.objects.get(uid=uid)
except Profile.DoesNotExist:
return render(request, 'err.html', ProfileNotFound)
try:
kind_info = CompetitionKindInfo.objects.get(kind_id=kind_id)
except CompetitionKindInfo.DoesNotExist:
return render(request, 'err.html', CompetitionNotFound)
ranks, rank_data = get_rank_data(kind_id)
for i in range(len(rank_data)):
rank_data[i].update({'rank': i + 1})
rank_data[i]['time'] = rank_data[i]['time'] / 1000.000
return render(request, 'competition/rank.html', {
'user_info': profile.data,
'kind_info': kind_info.data,
'rank': rank_data
})
@check_login
def search(request):
"""
搜索查询视图
:param request: 请求对象
:return: 渲染视图: user_info: 用户信息;result:查询结果比赛信息集合;key: 查询结果的关键字,是根据比赛名称查询还是根据赞助商关键字查询的结果
"""
uid = request.GET.get('uid', '')
keyword = request.GET.get('keyword', '')
try:
profile = Profile.objects.get(uid=uid)
except Profile.DoesNotExist:
render(request, 'err.html', ProfileNotFound)
keyword = keyword.strip(' ')
kinds = CompetitionKindInfo.objects.filter(kind_name__contains=keyword)
key = 'kind'
if not kinds:
kinds = CompetitionKindInfo.objects.filter(sponsor_name__contains=keyword)
key = 'sponsor'
return render(request, 'competition/search.html', {
'result': kinds,
'key': key or ''
})
@check_login
def contact(request):
"""
联系我们视图
:param request: 请求对象
:return: 渲染视图: user_info: 用户信息
"""
uid = request.GET.get('uid', '')
try:
profile = Profile.objects.get(uid=uid)
except Profile.DoesNotExist:
return render(request, 'err.html', ProfileNotFound)
return render(request, 'web/contact_us.html', {'user_info': profile.data})
def donate(request):
"""
捐助视图
:param request: 请求对象
:return: 渲染视图: user_info: 用户信息
"""
uid = request.GET.get('uid', '')
try:
profile = Profile.objects.get(uid=uid)
except Profile.DoesNotExist:
profile = None
return render(request, 'web/donate.html', {'user_info': profile.data if profile else None})

247
competition/game_views.py Normal file
View File

@@ -0,0 +1,247 @@
# -*- coding: utf-8 -*-
import datetime
import random
from django.db import transaction
from django.views.decorators.csrf import csrf_exempt
from account.models import Profile, UserInfo
from competition.models import (BankInfo, ChoiceInfo, CompetitionKindInfo,
CompetitionQAInfo, FillInBlankInfo)
from TimeConvert import TimeConvert as tc
from utils.check_utils import check_correct_num
from utils.decorators import check_copstatus, check_login
from utils.errors import BankInfoNotFound, CompetitionError, ProfileError
from utils.redis.rprofile import enter_userinfo
from utils.redis.rrank import add_to_rank
from utils.response import json_response
@check_login
@check_copstatus
@transaction.atomic
def get_questions(request):
"""
获取题目信息接口
:param request: 请求对象
:return: 返回json数据: user_info: 用户信息;kind_info: 比赛信息;qa_id: 比赛答题记录;questions: 比赛随机后的题目;
"""
kind_id = request.GET.get('kind_id', '') # 获取kind_id
uid = request.GET.get('uid', '') # 获取uid
try: # 获取比赛信息
kind_info = CompetitionKindInfo.objects.select_for_update().get(kind_id=kind_id)
except CompetitionKindInfo.DoesNotExist: # 未获取到返回错误码100001
return json_response(*CompetitionError.CompetitionNotFound)
try: # 获取题库信息
bank_info = BankInfo.objects.get(bank_id=kind_info.bank_id)
except BankInfo.DoesNotExist: # 未获取到返回错误码100004
return json_response(*CompetitionError.BankInfoNotFound)
try: # 获取用户信息
profile = Profile.objects.get(uid=uid)
except Profile.DoesNotExist: # 未获取到返回错误码200001
return json_response(*ProfileError.ProfileNotFound)
qc = ChoiceInfo.objects.filter(bank_id=kind_info.bank_id) # 选择题
qf = FillInBlankInfo.objects.filter(bank_id=kind_info.bank_id) # 填空题
questions = [] # 将两种题型放到同一个列表中
for i in qc.iterator():
questions.append(i.data)
for i in qf.iterator():
questions.append(i.data)
question_num = kind_info.question_num # 出题数
q_count = bank_info.total_question_num # 总题数
if q_count < question_num: # 出题数大于总题数返回错误码100005
return json_response(CompetitionError.QuestionNotSufficient)
qs = random.sample(questions, question_num) # 随机分配题目
qa_info = CompetitionQAInfo.objects.select_for_update().create( # 创建答题log数据
kind_id=kind_id,
uid=uid,
qsrecord=[q['question'] for q in qs],
asrecord=[q['answer'] for q in qs],
total_num=question_num,
started_stamp=tc.utc_timestamp(ms=True, milli=True), # 设置开始时间戳
started=True
)
for i in qs: # 剔除答案信息
i.pop('answer')
return json_response(200, 'OK', { # 返回JSON数据包括题目信息答题log信息等
'kind_info': kind_info.data,
'user_info': profile.data,
'qa_id': qa_info.qa_id,
'questions': qs
})
@csrf_exempt
@check_login
@check_copstatus
@transaction.atomic
def submit_answer(request):
"""
提交答案接口
:param request: 请求对象
:return: 返回json数据: user_info: 用户信息; qa_id: 比赛答题记录标识; kind_id: 比赛唯一标识
"""
stop_stamp = tc.utc_timestamp(ms=True, milli=True) # 结束时间戳
qa_id = request.POST.get('qa_id', '') # 获取qa_id
uid = request.POST.get('uid', '') # 获取uid
kind_id = request.POST.get('kind_id', '') # 获取kind_id
answer = request.POST.get('answer', '') # 获取answer
try: # 获取比赛信息
kind_info = CompetitionKindInfo.objects.get(kind_id=kind_id)
except CompetitionKindInfo.DoesNotExist: # 未获取到返回错误码100001
return json_response(*CompetitionError.CompetitionNotFound)
try: # 获取题库信息
bank_info = BankInfo.objects.get(bank_id=kind_info.bank_id)
except BankInfo.DoesNotExist: # 未获取到返回错误码100004
return json_response(*CompetitionError.BankInfoNotFound)
try: # 获取用户信息
profile = Profile.objects.get(uid=uid)
except Profile.DoesNotExist: # 未获取到返回错误码200001
return json_response(*ProfileError.ProfileNotFound)
try: # 获取答题log信息
qa_info = CompetitionQAInfo.objects.select_for_update().get(qa_id=qa_id)
except CompetitionQAInfo.DoesNotExist: # 未获取到返回错误码100006
return json_response(*CompetitionError.QuestionNotFound)
answer = answer.rstrip('#').split('#') # 处理答案数据
total, correct, wrong = check_correct_num(answer) # 检查答题情况
qa_info.aslogrecord = answer
qa_info.finished_stamp = stop_stamp
qa_info.expend_time = stop_stamp - qa_info.started_stamp
qa_info.finished = True
qa_info.correct_num = correct if total == qa_info.total_num else 0
qa_info.incorrect_num = wrong if total == qa_info.total_num else qa_info.total_num
qa_info.save() # 保存答题log
if qa_info.correct_num == kind_info.question_num: # 得分处理
score = kind_info.total_score
elif not qa_info.correct_num:
score = 0
else:
score = round((kind_info.total_score / kind_info.question_num) * correct, 3)
qa_info.score = score # 继续保存答题log
qa_info.save()
kind_info.total_partin_num += 1 # 保存比赛数据
kind_info.save() # 比赛答题次数
bank_info.partin_num += 1
bank_info.save() # 题库答题次数
if (kind_info.period_time > 0) and (qa_info.expend_time > kind_info.period_time * 60 * 1000): # 超时,不加入排行榜
qa_info.status = CompetitionQAInfo.OVERTIME
qa_info.save()
else: # 正常完成,加入排行榜
add_to_rank(uid, kind_id, qa_info.score, qa_info.expend_time)
qa_info.status = CompetitionQAInfo.COMPLETED
qa_info.save()
return json_response(200, 'OK', { # 返回JSON数据
'qa_id': qa_id,
'user_info': profile.data,
'kind_id': kind_id,
})
@csrf_exempt
@check_login
@check_copstatus
@transaction.atomic
def userinfo_entry(request):
"""
用户表单提交接口
:param request: 请求对象
:return: 返回json数据: user_info: 用户信息; kind_info: 比赛信息
"""
uid = request.POST.get('uid', '')
kind_id = request.POST.get('kind_id', '')
result = request.POST.get('result', '')
try:
profile = Profile.objects.get(uid=uid)
except Profile.DoesNotExist:
return json_response(*ProfileError.ProfileNotFound)
try:
kind_info = CompetitionKindInfo.objects.get(kind_id=kind_id)
except CompetitionKindInfo.DoesNotExist:
return json_response(*CompetitionError.CompetitionNotFound)
rl = [i.split(',') for i in result.rstrip('#').split('#')]
ui, _ = UserInfo.objects.select_for_update().get_or_create(
uid=uid,
kind_id=kind_id
)
for i in rl:
if hasattr(UserInfo, i[0]):
setattr(ui, i[0], i[1])
ui.save()
enter_userinfo(kind_id, uid)
return json_response(200, 'OK', {
'user_info': profile.data,
'kind_info': kind_info.data
})
@csrf_exempt
def games(request, s):
"""
获取所有比赛接口
:param request: 请求对象
:param s: 请求关键字
:return: 返回该请求关键字对应的所有比赛类别
"""
if s == 'hot':
# 筛选条件: 完成时间大于当前时间;根据参与人数降序排序;根据创建时间降序排序;筛选10个
kinds = CompetitionKindInfo.objects.filter(
cop_finishat__gt=datetime.datetime.now(tz=datetime.timezone.utc),
).order_by('-total_partin_num').order_by('-created_at')[:10]
elif s == 'tech':
kinds = CompetitionKindInfo.objects.filter(
kind_type=CompetitionKindInfo.IT_ISSUE,
cop_finishat__gt=datetime.datetime.now(tz=datetime.timezone.utc)
).order_by('-total_partin_num').order_by('-created_at')
elif s == 'edu':
kinds = CompetitionKindInfo.objects.filter(
kind_type=CompetitionKindInfo.EDUCATION,
cop_finishat__gt=datetime.datetime.now(tz=datetime.timezone.utc)
).order_by('-total_partin_num').order_by('-created_at')
elif s == 'culture':
kinds = CompetitionKindInfo.objects.filter(
kind_type=CompetitionKindInfo.CULTURE,
cop_finishat__gt=datetime.datetime.now(tz=datetime.timezone.utc)
).order_by('-total_partin_num').order_by('-created_at')
elif s == 'sport':
kinds = CompetitionKindInfo.objects.filter(
kind_type=CompetitionKindInfo.SPORT,
cop_finishat__gt=datetime.datetime.now(tz=datetime.timezone.utc)
).order_by('-total_partin_num').order_by('-created_at')
elif s == 'general':
kinds = CompetitionKindInfo.objects.filter(
kind_type=CompetitionKindInfo.GENERAL,
cop_finishat__gt=datetime.datetime.now(tz=datetime.timezone.utc)
).order_by('-total_partin_num').order_by('-created_at')
elif s == 'interview':
kinds = CompetitionKindInfo.objects.filter(
kind_type=CompetitionKindInfo.INTERVIEW,
cop_finishat__gt=datetime.datetime.now(tz=datetime.timezone.utc)
).order_by('-total_partin_num').order_by('-created_at')
else:
kinds = None
uid = request.session.get('uid', '')
return json_response(200, 'OK', {
'kinds': [i.data for i in kinds],
'uid' : uid
})

View File

@@ -0,0 +1,138 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2018-03-28 04:52
from __future__ import unicode_literals
import django.utils.timezone
from django.db import migrations, models
import shortuuidfield.fields
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='BankInfo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.BooleanField(db_index=True, default=True, help_text='状态', verbose_name='状态')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='创建时间', verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, help_text='更新时间', verbose_name='更新时间')),
('bank_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='题库唯一标识', max_length=22, null=True)),
('choice_num', models.IntegerField(default=0, help_text='选择题数', verbose_name='选择题数')),
('fillinblank_num', models.IntegerField(default=0, help_text='填空题数', verbose_name='填空题数')),
('bank_type', models.IntegerField(choices=[(0, '技术类'), (1, '教育类'), (2, '文化类'), (3, '常识类'), (4, '面试题')], default=0, help_text='题库类型', verbose_name='题库类型')),
('kind_num', models.IntegerField(default=0, help_text='比赛使用次数', verbose_name='比赛使用次数')),
('partin_num', models.IntegerField(default=0, help_text='总答题人数', verbose_name='总答题人数')),
],
options={
'verbose_name': '题库',
'verbose_name_plural': '题库',
},
),
migrations.CreateModel(
name='ChoiceInfo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.BooleanField(db_index=True, default=True, help_text='状态', verbose_name='状态')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='创建时间', verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, help_text='更新时间', verbose_name='更新时间')),
('image_url', models.CharField(blank=True, help_text='题目图片', max_length=255, null=True, verbose_name='图片链接')),
('audio_url', models.CharField(blank=True, help_text='题目音频', max_length=255, null=True, verbose_name='音频链接')),
('audio_time', models.IntegerField(default=0, help_text='题目音频时长', verbose_name='音频时长')),
('bank_id', models.CharField(blank=True, db_index=True, help_text='题库唯一标识', max_length=32, null=True, verbose_name='题库id')),
('ctype', models.IntegerField(choices=[(1, '文本'), (2, '图片'), (3, '音频')], default=1, help_text='题目类型', verbose_name='题目类型')),
('question', models.CharField(blank=True, help_text='题目', max_length=255, null=True, verbose_name='问题')),
('answer', models.CharField(blank=True, help_text='答案', max_length=255, null=True, verbose_name='答案')),
('item1', models.CharField(blank=True, help_text='选项一', max_length=255, null=True, verbose_name='选项1')),
('item2', models.CharField(blank=True, help_text='选项二', max_length=255, null=True, verbose_name='选项2')),
('item3', models.CharField(blank=True, help_text='选项三', max_length=255, null=True, verbose_name='选项3')),
('item4', models.CharField(blank=True, help_text='选项四', max_length=255, null=True, verbose_name='选项4')),
('source', models.CharField(blank=True, help_text='出处', max_length=255, null=True, verbose_name='出处')),
],
options={
'verbose_name': '选择题',
'verbose_name_plural': '选择题',
},
),
migrations.CreateModel(
name='CompetitionKindInfo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.BooleanField(db_index=True, default=True, help_text='状态', verbose_name='状态')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='创建时间', verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, help_text='更新时间', verbose_name='更新时间')),
('kind_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='比赛类别唯一标识', max_length=22, null=True)),
('account_id', models.CharField(blank=True, db_index=True, help_text='商家账户唯一标识', max_length=32, null=True, verbose_name='出题账户id')),
('app_id', models.CharField(blank=True, db_index=True, help_text='应用唯一标识', max_length=32, null=True, verbose_name='应用id')),
('bank_id', models.CharField(blank=True, db_index=True, help_text='题库唯一标识', max_length=32, null=True, verbose_name='题库id')),
('kind_type', models.IntegerField(choices=[(0, '技术类'), (1, '教育类'), (2, '文化类'), (3, '常识类'), (6, '地理类'), (7, '体育类'), (4, '面试题')], default=0, help_text='比赛类型', verbose_name='比赛类型')),
('kind_name', models.CharField(blank=True, help_text='竞赛类别名称', max_length=32, null=True, verbose_name='比赛名称')),
('sponsor_name', models.CharField(blank=True, help_text='赞助商名称', max_length=60, null=True, verbose_name='赞助商名称')),
('total_score', models.IntegerField(default=0, help_text='总分数', verbose_name='总分数')),
('question_num', models.IntegerField(default=0, help_text='出题数量', verbose_name='题目个数')),
('cop_startat', models.DateTimeField(default=django.utils.timezone.now, help_text='比赛开始时间', verbose_name='比赛开始时间')),
('period_time', models.IntegerField(default=60, help_text='答题时间(min)', verbose_name='答题时间')),
('cop_finishat', models.DateTimeField(blank=True, help_text='比赛结束时间', null=True, verbose_name='比赛结束时间')),
('total_partin_num', models.IntegerField(default=0, help_text='总参与人数', verbose_name='total_partin_num')),
],
options={
'verbose_name': '比赛类别信息',
'verbose_name_plural': '比赛类别信息',
},
),
migrations.CreateModel(
name='CompetitionQAInfo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.BooleanField(db_index=True, default=True, help_text='状态', verbose_name='状态')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='创建时间', verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, help_text='更新时间', verbose_name='更新时间')),
('kind_id', models.CharField(blank=True, db_index=True, help_text='比赛类别唯一标识', max_length=32, null=True, verbose_name='比赛id')),
('qa_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='QA 唯一标识', max_length=22, null=True)),
('uid', models.CharField(blank=True, db_index=True, help_text='用户唯一标识', max_length=32, null=True, verbose_name='用户id')),
('qsrecord', models.TextField(blank=True, help_text='问题记录', max_length=10000, null=True, verbose_name='问题记录')),
('asrecord', models.TextField(blank=True, help_text='答案记录', max_length=10000, null=True, verbose_name='答案记录')),
('aslogrecord', models.TextField(blank=True, help_text='答案提交记录', max_length=10000, null=True, verbose_name='答案提交记录')),
('started_stamp', models.BigIntegerField(default=0, help_text='开始时间戳(毫秒)', verbose_name='开始时间戳')),
('finished_stamp', models.BigIntegerField(default=0, help_text='结束时间戳(毫秒)', verbose_name='结束时间戳')),
('expend_time', models.IntegerField(default=0, help_text='耗费时间(毫秒)', verbose_name='耗时')),
('started', models.BooleanField(db_index=True, default=False, help_text='是否开始', verbose_name='已开始')),
('finished', models.BooleanField(db_index=True, default=False, help_text='是否结束', verbose_name='已结束')),
('correct_num', models.IntegerField(default=0, help_text='答对数量', verbose_name='正确数')),
('incorrect_num', models.IntegerField(default=0, help_text='答错数量', verbose_name='错误数')),
('total_num', models.IntegerField(default=0, help_text='总共数量', verbose_name='总数')),
('score', models.IntegerField(default=0, help_text='分数', verbose_name='得分')),
],
options={
'verbose_name': '比赛问题记录',
'verbose_name_plural': '比赛问题记录',
},
),
migrations.CreateModel(
name='FillInBlankInfo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.BooleanField(db_index=True, default=True, help_text='状态', verbose_name='状态')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='创建时间', verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, help_text='更新时间', verbose_name='更新时间')),
('image_url', models.CharField(blank=True, help_text='题目图片', max_length=255, null=True, verbose_name='图片链接')),
('audio_url', models.CharField(blank=True, help_text='题目音频', max_length=255, null=True, verbose_name='音频链接')),
('audio_time', models.IntegerField(default=0, help_text='题目音频时长', verbose_name='音频时长')),
('bank_id', models.CharField(blank=True, db_index=True, help_text='题库唯一标识', max_length=32, null=True, verbose_name='题库id')),
('ctype', models.IntegerField(choices=[(1, '文本'), (2, '图片'), (3, '音频')], default=1, help_text='题目类型', verbose_name='题目类型')),
('question', models.CharField(blank=True, help_text='题目', max_length=255, null=True, verbose_name='问题')),
('answer', models.CharField(blank=True, help_text='答案', max_length=255, null=True, verbose_name='答案')),
('source', models.CharField(blank=True, help_text='出处', max_length=255, null=True, verbose_name='出处')),
],
options={
'verbose_name': '填空题',
'verbose_name_plural': '填空题',
},
),
]

View File

@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2018-03-28 14:46
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('competition', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='bankinfo',
name='bank_name',
field=models.CharField(blank=True, help_text='题库名称', max_length=40, null=True, verbose_name='题库名称'),
),
migrations.AddField(
model_name='bankinfo',
name='uid',
field=models.CharField(blank=True, db_index=True, help_text='用户唯一标识', max_length=32, null=True, verbose_name='用户id'),
),
migrations.AlterField(
model_name='bankinfo',
name='bank_type',
field=models.IntegerField(choices=[(0, '技术类'), (1, '教育类'), (2, '文化类'), (3, '常识类'), (6, '地理类'), (7, '体育类'), (4, '面试题')], default=0, help_text='题库类型', verbose_name='题库类型'),
),
]

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2018-04-01 13:59
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('competition', '0002_auto_20180328_2246'),
]
operations = [
migrations.AlterField(
model_name='competitionqainfo',
name='status',
field=models.IntegerField(choices=[(0, '未完成'), (1, '已完成'), (2, '超时')], default=0, help_text='答题状态', verbose_name='答题状态'),
),
]

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2018-04-04 07:28
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('competition', '0003_auto_20180401_2159'),
]
operations = [
migrations.AlterField(
model_name='competitionqainfo',
name='score',
field=models.FloatField(default=0, help_text='分数', verbose_name='得分'),
),
]

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2018-04-05 15:20
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('competition', '0004_auto_20180404_1528'),
]
operations = [
migrations.AddField(
model_name='bankinfo',
name='account_id',
field=models.CharField(blank=True, db_index=True, help_text='商家账户唯一标识', max_length=32, null=True, verbose_name='商家id'),
),
]

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2018-04-16 13:51
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('competition', '0005_bankinfo_account_id'),
]
operations = [
migrations.AddField(
model_name='competitionqainfo',
name='correct_list',
field=models.CharField(blank=True, help_text='正确答案列表', max_length=10000, null=True, verbose_name='正确答案列表'),
),
migrations.AddField(
model_name='competitionqainfo',
name='wrong_list',
field=models.CharField(blank=True, help_text='错误答案列表', max_length=10000, null=True, verbose_name='错误答案列表'),
),
]

View File

336
competition/models.py Normal file
View File

@@ -0,0 +1,336 @@
# -*- coding: utf-8 -*-
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from shortuuidfield import ShortUUIDField
from utils.basemodels import CreateUpdateMixin, MediaMixin
class CompetitionKindInfo(CreateUpdateMixin):
"""比赛类别信息类"""
IT_ISSUE = 0
EDUCATION = 1
CULTURE = 2
GENERAL = 3
INTERVIEW = 4
REAR = 5
GEO = 6
SPORT = 7
KIND_TYPES = (
(IT_ISSUE, u'技术类'),
(EDUCATION, u'教育类'),
(CULTURE, u'文化类'),
(GENERAL, u'常识类'),
(GEO, u'地理类'),
(SPORT, u'体育类'),
(INTERVIEW, u'面试题')
)
kind_id = ShortUUIDField(_(u'比赛id'), max_length=32, blank=True, null=True, help_text=u'比赛类别唯一标识', db_index=True)
account_id = models.CharField(_(u'出题账户id'), max_length=32, blank=True, null=True, help_text=u'商家账户唯一标识', db_index=True)
app_id = models.CharField(_(u'应用id'), max_length=32, blank=True, null=True, help_text=u'应用唯一标识', db_index=True)
bank_id = models.CharField(_(u'题库id'), max_length=32, blank=True, null=True, help_text=u'题库唯一标识', db_index=True)
kind_type = models.IntegerField(_(u'比赛类型'), default=IT_ISSUE, choices=KIND_TYPES, help_text=u'比赛类型')
kind_name = models.CharField(_(u'比赛名称'), max_length=32, blank=True, null=True, help_text=u'竞赛类别名称')
sponsor_name = models.CharField(_(u'赞助商名称'), max_length=60, blank=True, null=True, help_text=u'赞助商名称')
total_score = models.IntegerField(_(u'总分数'), default=0, help_text=u'总分数')
question_num = models.IntegerField(_(u'题目个数'), default=0, help_text=u'出题数量')
# 周期相关
cop_startat = models.DateTimeField(_(u'比赛开始时间'), default=timezone.now, help_text=_(u'比赛开始时间'))
period_time = models.IntegerField(_(u'答题时间'), default=60, help_text=u'答题时间(min)')
cop_finishat = models.DateTimeField(_(u'比赛结束时间'), blank=True, null=True, help_text=_(u'比赛结束时间'))
# 参与相关
total_partin_num = models.IntegerField(_(u'total_partin_num'), default=0, help_text=u'总参与人数')
class Meta:
verbose_name = _(u'比赛类别信息')
verbose_name_plural = _(u'比赛类别信息')
def __unicode__(self):
return str(self.pk)
@property
def data(self):
return {
'account_id': self.account_id,
'app_id': self.app_id,
'kind_id': self.kind_id,
'kind_type': self.kind_type,
'kind_name': self.kind_name,
'total_score': self.total_score,
'question_num': self.question_num,
'total_partin_num': self.total_partin_num,
'cop_startat': self.cop_startat,
'cop_finishat': self.cop_finishat,
'period_time': self.period_time,
'sponsor_name': self.sponsor_name,
}
class BankInfo(CreateUpdateMixin):
"""
题库信息类
"""
IT_ISSUE = 0
EDUCATION = 1
CULTURE = 2
GENERAL = 3
INTERVIEW = 4
REAR = 5
GEO = 6
SPORT = 7
BANK_TYPES = (
(IT_ISSUE, u'技术类'),
(EDUCATION, u'教育类'),
(CULTURE, u'文化类'),
(GENERAL, u'常识类'),
(GEO, u'地理类'),
(SPORT, u'体育类'),
(INTERVIEW, u'面试题')
)
bank_id = ShortUUIDField(_(u'题库id'), max_length=32, blank=True, null=True, help_text=u'题库唯一标识', db_index=True)
uid = models.CharField(_(u'用户id'), max_length=32, blank=True, null=True, help_text=u'用户唯一标识', db_index=True)
account_id = models.CharField(_(u'商家id'), max_length=32, blank=True, null=True, help_text=u'商家账户唯一标识', db_index=True)
bank_name = models.CharField(_(u'题库名称'), max_length=40, blank=True, null=True, help_text=u'题库名称')
choice_num = models.IntegerField(_(u'选择题数'), default=0, help_text=u'选择题数')
fillinblank_num = models.IntegerField(_(u'填空题数'), default=0, help_text=u'填空题数')
bank_type = models.IntegerField(_(u'题库类型'), default=IT_ISSUE, choices=BANK_TYPES, help_text=u'题库类型')
kind_num = models.IntegerField(_(u'比赛使用次数'), default=0, help_text=u'比赛使用次数')
partin_num = models.IntegerField(_(u'总答题人数'), default=0, help_text=u'总答题人数')
class Meta:
verbose_name = _(u'题库')
verbose_name_plural = _(u'题库')
def __unicode__(self):
return str(self.pk)
@property
def total_question_num(self):
return self.choice_num + self.fillinblank_num
@property
def data(self):
return {
'bank_id': self.bank_id,
'bank_name': self.bank_name,
'choice_num': self.choice_num,
'fillinblank_num': self.fillinblank_num,
'bank_type': dict(self.BANK_TYPES)[self.bank_type],
'kind_num': self.kind_num,
'partin_num': self.partin_num,
'total_question_num': self.total_question_num
}
class ChoiceInfo(CreateUpdateMixin, MediaMixin):
"""
选择题信息类
"""
QUESTION_TYPE = 'choice'
TXT = 1
IMG = 2
AUDIO = 3
CONTENT_TYPE = (
(TXT, u'文本'),
(IMG, u'图片'),
(AUDIO, u'音频'),
)
bank_id = models.CharField(_(u'题库id'), max_length=32, blank=True, null=True, help_text=u'题库唯一标识', db_index=True)
ctype = models.IntegerField(_(u'题目类型'), choices=CONTENT_TYPE, default=TXT, help_text=u'题目类型')
question = models.CharField(_(u'问题'), max_length=255, blank=True, null=True, help_text=u'题目')
answer = models.CharField(_(u'答案'), max_length=255, blank=True, null=True, help_text=u'答案')
item1 = models.CharField(_(u'选项1'), max_length=255, blank=True, null=True, help_text=u'选项一')
item2 = models.CharField(_(u'选项2'), max_length=255, blank=True, null=True, help_text=u'选项二')
item3 = models.CharField(_(u'选项3'), max_length=255, blank=True, null=True, help_text=u'选项三')
item4 = models.CharField(_(u'选项4'), max_length=255, blank=True, null=True, help_text=u'选项四')
source = models.CharField(_(u'出处'), max_length=255, blank=True, null=True, help_text=u'出处')
class Meta:
verbose_name = _(u'选择题')
verbose_name_plural = _(u'选择题')
def __unicode__(self):
return str(self.pk)
@property
def items(self):
tmp = []
if self.item1:
tmp.append(self.item1)
if self.item2:
tmp.append(self.item2)
if self.item3:
tmp.append(self.item3)
if self.item4:
tmp.append(self.item4)
return tmp
@property
def data_without_answer(self):
return {
'pk': self.pk,
'qtype': self.QUESTION_TYPE,
'bank_id': self.bank_id,
'ctype': self.ctype,
'question': self.question,
'items': self.items,
'source': self.source,
'media': self.media,
}
@property
def data(self):
return {
'pk': self.pk,
'qtype': self.QUESTION_TYPE,
'bank_id': self.bank_id,
'ctype': self.ctype,
'question': self.question,
'answer': self.answer,
'items': self.items,
'source': self.source,
'media': self.media,
}
class FillInBlankInfo(CreateUpdateMixin, MediaMixin):
"""
填空题信息类
"""
QUESTION_TYPE = 'fillinblank'
TXT = 1
IMG = 2
AUDIO = 3
CONTENT_TYPE = (
(TXT, u'文本'),
(IMG, u'图片'),
(AUDIO, u'音频'),
)
bank_id = models.CharField(_(u'题库id'), max_length=32, blank=True, null=True, help_text=u'题库唯一标识', db_index=True)
ctype = models.IntegerField(_(u'题目类型'), choices=CONTENT_TYPE, default=TXT, help_text=u'题目类型')
question = models.CharField(_(u'问题'), max_length=255, blank=True, null=True, help_text=u'题目')
answer = models.CharField(_(u'答案'), max_length=255, blank=True, null=True, help_text=u'答案')
source = models.CharField(_(u'出处'), max_length=255, blank=True, null=True, help_text=u'出处')
class Meta:
verbose_name = _(u'填空题')
verbose_name_plural = _(u'填空题')
def __unicode__(self):
return str(self.pk)
@property
def data_without_answer(self):
return {
'pk': self.pk,
'bank_id': self.bank_id,
'ctype': self.ctype,
'question': self.question,
'qtype': self.QUESTION_TYPE,
'source': self.source
}
@property
def data(self):
return {
'pk': self.pk,
'bank_id': self.bank_id,
'ctype': self.ctype,
'question': self.question,
'qtype': self.QUESTION_TYPE,
'answer': self.answer,
'source': self.source
}
class CompetitionQAInfo(CreateUpdateMixin):
"""答题记录信息类"""
UNCOMPLETED = 0
COMPLETED = 1
OVERTIME = 2
STATUS_CHOICES = (
(UNCOMPLETED, u'未完成'),
(COMPLETED, u'已完成'),
(OVERTIME, u'超时')
)
status = models.IntegerField(_(u'答题状态'), choices=STATUS_CHOICES, default=0, help_text=u'答题状态')
kind_id = models.CharField(_(u'比赛id'), max_length=32, blank=True, null=True, help_text=u'比赛类别唯一标识', db_index=True)
qa_id = ShortUUIDField(_(u'问题id'), max_length=32, blank=True, null=True, help_text=u'QA 唯一标识', db_index=True)
uid = models.CharField(_(u'用户id'), max_length=32, blank=True, null=True, help_text=u'用户唯一标识', db_index=True)
# 问题答案相关
qsrecord = models.TextField(_('问题记录'), max_length=10000, blank=True, null=True, help_text=u'问题记录')
asrecord = models.TextField(_('答案记录'), max_length=10000, blank=True, null=True, help_text=u'答案记录')
aslogrecord = models.TextField(_('答案提交记录'), max_length=10000, blank=True, null=True, help_text=u'答案提交记录')
# 耗费时间相关
started_stamp = models.BigIntegerField(_(u'开始时间戳'), default=0, help_text=u'开始时间戳(毫秒)')
finished_stamp = models.BigIntegerField(_(u'结束时间戳'), default=0, help_text=u'结束时间戳(毫秒)')
expend_time = models.IntegerField(_(u'耗时'), default=0, help_text=u'耗费时间(毫秒)')
started = models.BooleanField(_(u'已开始'), default=False, help_text=u'是否开始', db_index=True)
finished = models.BooleanField(_(u'已结束'), default=False, help_text=u'是否结束', db_index=True)
# 得分相关
correct_num = models.IntegerField(_(u'正确数'), default=0, help_text=u'答对数量')
incorrect_num = models.IntegerField(_(u'错误数'), default=0, help_text=u'答错数量')
correct_list = models.CharField(_(u'正确答案列表'), max_length=10000, blank=True, null=True, help_text=u'正确答案列表')
wrong_list = models.CharField(_(u'错误答案列表'), max_length=10000, blank=True, null=True, help_text=u'错误答案列表')
total_num = models.IntegerField(_(u'总数'), default=0, help_text=u'总共数量')
score = models.FloatField(_(u'得分'), default=0, help_text=u'分数')
class Meta:
verbose_name = _(u'比赛问题记录')
verbose_name_plural = _(u'比赛问题记录')
def __unicode__(self):
return str(self.pk)
@property
def data(self):
return {
'qa_id': self.qa_id,
'kind_id': self.kind_id,
'uid': self.uid,
}
@property
def detail(self):
return {
'status': self.status,
'qa_id': self.qa_id,
'qs': self.qsrecord,
'as': self.asrecord,
'aslog': self.aslogrecord,
'total_num': self.total_num,
'correct_num': self.correct_num,
'incorrect_num': self.incorrect_num,
'correct_list': self.correct_list,
'wrong_list': self.wrong_list,
'score': self.score,
'time': self.expend_time / 1000.000,
}

31
competition/rank_views.py Normal file
View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
from account.models import Profile
from competition.models import CompetitionKindInfo
from utils.response import json_response
from utils.decorators import check_login
from utils.redis.rrank import get_user_rank, get_rank
from utils.errors import UserError, CompetitionError
@check_login
def get_my_rank(request):
uid = request.GET.get('uid', '')
kind_id = request.GET.get('kind_id', '')
try:
profile = Profile.objects.get(uid=uid)
except Profile.DoesNotExist:
return json_response(*UserError.UserNotFound)
try:
kind_info = CompetitionKindInfo.objects.get(kind_id=kind_id)
except CompetitionKindInfo.DoesNotExist:
return json_response(*CompetitionError.CompetitionNotFound)
return json_response(200, 'OK', {
'time': get_user_rank(kind_id, uid).get('time', 0),
'score': get_user_rank(kind_id, uid).get('score', 0),
'rank': get_rank(kind_id, uid)
})

165
competition/set_render.py Normal file
View File

@@ -0,0 +1,165 @@
# -*- coding: utf-8 -*-
import os
from django.conf import settings
from django.db import transaction
from django.http.response import StreamingHttpResponse
from django.shortcuts import render
from account.models import Profile, UserInfo
from competition.models import BankInfo
from business.models import BusinessAccountInfo
from utils.decorators import check_login
from utils.errors import (FileNotFound, FileTypeError, ProfileNotFound,
TemplateNotFound, BizAccountNotFound)
from utils.small_utils import get_now_string, get_today_string
from utils.upload_questions import upload_questions
@check_login
def index(request):
"""
题库和比赛导航页
:param request: 请求对象
:return: 渲染视图和user_info用户信息数据
"""
uid = request.GET.get('uid', '')
try:
profile = Profile.objects.get(uid=uid)
except Profile.DoesNotExist:
return render(request, 'err.html', ProfileNotFound)
return render(request, 'setgames/index.html', {'user_info': profile.data})
@check_login
def set_bank(request):
"""
配置题库页面
:param request: 请求对象
:return: 渲染页面返回user_info用户信息数据和bank_types题库类型数据
"""
uid = request.GET.get('uid', '')
try:
profile = Profile.objects.get(uid=uid) # 检查账户信息
except Profile.DoesNotExist:
return render(request, 'err.html', ProfileNotFound)
bank_types = []
for i, j in BankInfo.BANK_TYPES: # 返回所有题库类型
bank_types.append({'id': i, 'name': j})
return render(request, 'setgames/bank.html', { # 渲染模板
'user_info': profile.data,
'bank_types': bank_types
})
@check_login
def template_download(request):
"""
题库模板下载
:param request: 请求对象
:return: 返回excel文件的数据流
"""
uid = request.GET.get('uid', '') # 获取uid
try:
Profile.objects.get(uid=uid) # 用户信息
except Profile.DoesNotExist:
return render(request, 'err.html', ProfileNotFound)
def iterator(file_name, chunk_size=512): # chunk_size大小512KB
with open(file_name, 'rb') as f: # rb以字节读取
while True:
c = f.read(chunk_size)
if c:
yield c # 使用yield返回数据直到所有数据返回完毕才退出
else:
break
template_path = 'web/static/template/template.xlsx'
file_path = os.path.join(settings.BASE_DIR, template_path) # 希望保留题库文件到一个单独目录
if not os.path.exists(file_path): # 路径不存在
return render(request, 'err.html', TemplateNotFound)
# 将文件以流式响应返回到客户端。
response = StreamingHttpResponse(iterator(file_path), content_type='application/vnd.ms-excel')
response['Content-Disposition'] = 'attachment; filename=template.xlsx' # 格式为xlsx
return response
@check_login
@transaction.atomic
def upload_bank(request):
"""
上传题库
:param request:请求对象
:return: 返回用户信息user_info和上传成功的个数
"""
uid = request.POST.get('uid', '') # 获取uid
bank_name = request.POST.get('bank_name', '') # 获取题库名称
bank_type = request.POST.get('bank_type', BankInfo.IT_ISSUE) # 获取题库类型
template = request.FILES.get('template', None) # 获取模板文件
for k, v in dict(BankInfo.BANK_TYPES).items():
if v == bank_type:
bank_type = k
break
if not template: # 模板不存在
return render(request, 'err.html', FileNotFound)
if template.name.split('.')[-1] not in ['xls', 'xlsx']: # 模板格式为xls或者xlsx
return render(request, 'err.html', FileTypeError)
try: # 获取用户信息
profile = Profile.objects.get(uid=uid)
except Profile.DoesNotExist:
return render(request, 'err.html', ProfileNotFound)
bank_info = BankInfo.objects.select_for_update().create( # 创建题库BankInfo
uid=uid,
bank_name=bank_name or '暂无',
bank_type=bank_type
)
today_bank_repo = os.path.join(settings.BANK_REPO, get_today_string()) # 保存文件目录以当天时间为准
if not os.path.exists(today_bank_repo):
os.mkdir(today_bank_repo) # 不存在该目录则创建
final_path = os.path.join(today_bank_repo, get_now_string(bank_info.bank_id)) + '.xlsx' # 生成文件名
with open(final_path, 'wb+') as f: # 保存到目录
f.write(template.read())
choice_num, fillinblank_num = upload_questions(final_path, bank_info) # 使用xlrd读取excel文件到数据库
return render(request, 'setgames/bank.html', { # 渲染视图
'user_info': profile.data,
'created': {
'choice_num': choice_num,
'fillinblank_num': fillinblank_num
}
})
@check_login
def set_game(request):
uid = request.GET.get('uid', '')
try:
profile = Profile.objects.get(uid=uid)
except Profile.DoesNotExist:
return render(request, 'err.html', ProfileNotFound)
try:
biz = BusinessAccountInfo.objects.get(email=profile.email)
except BusinessAccountInfo.DoesNotExist:
return render(request, 'err.html', BizAccountNotFound)
bank_types = []
for i, j in BankInfo.BANK_TYPES:
bank_types.append({'id': i, 'name': j})
form_fields = []
for f in UserInfo._meta.fields:
if f.name not in ['id', 'created_at', 'updated_at', 'kind_id', 'uid', 'status']:
form_fields.append({'field_name': f.name, 'label': f.verbose_name})
banks = BankInfo.objects.values_list('bank_name', 'bank_id', 'kind_num', 'choice_num', 'fillinblank_num').order_by('-kind_num')[:10]
banks = [{'bank_name': b[0], 'bank_id': b[1], 'kind_num': b[2], 'total_question_num': b[3] + b[4]} for b in banks]
return render(request, 'setgames/game.html', {
'account_id': biz.account_id,
'bank_types': bank_types,
'form_fields': form_fields,
'banks': banks,
})

124
competition/set_views.py Normal file
View File

@@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
from django.db import transaction
from django.views.decorators.csrf import csrf_exempt
from competition.models import BankInfo, CompetitionKindInfo
from business.models import BusinessAccountInfo, AppConfigInfo, BusinessAppInfo
from account.models import Profile
from utils.response import json_response
from utils.redis.rpageconfig import set_pageconfig
from utils.errors import SetError, BizError, ProfileError
from utils.decorators import check_login, logerr
def banks(request, s):
if s == '999':
banks = BankInfo.objects.values_list('bank_name', 'bank_id', 'kind_num', 'choice_num', 'fillinblank_num').order_by('-kind_num')
return json_response(200, 'OK', {'banks': [{'bank_name': b[0], 'bank_id': b[1], 'kind_num': b[2], 'total_question_num': b[3] + b[4]} for b in banks]})
bank_types = [t[0] for t in BankInfo.BANK_TYPES]
if int(s) in bank_types:
banks = BankInfo.objects.filter(bank_type=s).values_list('bank_name', 'bank_id', 'kind_num', 'choice_num', 'fillinblank_num')
return json_response(200, 'OK', {'banks': [{'bank_name': b[0], 'bank_id': b[1], 'kind_num': b[2], 'total_question_num': b[3] + b[4]} for b in banks]})
return json_response(*SetError.BankTypeError)
def bank_detail(request, bank_id):
try:
bank = BankInfo.objects.get(bank_id=bank_id)
except BankInfo.DoesNotExist:
return json_response(*SetError.BankInfoNotFound)
return json_response(200, 'OK', {'bank_info': bank.data})
@logerr
@csrf_exempt
@check_login
@transaction.atomic
def set_bank(request):
account_id = request.POST.get('account_id', '')
uid = request.POST.get('uid', '')
bank_id = request.POST.get('bank_id', '')
kind_name = request.POST.get('kind_name', '')
sponsor_name = request.POST.get('sponsor_name', '')
question_num = int(request.POST.get('question_num', 1))
total_score = int(request.POST.get('total_score', 100))
cop_startat = request.POST.get('cop_startat')
cop_finishat = request.POST.get('cop_finishat')
period = request.POST.get('period')
rule_text = request.POST.get('rule_text', '')
is_show_userinfo = request.POST.get('is_show_userinfo', 'false')
form_data = request.POST.get('form_data', '')
field_name_data = request.POST.get('field_name_data', '')
option_data = request.POST.get('option_data', '')
try:
BusinessAccountInfo.objects.select_for_update().get(account_id=account_id)
except BusinessAccountInfo.DoesNotExist:
return json_response(*BizError.BizAccountNotFound)
try:
profile = Profile.objects.select_for_update().get(uid=uid)
except Profile.DoesNotExist:
return json_response(*ProfileError.ProfileNotFound)
try:
bank_info = BankInfo.objects.select_for_update().get(bank_id=bank_id)
except BankInfo.DoesNotExist:
return json_response(*SetError.BankInfoNotFound)
app_info = BusinessAppInfo.objects.select_for_update().create(
account_id=account_id,
app_name=kind_name
)
app_config_values = {
'app_name': kind_name,
'rule_text': rule_text,
'is_show_userinfo': True if is_show_userinfo == 'true' else False,
'userinfo_fields': form_data.rstrip('#'),
'userinfo_field_names': field_name_data.rstrip('#'),
'option_fields': option_data.rstrip('#'),
}
app_config_info, app_config_created = AppConfigInfo.objects.select_for_update().get_or_create(
app_id=app_info.app_id,
defaults=app_config_values
)
if not app_config_created:
for k, v in app_config_values.items():
setattr(app_config_info, k, v)
app_config_info.save()
kind_values = {
'kind_name': kind_name,
'sponsor_name': sponsor_name,
'kind_type': bank_info.bank_type,
'total_score': total_score,
'question_num': question_num,
'cop_startat': cop_startat,
'period_time': period or 0,
'cop_finishat': cop_finishat
}
kind_info, kind_created = CompetitionKindInfo.objects.select_for_update().get_or_create(
account_id=account_id,
app_id=app_info.app_id,
bank_id=bank_id,
defaults=kind_values
)
if not kind_created:
for k, v in kind_values.items():
setattr(kind_info, k, v)
kind_info.save()
set_pageconfig(app_config_info.data)
return json_response(200, 'OK', {
'kind_info': kind_info.data,
})

3
competition/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

28
competition/urls.py Normal file
View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from django.conf.urls import include, url
from competition import cop_render, set_render
from django.urls import path,re_path
# 比赛url
urlpatterns = [
path('', cop_render.home, name='index'),
re_path('games/s/(\w+)', cop_render.games, name='query_games'),
path('game', cop_render.game, name='game'),
path('result', cop_render.result, name='result'),
path('rank', cop_render.rank, name='rank'),
path('search', cop_render.search, name='search'),
path('contact', cop_render.contact, name='contact'),
path('donate', cop_render.donate, name='donate'),
]
# 配置比赛url
urlpatterns += [
path('set', set_render.index, name='set_index'),
path('set/bank', set_render.set_bank, name='set_bank'),
path('set/bank/tdownload', set_render.template_download, name='template_download'),
path('set/bank/upbank', set_render.upload_bank, name='upload_bank'),
path('set/game', set_render.set_game, name='set_game'),
]

5
competition/views.py Normal file
View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
def index(request):
pass