add后端代码

main
Mia-012 2 months ago
parent 21311e3725
commit 40325d0281

@ -0,0 +1,3 @@
# 默认忽略的文件
/shelf/
/workspace.xml

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="wxapplet" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
</module>

@ -0,0 +1,12 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredIdentifiers">
<list>
<option value="seg.data_prepare.data_prepare_elec.helper_ply" />
</list>
</option>
</inspection_tool>
</profile>
</component>

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="wxapplet" project-jdk-type="Python SDK" />
<component name="PyCharmProfessionalAdvertiser">
<option name="shown" value="true" />
</component>
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Rollcall_applet.iml" filepath="$PROJECT_DIR$/.idea/Rollcall_applet.iml" />
</modules>
</component>
</project>

@ -0,0 +1,45 @@
"""
ASGI config for Rollcall_applet project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
"""
# import os
#
# from django.core.asgi import get_asgi_application
# from channels.routing import ProtocolTypeRouter, URLRouter
# from channels.auth import AuthMiddlewareStack
# from . import routings
# from api.consumers import RollCallConsumer
# os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Rollcall_applet.settings')
#
#
# # application = get_asgi_application()
#
# application = ProtocolTypeRouter(
# {
# "http": get_asgi_application(), # http走Django默认的asgi
# "websocket": URLRouter(routings.websocket_urlpatterns), # websocket走channels
# }
# )
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from api.routing import websocket_urlpatterns
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
websocket_urlpatterns
)
),
})

@ -0,0 +1,8 @@
from django.urls import re_path
# from chat import consumers # 从chat这个app导入consumers先写上稍后会说。
from api import consumers
# websocket的路由配置
websocket_urlpatterns = [
# re_path("^room/(?P<group>\w+)", consumers.ChatConsumer.as_asgi()),
re_path(r'ws/rollcall/$', consumers.RollCallConsumer.as_asgi()),
]

@ -0,0 +1,143 @@
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-g434w(!4sm=r5qn^@fz-rav2__h=g_j&jcg6d^*6jmda+t2^a7'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['192.168.124.10', 'localhost', '127.0.0.1', '10.133.64.210']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'channels',
'api',
'chat',
'debug_toolbar',
]
# 设置显示的内部 IP默认只在本地环境可用
INTERNAL_IPS = [
'127.0.0.1',
'10.133.64.210',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware',
]
ROOT_URLCONF = 'Rollcall_applet.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'Rollcall_applet.wsgi.application'
ASGI_APPLICATION = "Rollcall_applet.asgi.application"
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'roll_applet', # 数据库名字
'USER': 'root',
'PASSWORD': '123456',
'HOST': '127.0.0.1', # 那台机器安装了MySQL
'PORT': 3306,
}
}
# 使用 Redis 作为 Channel 层的后端
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('127.0.0.1', 6379)],
},
},
}
# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
STATIC_URL = '/static/'
# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
DEBUG_TOOLBAR_CONFIG = {
'SHOW_TOOLBAR_CALLBACK': lambda request: True, # 始终显示工具栏
}

@ -0,0 +1,22 @@
from django.contrib import admin
from django.urls import path, include # 导入 path 和 re_path
from api import views
urlpatterns = [
# # 管理员页面的路由(如果需要)
# path('admin/', admin.site.urls),
# 上传学生名单
path('api/upload-students/', views.upload_students, name='upload_students'),
# # 随机选择学生
# path('api/select-student/', views.select_student, name='select-student'),
# path('api/classroom_view/', views.classroom_view, name='classroom'),
path('api/get_student_info/<str:student_id>/', views.get_student_info, name='get_student_info'),
path('api/update_student_score/', views.update_student_score, name='update_student_score'),
path('api/leaderboard/', views.leaderboard, name='leaderboard'),
path('api/create_pledge/', views.create_pledge, name='create_pledge'),
path('api/update_question_score', views.update_question_score, name='update_question_score'),
path('api/export_students_scores', views.export_students_scores, name='export_students_scores'),
path('__debug__/', include('debug_toolbar.urls')),
]

@ -0,0 +1,16 @@
"""
WSGI config for Rollcall_applet project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Rollcall_applet.settings')
application = get_wsgi_application()

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

@ -0,0 +1,6 @@
from django.apps import AppConfig
class ApiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'api'

@ -0,0 +1,119 @@
from channels.generic.websocket import AsyncWebsocketConsumer
import json
import random
from asgiref.sync import sync_to_async
from .models import Students
class ClassroomConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.teacher_group = 'teacher_group'
self.student_group = 'student_group'
await self.channel_layer.group_add(self.teacher_group, self.channel_name)
await self.channel_layer.group_add(self.student_group, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(self.teacher_group, self.channel_name)
await self.channel_layer.group_discard(self.student_group, self.channel_name)
async def receive(self, text_data):
data = json.loads(text_data)
if data['action'] == 'pick_student':
selected_student = await self.pick_student()
await self.send_student_message(selected_student)
elif data['action'] == 'use_exemption_card':
# 处理学生使用豁免卡的信息
student_id = data['studentId']
new_score = data['newScore']
# 更新学生积分等逻辑,假设在数据库中保存
await self.update_student_score(student_id, new_score)
# 将豁免卡信息发送给教师组
await self.channel_layer.group_send(
self.teacher_group,
{
'type': 'exemption_card_used',
'student_id': student_id,
'student_name': data['studentName'],
'new_score': new_score,
}
)
# 处理教师端收到豁免卡使用信息
async def exemption_card_used(self, event):
student_id = event['student_id']
student_name = event['student_name']
new_score = event['new_score']
# 在教师端显示学生使用豁免卡的信息
await self.send(text_data=json.dumps({
'action': 'exemption_card_used',
'student_id': student_id,
'student_name': student_name,
'new_score': new_score,
}))
async def send_student_message(self, student):
if student is None:
return # 如果没有选中学生,直接返回
message = {
'name': student.name,
'student_id': student.sid,
}
# 将点名结果发送到学生组
await self.channel_layer.group_send(
self.student_group,
{
'type': 'student_message',
'message': message,
}
)
# 接收到点名结果后发送给学生端
async def student_message(self, event):
message = event['message']
await self.send(text_data=json.dumps({
'action': 'student_picked',
'name': message['name'],
'student_id': message['student_id'],
}))
async def pick_student(self):
# 使用 sync_to_async 获取学生列表
students = await sync_to_async(list)(Students.objects.all())
if not students:
return None
# 计算权重
weights = []
for student in students:
score = float(student.score)
if score < 0:
weights.append(-score) # 负分数,绝对值越大权重越大
elif score == 0:
weights.append(0.2) # 分数为0设置为一个中等权重
else:
weights.append(1 / (score + 10)) # 正分数,分数越大权重越小
# 防止所有权重为0的情况
if sum(weights) == 0:
return random.choice(students) # 如果所有权重为0随机选一个学生
# 根据权重选择学生
chosen_student = random.choices(students, weights=weights, k=1)[0]
return chosen_student
async def update_student_score(self, student_id, new_score):
# 使用 Django ORM 更新学生积分
student = await sync_to_async(Students.objects.get)(sid=student_id)
student.score = new_score
await sync_to_async(student.save)()

@ -0,0 +1,34 @@
# Generated by Django 3.2.25 on 2024-10-09 06:20
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Pledge',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('pledge_score', models.IntegerField()),
('status', models.IntegerField(default=0)),
('created_at', models.DateTimeField(auto_now_add=True)),
('student_id', models.CharField(max_length=10)),
('student_name', models.CharField(max_length=10)),
],
),
migrations.CreateModel(
name='Students',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sid', models.CharField(max_length=10, unique=True, verbose_name='学号')),
('name', models.CharField(max_length=10, verbose_name='姓名')),
('score', models.DecimalField(decimal_places=1, default=0, max_digits=5, verbose_name='积分')),
],
),
]

@ -0,0 +1,20 @@
from django.db import models
class Students(models.Model):
""" 学生信息表 """
sid = models.CharField(verbose_name="学号", max_length=10, unique=True)
name = models.CharField(verbose_name="姓名", max_length=10)
score = models.DecimalField(verbose_name="积分", max_digits=5, decimal_places=1, default=0)
# signin = models.IntegerField(verbose_name="签到次数", default=0)
# absences = models.IntegerField(verbose_name="缺席次数", default=0)
class Pledge(models.Model):
""" 典当信息表 """
pledge_score = models.IntegerField() # 典当的积分
status = models.IntegerField(default=0) # 典当状态0 表示未完成
created_at = models.DateTimeField(auto_now_add=True) # 自动设置为当前时间
student_id = models.CharField(max_length=10) # 学号
student_name = models.CharField(max_length=10) # 姓名

@ -0,0 +1,6 @@
from django.urls import path
from .consumers import ClassroomConsumer
websocket_urlpatterns = [
path('ws/classroom/', ClassroomConsumer.as_asgi()),
]

@ -0,0 +1,105 @@
import os
import pytest
import django
import json
from asgiref.sync import sync_to_async
from channels.testing import WebsocketCommunicator
from channels.layers import get_channel_layer
from channels.db import database_sync_to_async # 确保导入
from api.consumers import ClassroomConsumer
from api.models import Students
# 设置 Django 设置模块
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Rollcall_applet.settings')
django.setup()
@pytest.mark.django_db
@database_sync_to_async
def create_student(sid, name, score):
return Students.objects.create(sid=sid, name=name, score=score)
@pytest.mark.django_db
@database_sync_to_async
def get_student(sid):
return Students.objects.get(sid=sid)
@pytest.mark.django_db
@database_sync_to_async
def update_student_score(sid, new_score):
student = Students.objects.get(sid=sid)
student.score = new_score
student.save()
@pytest.mark.django_db
@pytest.mark.asyncio
async def test_pick_student():
# 创建一些学生
await create_student("1", "Student 1", 10)
await create_student("2", "Student 2", 0)
await create_student("3", "Student 3", -5)
consumer = ClassroomConsumer()
await consumer.connect()
chosen_student = await consumer.pick_student()
assert chosen_student is not None
assert chosen_student.sid in ["1", "2", "3"]
@pytest.mark.django_db
@pytest.mark.asyncio
async def test_exemption_card_used():
# 创建一个学生
student = await create_student("1", "Student 1", 10)
consumer = ClassroomConsumer()
await consumer.connect()
# 模拟接收豁免卡使用信息
await consumer.receive(text_data=json.dumps({
'action': 'use_exemption_card',
'studentId': student.sid,
'newScore': 20,
'studentName': student.name,
}))
updated_student = await get_student(student.sid)
assert updated_student.score == 20
@pytest.mark.django_db
@pytest.mark.asyncio
async def test_send_student_message():
student = await create_student("2", "Student 2", 0)
consumer = ClassroomConsumer()
await consumer.connect()
# 发送学生消息
await consumer.send_student_message(student)
# 验证消息发送到学生组
channel_layer = get_channel_layer()
message = await channel_layer.receive(consumer.student_group)
assert message['type'] == 'student_message'
assert message['message']['name'] == student.name
assert message['message']['student_id'] == student.sid
@pytest.mark.django_db
@pytest.mark.asyncio
async def test_update_student_score():
student = await create_student("3", "Student 3", -5)
consumer = ClassroomConsumer()
await consumer.connect()
await update_student_score(student.sid, 30)
updated_student = await get_student(student.sid)
assert updated_student.score == 30

@ -0,0 +1,90 @@
from django.test import TestCase
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from api.models import Students
from api.models import Pledge
from decimal import Decimal
class StudentsAPITests(APITestCase):
def setUp(self):
# 创建一些测试数据
self.student = Students.objects.create(sid='123', name='Student1', score=Decimal('0.0'))
self.student = Students.objects.create(sid='WW1', name='小明', score=Decimal('0.0'))
self.student = Students.objects.create(sid='@1wW23rbrsrf3', name='123HI', score=Decimal('0.0'))
def test_update_student_score(self):
# 测试更新学生积分的功能
response = self.client.post(reverse('update_student_score'), {'student_id': self.student.sid, 'score': 5.5})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.student.refresh_from_db() # 刷新数据库数据
self.assertEqual(self.student.score, Decimal('5.5'))
def test_create_pledge_success(self):
# 测试成功创建典当信息
data = {
'studentId': self.student.sid,
'studentName': self.student.name,
'pledgeScore': 10,
}
response = self.client.post(reverse('create_pledge'), data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertIn('典当信息已保存', response.data.values())
self.assertTrue(Pledge.objects.filter(student_id=self.student.sid).exists())
def test_create_pledge_missing_fields(self):
# 测试创建典当时缺少字段
data = {
'studentId': self.student.sid,
'studentName': '',
'pledgeScore': 10,
}
response = self.client.post(reverse('create_pledge'), data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('所有字段都是必需的', response.data['error'])
def test_update_question_score_success_without_pledge(self):
# 测试更新积分时没有典当信息
data = {
'studentId': self.student.sid,
'questionScore': 2,
}
response = self.client.post(reverse('update_question_score'), data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.student.refresh_from_db()
self.assertEqual(self.student.score, Decimal('2.0')) # 0.0 + 2
def test_update_question_score_student_not_found(self):
# 测试更新积分时学生不存在
data = {
'studentId': '999', # 一个不存在的学生ID
'questionScore': 5,
}
response = self.client.post(reverse('update_question_score'), data)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.assertEqual(response.data['error'], '学生不存在')
def test_update_question_score_exception_handling(self):
# 测试更新积分时的异常处理
pledge = Pledge.objects.create(student_id=self.student.sid, student_name=self.student.name, pledge_score=5,
status=0)
# 模拟异常(例如分数为字符串)
data = {
'studentId': self.student.sid,
'questionScore': 'erewc3ergag',
}
response = self.client.post(reverse('update_question_score'), data)
self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR)

@ -0,0 +1,253 @@
import random
from django.shortcuts import render
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
import openpyxl
from .models import Students
from .models import Pledge
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
from django.http import JsonResponse
from decimal import Decimal
from django.http import HttpResponse
from django.db.models import Min, Max
from django.db import transaction
# class TestView(APIView):
# def get(self, request):
# data = {
# "message": "Hello from Django!",
# "status": "success"
# }
# return Response(data, status=status.HTTP_200_OK)
# @api_view(['POST'])
# def upload_students(request):
# # 获取上传的文件
# file = request.FILES.get('file')
#
# if not file:
# return Response({'error': '没有上传文件'}, status=status.HTTP_400_BAD_REQUEST)
#
# try:
# # 读取 Excel 文件
# wb = openpyxl.load_workbook(file)
# sheet = wb.active
#
# # 遍历 Excel 表格的每一行,并保存到数据库
# for row in sheet.iter_rows(min_row=2, values_only=True):
# sid, name = row[0], row[1]
#
# # 使用 update_or_create 保存到数据库
# Students.objects.update_or_create(
# sid=sid,
# defaults={'name': name, 'score': 0}
# )
#
# return Response({'message': '导入成功!'}, status=status.HTTP_200_OK)
#
# except Exception as e:
# return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@api_view(['POST'])
def upload_students(request):
file = request.FILES.get('file')
if not file:
return Response({'error': '没有上传文件'}, status=status.HTTP_400_BAD_REQUEST)
try:
wb = openpyxl.load_workbook(file)
sheet = wb.active
# 使用列表收集学生数据
students_data = []
for row in sheet.iter_rows(min_row=2, values_only=True):
sid, name = row[0], row[1]
students_data.append(Students(sid=sid, name=name, score=0))
# 开始一个事务
with transaction.atomic():
# 清空表
Students.objects.all().delete()
# 批量插入学生数据
Students.objects.bulk_create(students_data)
return Response({'message': '导入成功!'}, status=status.HTTP_200_OK)
except Exception as e:
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@api_view(['GET'])
def get_student_info(request, student_id):
try:
student = Students.objects.get(sid=student_id)
return JsonResponse({'name': student.name, 'score': student.score})
except Students.DoesNotExist:
return JsonResponse({'error': 'Student not found'}, status=404)
@api_view(['POST'])
def update_student_score(request):
student_id = request.data.get('student_id')
score = request.data.get('score')
try:
student = Students.objects.get(sid=student_id)
student.score = Decimal(score) # 将分数转换为 Decimal 类型
student.save()
return JsonResponse({'success': True})
except Students.DoesNotExist:
return JsonResponse({'error': 'Student not found'}, status=404)
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
@api_view(['GET'])
def get_student_info_by_name(request, student_name):
try:
student = Students.objects.get(name=student_name) # 根据姓名查询学生
return JsonResponse({'name': student.name, 'score': student.score})
except Students.DoesNotExist:
return JsonResponse({'error': 'Student not found'}, status=404)
@api_view(['POST'])
def send_exemption_card(request):
student_id = request.data.get('studentId')
student_name = request.data.get('studentName')
new_score = request.data.get('newScore')
try:
student = Students.objects.get(sid=student_id)
student.score = new_score # 更新积分
student.save()
return JsonResponse({'message': '豁免卡信息处理成功'}, status=200)
except Students.DoesNotExist:
return JsonResponse({'error': '学生未找到'}, status=404)
@api_view(['GET'])
def leaderboard(request):
# 获取积分排名前5的学生
top_students = Students.objects.order_by('-score')[:5]
leaderboard_data = [{'name': student.name, 'score': student.score} for student in top_students]
return JsonResponse(leaderboard_data, safe=False)
@api_view(['POST'])
def create_pledge(request):
student_id = request.data.get('studentId')
student_name = request.data.get('studentName')
pledge_score = request.data.get('pledgeScore')
if not student_id or not student_name or not pledge_score:
return Response({'error': '所有字段都是必需的'}, status=status.HTTP_400_BAD_REQUEST)
pledge = Pledge.objects.create(
student_id=student_id,
student_name=student_name,
pledge_score=pledge_score,
status=0 # 设置状态为 0表示未完成
)
return Response({'message': '典当信息已保存', 'pledge_id': pledge.id}, status=status.HTTP_201_CREATED)
@api_view(['POST'])
def update_question_score(request):
student_id = request.data.get('studentId')
question_score = request.data.get('questionScore')
print(request.data)
try:
student = Students.objects.get(sid=student_id)
# student = Students.objects.prefetch_related('pledges').get(sid=student_id)
student.score += Decimal(question_score) # 直接增加教师给的积分
student.save() # 保存更新后的积分
pledge = Pledge.objects.filter(student_id=student_id).order_by('-created_at').first()
# pledge = student.pledges.order_by('-created_at').first()
if pledge:
if pledge.status == 0: # 典当状态为0
pledge_score_decimal = Decimal(pledge.pledge_score) # 获取典当的积分
if question_score >= 2:
student.score += pledge_score_decimal * Decimal(2) # 加倍典当积分
else:
student.score -= pledge_score_decimal # 减去典当积分
pledge.status = 1 # 更新典当状态为1
pledge.save() # 保存更新后的典当状态
student.save() # 保存最终的学生积分
# 返回积分和典当信息
return Response({
'message': '积分更新成功',
'current_score': str(student.score),
'pledge_score': str(pledge.pledge_score),
'pledge_status': pledge.status,
}, status=status.HTTP_200_OK)
else:
# 返回更新后的积分和初始积分
return Response({
'message': '积分更新成功,但没有新的典当信息',
'updated_score': str(student.score),
}, status=status.HTTP_200_OK)
else:
return Response({
'updated_score': str(student.score),
}, status=status.HTTP_200_OK)
except Students.DoesNotExist:
return Response({'error': '学生不存在'}, status=status.HTTP_404_NOT_FOUND)
except Exception as e:
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# 定义导出学生数据的 API 视图,接受 POST 请求
@api_view(['POST'])
def export_students_scores(request):
try:
# 获取所有学生信息
students = Students.objects.all()
# 找到最低和最高积分用于计算百分比
min_score = students.aggregate(min_score=Min('score'))['min_score']
max_score = students.aggregate(max_score=Max('score'))['max_score']
# 处理为百分比
def calculate_percentage(score, min_score, max_score):
if max_score == min_score: # 避免分母为0
return 50 # 所有学生得分相同时统一设为50%
return (score - min_score) / (max_score - min_score) * 100
# 创建 Excel 工作簿
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "学生积分百分比"
# 写入表头
ws.append(["学号", "姓名", "积分", "百分比"])
# 写入每个学生的信息
for student in students:
percentage = calculate_percentage(student.score, min_score, max_score)
ws.append([student.sid, student.name, float(student.score), f"{percentage:.2f}%"])
# 保存 Excel 文件
response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = 'attachment; filename=students_scores.xlsx'
wb.save(response)
return response
except Exception as e:
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

@ -0,0 +1,6 @@
from django.apps import AppConfig
class ChatConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'chat'

@ -0,0 +1,13 @@
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
class ChatConsumer(WebsocketConsumer):
def websocket_connect(self, message):
self.accept()
def websocket_receive(self, message):
self.send(text_data='OK') # 返回给客户端的消息
def websocket_disconnect(self, message):
raise StopConsumer()

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

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

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Rollcall_applet.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

Binary file not shown.

@ -0,0 +1,2 @@
[pytest]
DJANGO_SETTINGS_MODULE = Rollcall_applet.settings
Loading…
Cancel
Save