commit f56b66d8f126a5ebdff628431d6a1a6a4ece23cc Author: pzc-x99 <927068267@qq.com> Date: Fri Feb 28 23:05:31 2025 +0800 /health/ /error_code_map/ /ec_user/* /diagnose/* /admin/* diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..43cabbf --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ + +/upload_file +/generate_paper +*.pyc \ No newline at end of file diff --git a/authentication/__init__.py b/authentication/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/authentication/admin.py b/authentication/admin.py new file mode 100644 index 0000000..6a8588a --- /dev/null +++ b/authentication/admin.py @@ -0,0 +1,8 @@ +from django.contrib import admin + +# Register your models here. + +from django.contrib import admin +from .models import CustomToken + +admin.site.register(CustomToken) diff --git a/authentication/apps.py b/authentication/apps.py new file mode 100644 index 0000000..8bab8df --- /dev/null +++ b/authentication/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AuthenticationConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'authentication' diff --git a/authentication/authentication.py b/authentication/authentication.py new file mode 100644 index 0000000..38e5f2b --- /dev/null +++ b/authentication/authentication.py @@ -0,0 +1,31 @@ +from rest_framework.authentication import TokenAuthentication +from rest_framework.exceptions import AuthenticationFailed +from .models import CustomToken +from datetime import timedelta +from django.utils import timezone +from functools import wraps + +class CustomTokenAuthentication(TokenAuthentication): + def authenticate(self, request): + # 获取请求中的 token + token_key = request.headers.get('token') + if token_key: + # 移除前缀 (如果有 "Token " 前缀的话) + if token_key.startswith('Token '): + token_key = token_key[6:] + # print(token_key) + try: + # 获取自定义的 Token 对象 + token = CustomToken.objects.get(key=token_key) + + # 检查 token 是否过期 + if token.expiration_date < timezone.now(): + raise AuthenticationFailed('Token has expired.') + + return (token.user, token) # 返回用户和 token + + except CustomToken.DoesNotExist: + raise AuthenticationFailed('Invalid token.') + + raise AuthenticationFailed('Authorization header missing or invalid.') + diff --git a/authentication/migrations/0001_initial.py b/authentication/migrations/0001_initial.py new file mode 100644 index 0000000..5cf42c8 --- /dev/null +++ b/authentication/migrations/0001_initial.py @@ -0,0 +1,32 @@ +# Generated by Django 4.2.19 on 2025-02-26 08:21 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='CustomToken', + fields=[ + ('key', models.CharField(max_length=40, primary_key=True, serialize=False, verbose_name='Key')), + ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), + ('extra_info', models.CharField(blank=True, max_length=255, null=True)), + ('last_used', models.DateTimeField(auto_now=True)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='auth_token', to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'verbose_name': 'Token', + 'verbose_name_plural': 'Tokens', + 'abstract': False, + }, + ), + ] diff --git a/authentication/migrations/0002_customtoken_expiration_date.py b/authentication/migrations/0002_customtoken_expiration_date.py new file mode 100644 index 0000000..d7356e1 --- /dev/null +++ b/authentication/migrations/0002_customtoken_expiration_date.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.19 on 2025-02-26 08:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authentication', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='customtoken', + name='expiration_date', + field=models.DateTimeField(blank=True, null=True), + ), + ] diff --git a/authentication/migrations/__init__.py b/authentication/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/authentication/models.py b/authentication/models.py new file mode 100644 index 0000000..52002b6 --- /dev/null +++ b/authentication/models.py @@ -0,0 +1,24 @@ +from django.db import models + +# Create your models here. + +from rest_framework.authtoken.models import Token + +class CustomToken(Token): + # 在 Token 上添加更多字段(可以根据需求自定义) + extra_info = models.CharField(max_length=255, null=True, blank=True) # 示例附加信息字段 + last_used = models.DateTimeField(auto_now=True) # 记录最后一次使用的时间 + expiration_date = models.DateTimeField(null=True, blank=True) # 记录 Token 的过期时间 + + def __str__(self): + # 返回 Token 的字符串表示,包括用户名和 key + return f"Token for {self.user.username} - {self.key}" + + def get_user_info(self): + # 可以提供一个方法来获取与 Token 关联的用户的更多信息 + return { + "username": self.user.username, + "email": self.user.email, + "role": self.user.groups.first().name if self.user.groups.exists() else "No role" + } + diff --git a/authentication/tests.py b/authentication/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/authentication/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/authentication/urls.py b/authentication/urls.py new file mode 100644 index 0000000..52b25f1 --- /dev/null +++ b/authentication/urls.py @@ -0,0 +1,6 @@ +from django.urls import path +from .views import MyProtectedView + +urlpatterns = [ + path('', MyProtectedView.as_view(), name='protected-view'), +] diff --git a/authentication/views.py b/authentication/views.py new file mode 100644 index 0000000..e1efe50 --- /dev/null +++ b/authentication/views.py @@ -0,0 +1,19 @@ +from django.shortcuts import render + +# Create your views here. + +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.permissions import IsAuthenticated +from authentication.authentication import CustomTokenAuthentication + +class MyProtectedView(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def get(self, request): + # 只有认证通过的用户可以访问这个视图 + + print(request.user.id) # 访问当前用户 + return Response({"message": "This is a protected view!"}) + diff --git a/celerybeat-schedule.bak b/celerybeat-schedule.bak new file mode 100644 index 0000000..f17cf48 --- /dev/null +++ b/celerybeat-schedule.bak @@ -0,0 +1,4 @@ +'entries', (0, 6) +'__version__', (512, 15) +'tz', (1024, 23) +'utc_enabled', (1536, 4) diff --git a/celerybeat-schedule.dat b/celerybeat-schedule.dat new file mode 100644 index 0000000..7bce756 Binary files /dev/null and b/celerybeat-schedule.dat differ diff --git a/celerybeat-schedule.dir b/celerybeat-schedule.dir new file mode 100644 index 0000000..f17cf48 --- /dev/null +++ b/celerybeat-schedule.dir @@ -0,0 +1,4 @@ +'entries', (0, 6) +'__version__', (512, 15) +'tz', (1024, 23) +'utc_enabled', (1536, 4) diff --git a/diagnose/__init__.py b/diagnose/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/diagnose/admin.py b/diagnose/admin.py new file mode 100644 index 0000000..46df67d --- /dev/null +++ b/diagnose/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin + +# Register your models here. +from . models import Papers + +@admin.register(Papers) + +class PaperAdmin(admin.ModelAdmin): + list_display = ('id', 'uuid', 'status', 'created_at', 'updated_at') \ No newline at end of file diff --git a/diagnose/apps.py b/diagnose/apps.py new file mode 100644 index 0000000..32c8669 --- /dev/null +++ b/diagnose/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class DiagnoseConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'diagnose' diff --git a/diagnose/ch.jpg b/diagnose/ch.jpg new file mode 100644 index 0000000..033a50c Binary files /dev/null and b/diagnose/ch.jpg differ diff --git a/diagnose/dp_views.py b/diagnose/dp_views.py new file mode 100644 index 0000000..6971e82 --- /dev/null +++ b/diagnose/dp_views.py @@ -0,0 +1,253 @@ +# -*- coding: utf-8 -*- + +import os +from openai import OpenAI +import markdown2 +import pdfkit +from django.conf import settings +from django.http import JsonResponse, FileResponse +from celery import shared_task +import uuid +from django.utils import timezone +from rest_framework.views import APIView +from rest_framework.permissions import IsAuthenticated +from authentication.authentication import CustomTokenAuthentication +from django.core.exceptions import ObjectDoesNotExist +from .models import Papers +from ec_user.models import SchoolInfo +import json +# ============== PYdoc =================== +# from docx import Document +# import pypandoc +# pypandoc.download_pandoc() + +# ================ weasyprint =========== +# 库用不了 +# from weasyprint import HTML +# HTML(string=html).write_pdf("output.pdf") + +class DpArguments(): + subject = "数学" + textbook_version = "人教版" + + start_grade = 8 + start_semester = 1 + start_chapter = 1 + + end_grade = 9 + end_semester = 1 + end_chapter = 1 + + student = { + "grade": 8, + "age": 10, + "school": "民族小学" + } + + def __getstate__(self): + # 返回一个字典,包含所有需要序列化的属性 + state = self.__dict__.copy() + # 如果你不想序列化某个属性,可以从字典中删除 + # 例如: del state['student'] + return state + + def __setstate__(self, state): + # 用字典中的数据恢复对象状态 + self.__dict__.update(state) + + + +def generate_html_paper(md_result, file_path_name): + # 写到md中 + md_file = open(f'{file_path_name}.md', 'w', encoding='utf-8') + md_file.write(md_result) + md_file.close() + + # 从md中读出来再写到html中 + md_read_file = open(f'{file_path_name}.md', 'r', encoding='utf-8') + md_text = md_read_file.read() + md_read_file.close() + html = markdown2.markdown(md_text) + html_write_file = open(f'{file_path_name}.html', 'w', encoding='utf-8') + html_write_file.write( + """
+ + + """+html) + html_write_file.close() + +def convert_to_pdf(file_path_name): + # =============== pdfkit ================ + # 从html中读出来再写到pdf中 + f = open(f'{file_path_name}.html', 'r', encoding='utf-8') + content = f.read() + f.close() + config = pdfkit.configuration(wkhtmltopdf=settings.WKHTMLTOPDF_PATH) + pdfkit.from_string(content, f'{file_path_name}.pdf', configuration=config) + +@shared_task +def async_wraper(file_path_name, uuid_str, subject, textbook_version, start_grade, \ + start_semester, start_chapter, end_grade, end_semester, end_chapter, student): + + # client = OpenAI( + # api_key = settings.OPAI_API_KEY, + # base_url = settings.OPAI_BASE_URL, + # ) + # completion = client.chat.completions.create( + # model = "deepseek-r1-250120", # your model endpoint ID + # messages = [ + # {"role": "system", "content": "你是教学助手"}, + # # {"role": "user", + # # "content": f"请根据以下信息生成一份检测卷:学科为{subject},教材版本是{textbook_version},\ + # # 检测范围从{start_grade}年级{start_semester}学期{start_chapter}章\ + # # 到{end_grade}年级{end_semester}学期{end_chapter}章,学生年级为{student.get('grade')},\ + # # 年龄为{student.get('age')},所在学校是{student.get('school')}。\ + # # 根据答题所需时间一个半小时设置题量。题目有梯度。\ + # # 试卷题目为{subject}{textbook_version}检测卷。只生成题目,不输出答案及说明等其他内容。" + # # }, + # {"role": "user", "content": f"""请严格按照以下要求生成一份{subject}检测卷: + # 【试卷信息】 + # 1. 标题:**{subject}{textbook_version}综合检测卷** + # 2. 检测范围:{start_grade}年级{start_semester}学期第{start_chapter}章 至 + # {end_grade}年级{end_semester}学期第{end_chapter}章 + # 3. 适用对象:{student.get('school')} {student.get('grade')}年级学生(年龄{student.get('age')}) + # 【命题要求】 + # 1. 题型结构: + # - 按难度梯度分为基础题(60%)、中档题(30%)、提高题(10%) + # - 根据学科特点设计合理的题型(如数学可设置选择题/填空题/计算题/应用题,语文可设置阅读理解/文言文/写作等) + # - 同一知识点不重复考查 + # 2. 题量控制: + # - 总题数控制在18-22题(以优秀学生完成时间约75分钟为标准) + # - 选择题不超过5题,填空题不超过6题 + # - 需包含至少1道综合应用题(理科)或材料分析题(文科) + # 3. 内容要求: + # - 严格按照指定章节范围命题 + # - 题目表述清晰无歧义,数字单位标注完整 + # - 禁止出现需图片辅助的题目 + # - 数学类题目需给出必要公式位置(如"(提示:可用公式____)") + # 【输出格式】 + # 仅输出以下内容: + # 1. 试卷标题(加粗居中) + # 2. 考试说明(含总分值100分、考试时长、姓名班级填写处) + # 3. 分模块的题目内容(标注题型、分值和题号) + # 4. "——以下为题目区域——" 分隔线 + # 禁止包含: + # - 答案及解析 + # - 评分标准 + # - 额外说明文字 + # - markdown格式及特殊符号"""} + # ], + # ) + # generate_html_paper(completion.choices[0].message.content, file_path_name) + import time + time.sleep(30) + generate_html_paper(""" # Test md""", file_path_name) + try: + obj = Papers.objects.get(uuid=uuid_str) # 只获取一个对象 + obj.status = 'D' + obj.updated_at = timezone.now() + obj.save() + except ObjectDoesNotExist: + print("warning: record not found") + +class MyProtectedGeneratePaper(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def post(self, request): + user = request.user + uuid_str = str(uuid.uuid4()) + radom_path = user.username +'_'+ uuid_str + file_path_name = os.path.join(settings.BASE_DIR, 'generate_paper', radom_path) + + # 处理 POST 请求,更新用户的联系信息 + try: + data = json.loads(request.body) + subject = data.get("subject") + textbook_version = data.get("textbook_version") + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON format"}, status=400) + + # auth_subjects = user.subjects.all() + if not user.subjects.filter(name=subject).exists(): + print("subject", subject) + return JsonResponse({"error_code": "2001"}, status=403) + + if user.subject_usage_count <= 0: + return JsonResponse({"error_code": "2002"}, status=403) + + dp_args = DpArguments() + dp_args.subject = subject + dp_args.textbook_version = textbook_version + + school_obj = SchoolInfo.objects.filter(user=user).first() + if school_obj is None: + return JsonResponse({"error_code": "2003"}, status=403) + + dp_args.student['age'] = user.age + dp_args.student['school'] = school_obj.school_name + dp_args.student['grade'] = school_obj.grade + + print(dp_args.student, dp_args.subject, dp_args.textbook_version, dp_args.start_grade, dp_args.start_semester,\ + dp_args.start_chapter, dp_args.end_grade, dp_args.end_semester, dp_args.end_chapter) + # 调用异步任务 + obj = Papers.objects.create(uuid=uuid_str, user=user, status='P') + obj.save() + user.subject_usage_count = user.subject_usage_count - 1 + user.save() + async_wraper.delay(file_path_name, uuid_str, \ + dp_args.subject, dp_args.textbook_version, dp_args.start_grade, dp_args.start_semester, dp_args.start_chapter, \ + dp_args.end_grade, dp_args.end_semester, dp_args.end_chapter, dp_args.student) + return JsonResponse({"status": "success", "uuid": uuid_str}) + +class MyProtectedGenerateCheck(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def post(self, request): + user = request.user + # 处理 POST 请求,更新用户的联系信息 + try: + data = json.loads(request.body) + uuid_str = data.get("uuid") + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON format"}, status=400) + try: + # print(uuid_str) + obj = Papers.objects.get(uuid=uuid_str, user=user) # 只获取一个对象 + if obj.status == 'D': + return JsonResponse({"status": "done", "download_url": f'/diagnose/download/?uuid_str={uuid_str}'}) + else: + return JsonResponse({"status": "processing", "download_url": None}) + except ObjectDoesNotExist: + return JsonResponse({"error_code": "2004"}, status=404) + +class MyProtectedDownloadPaper(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def get(self, request): + uuid_str = request.query_params.get('uuid_str', None) + if not uuid_str: + return JsonResponse({"error": "Missing uuid_str in query parameters"}, status=400) + + user = request.user + username = user.username + file_path_name = os.path.join(settings.BASE_DIR, 'generate_paper', f'{username}_{uuid_str}') + + # 检查文件是否存在 + print(file_path_name) + if not os.path.exists(file_path_name + ".html"): + return JsonResponse({"error_code": "2005"}, status=403) + + convert_to_pdf(file_path_name) + + try: + # 直接将文件路径传递给 FileResponse + response = FileResponse(open(file_path_name+".pdf", 'rb'), as_attachment=True) + response['Content-Disposition'] = f'attachment; filename="{username}_{uuid_str}.pdf"' + return response + except Exception as e: + return JsonResponse({"error": f"Failed to open file: {str(e)}"}, status=500) \ No newline at end of file diff --git a/diagnose/migrations/0001_initial.py b/diagnose/migrations/0001_initial.py new file mode 100644 index 0000000..dd0d563 --- /dev/null +++ b/diagnose/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.19 on 2025-02-27 06:23 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Papers', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('uuid', models.CharField(max_length=100)), + ('status', models.CharField(choices=[('C', '任务创建'), ('P', '生成中'), ('D', '已完成')], default='C', max_length=1)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/diagnose/migrations/0002_papers_created_at_papers_updated_at.py b/diagnose/migrations/0002_papers_created_at_papers_updated_at.py new file mode 100644 index 0000000..7976f50 --- /dev/null +++ b/diagnose/migrations/0002_papers_created_at_papers_updated_at.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.19 on 2025-02-27 06:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('diagnose', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='papers', + name='created_at', + field=models.DateTimeField(auto_now_add=True, null=True), + ), + migrations.AddField( + model_name='papers', + name='updated_at', + field=models.DateTimeField(auto_now=True, null=True), + ), + ] diff --git a/diagnose/migrations/__init__.py b/diagnose/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/diagnose/models.py b/diagnose/models.py new file mode 100644 index 0000000..c4bc214 --- /dev/null +++ b/diagnose/models.py @@ -0,0 +1,19 @@ +from django.db import models +from ec_user.models import EcUser +# Create your models here. + +class Papers(models.Model): + STATUS_CHOICES = ( + ('C', '任务创建'), + ('P', '生成中'), + ('D', '已完成') + ) + uuid = models.CharField(max_length=100) # 试卷的唯一标识符 + # 定义一个与EcUser一对多关联的外键 + user = models.ForeignKey(EcUser, on_delete=models.CASCADE) # 当EcUser被删除时,关联的Papers也会被删除 + status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='C') # 默认值设置为'C' + created_at = models.DateTimeField(auto_now_add=True,null=True, blank=True) # 创建时间 + updated_at = models.DateTimeField(auto_now=True,null=True, blank=True) # 更新时间 + + def __str__(self): + return self.user.username + '的试卷' \ No newline at end of file diff --git a/diagnose/openaitest.py b/diagnose/openaitest.py new file mode 100644 index 0000000..de0c8d5 --- /dev/null +++ b/diagnose/openaitest.py @@ -0,0 +1,43 @@ +import os +from openai import OpenAI +import base64 + +client = OpenAI( + api_key = 'd04d386a-7c67-4927-8251-171a236583a6', + base_url = "https://ark.cn-beijing.volces.com/api/v3", +) +def encode_image(image_path): + with open(image_path, "rb") as image_file: + return base64.b64encode(image_file.read()).decode('utf-8') +# 将图片转为Base64编码 + +# 需要传给大模型的图片 +image_path = "ch.jpg" +base64_image = encode_image(image_path) +# Image input: +response = client.chat.completions.create( + model="doubao-1-5-vision-pro-32k-250115", + messages=[ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "图中是几个语文题目,帮我分析一下,我是否做对了", + }, + { + "type": "image_url", + "image_url": { + # 需要注意:传入Base64编码前需要增加前缀 data:image/{图片格式};base64,{Base64编码}: + # PNG图片:"url": f"data:image/png;base64,{base64_image}" + # JEPG图片:"url": f"data:image/jpeg;base64,{base64_image}" + # WEBP图片:"url": f"data:image/webp;base64,{base64_image}" + "url": f"data:image/jpg;base64,{base64_image}" + }, + }, + ], + } + ], +) + +print(response.choices[0].message.content) \ No newline at end of file diff --git a/diagnose/te.py b/diagnose/te.py new file mode 100644 index 0000000..392d4ae --- /dev/null +++ b/diagnose/te.py @@ -0,0 +1,138 @@ +import _thread as thread +import base64 +import datetime +import hashlib +import hmac +import json +from urllib.parse import urlparse +import ssl +from datetime import datetime +from time import mktime +from urllib.parse import urlencode +from wsgiref.handlers import format_date_time +# websocket-client +import websocket + +class Ws_Param(object): + # 初始化 + def __init__(self, APPID, APIKey, APISecret, gpt_url): + self.APPID = APPID + self.APIKey = APIKey + self.APISecret = APISecret + self.host = urlparse(gpt_url).netloc + self.path = urlparse(gpt_url).path + self.gpt_url = gpt_url + + # 生成url + def create_url(self): + # 生成RFC1123格式的时间戳 + now = datetime.now() + date = format_date_time(mktime(now.timetuple())) + + # 拼接字符串 + signature_origin = "host: " + self.host + "\n" + signature_origin += "date: " + date + "\n" + signature_origin += "GET " + self.path + " HTTP/1.1" + + # 进行hmac-sha256进行加密 + signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'), + digestmod=hashlib.sha256).digest() + + signature_sha_base64 = base64.b64encode(signature_sha).decode(encoding='utf-8') + + authorization_origin = f'api_key="{self.APIKey}", algorithm="hmac-sha256", headers="host date request-line", signature="{signature_sha_base64}"' + + authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8') + + # 将请求的鉴权参数组合为字典 + v = { + "authorization": authorization, + "date": date, + "host": self.host + } + # 拼接鉴权参数,生成url + url = self.gpt_url + '?' + urlencode(v) + # 此处打印出建立连接时候的url,参考本demo的时候可取消上方打印的注释,比对相同参数时生成的url与自己代码生成的url是否一致 + return url + +# 收到websocket错误的处理 +def on_error(ws, error): + print("### error:", error) + +# 收到websocket关闭的处理 +def on_close(ws, status_code, reason): + print("") + +# 收到websocket连接建立的处理 +def on_open(ws): + thread.start_new_thread(run, (ws,)) + +def run(ws, *args): + data = json.dumps(gen_params(appid=ws.appid, question=ws.question)) + ws.send(data) + +# 收到websocket消息的处理 +def on_message(ws, message): + # print(message) + data = json.loads(message) + code = data['header']['code'] + if code != 0: + print(f'请求错误: {code}, {data}') + ws.close() + else: + choices = data["payload"]["choices"] + status = choices["status"] + content = choices["text"][0]["content"] + print(content, end='') + if status == 2: + ws.close() + +def gen_params(appid, question): + """ + 通过appid和用户的提问来生成请参数 + """ + data = { + "header": { + "app_id": appid, + "uid": "1234" + }, + "parameter": { + "chat": { + "domain": "general", + "random_threshold": 0.5, + "max_tokens": 4096, + "auditing": "default" + } + }, + "payload": { + "message": { + "text": [ + {"role": "user", "content": question} + ] + } + } + } + return data + +def main(appid, api_key, api_secret, gpt_url, question): + wsParam = Ws_Param(appid, api_key, api_secret, gpt_url) + websocket.enableTrace(False) + wsUrl = wsParam.create_url() + + ws = websocket.WebSocketApp(wsUrl, on_message=on_message, on_error=on_error, on_close=on_close, on_open=on_open) + ws.appid = appid + ws.question = question + ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE}) + + print("end") + + + +if __name__ == "__main__": + # 测试时候在此处正确填写相关信息即可运行 + main(appid = "0c0a6c2f", + api_secret = "Y2UwZjhiYTRhNDM1NjkwNzg3NmEzMTA3", + api_key = "42e836c7c40e1ff9dacac9de6b689bb1", + gpt_url = "ws://spark-api.xf-yun.com/v3.5/chat", + question = '帮我生成一份高三年级英语期中考试试卷,要求包括选择题、填空题、完形填空题、阅读理解题和写作题,每道题都有答案和解析。请直接输出试卷内容,不要输出其他信息。' + ) \ No newline at end of file diff --git a/diagnose/upload_views.py b/diagnose/upload_views.py new file mode 100644 index 0000000..beef3df --- /dev/null +++ b/diagnose/upload_views.py @@ -0,0 +1,36 @@ + + +from rest_framework.views import APIView +from rest_framework.permissions import IsAuthenticated +from authentication.authentication import CustomTokenAuthentication +import uuid +import os +from django.conf import settings +from django.http import JsonResponse +from rest_framework import status + +class MyProtectedUploadfile(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def post(self, request): + user = request.user + uuid_str = str(uuid.uuid4()) + save_file_path = os.path.join(settings.BASE_DIR, 'upload_file', user.username, uuid_str) + + os.makedirs(os.path.dirname(save_file_path), exist_ok=True) + + # 获取上传的文件 + uploaded_file = request.FILES.get('file') + if not uploaded_file: + return JsonResponse({"error": "No file uploaded."}, status=status.HTTP_400_BAD_REQUEST) + + # 保存文件 + try: + with open(save_file_path, 'wb+') as destination: + for chunk in uploaded_file.chunks(): + destination.write(chunk) + except: + return JsonResponse({"error": "Error saving file."}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + return JsonResponse({"message": "File uploaded successfully."}, status=status.HTTP_200_OK) \ No newline at end of file diff --git a/diagnose/urls.py b/diagnose/urls.py new file mode 100644 index 0000000..c86332a --- /dev/null +++ b/diagnose/urls.py @@ -0,0 +1,11 @@ + +from django.urls import path +from .dp_views import MyProtectedGeneratePaper, MyProtectedGenerateCheck, MyProtectedDownloadPaper +from .upload_views import MyProtectedUploadfile +urlpatterns = [ + path('', MyProtectedGeneratePaper.as_view(), name='api_generate_paper'), + path('check/', MyProtectedGenerateCheck.as_view(), name='api_generate_check'), + path('download/', MyProtectedDownloadPaper.as_view(), name='api_download_paper'), + + path('upload/', MyProtectedUploadfile.as_view(), name='api_upload_file') +] diff --git a/ec_user/__init__.py b/ec_user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ec_user/admin.py b/ec_user/admin.py new file mode 100644 index 0000000..c3a8d4d --- /dev/null +++ b/ec_user/admin.py @@ -0,0 +1,66 @@ +from django.contrib import admin + + +""" +设置后台管理的全局参数 +""" + +admin.site.site_header = '诊断应用后台' +admin.site.site_title = '诊断服务端' +admin.site.index_title = '诊断服务端' + +""" +设置后台管理的全局参数 +结束 +""" +from django.urls import reverse +from django.utils.html import format_html + +# Register your models here. +from . models import EcUser, Subject, SchoolInfo +from django import forms + +class UcUserInfoForm(forms.ModelForm): + class Meta: + model = EcUser + fields = '__all__' + widgets = { + 'subjects': forms.CheckboxSelectMultiple, # 使用复选框小部件 + } + +@admin.register(EcUser) +class UcUserInfo(admin.ModelAdmin): + # 在列表视图中显示哪些字段 + list_display = ('username','id', 'gender', 'dob', 'political_status', 'custom_link') + + # 通过 fieldsets 分组字段并定义显示顺序 + fieldsets = ( + (None, { + 'fields': ('username', 'password') # 先显示用户名和密码 + }), + ('Personal Information', { + 'fields': ('name', 'gender', 'age', 'ethnicity', 'dob', 'id_card_number', 'political_status', 'subjects', 'subject_usage_count') + }), + ) + + # 还可以通过 'search_fields' 添加搜索功能 + search_fields = ('name', 'id_card_number') + + # 可以设置过滤选项,方便后台管理 + list_filter = ('gender', 'political_status') + + # 可选:设置只读字段 + # readonly_fields = ('id_card_number',) # 设置身份证号为只读字段 + form = UcUserInfoForm # 设置使用自定义表单 + + def custom_link(self, obj): + # 为每个用户添加自定义链接 + url = reverse('all_info_form', args=[obj.pk]) # 生成链接 + return format_html('查看', url) # 创建 HTML 链接 + + custom_link.short_description = '详细信息' # 设置列标题 + + + +admin.site.register(Subject) +admin.site.register(SchoolInfo) \ No newline at end of file diff --git a/ec_user/apps.py b/ec_user/apps.py new file mode 100644 index 0000000..bc917e8 --- /dev/null +++ b/ec_user/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class EcUserConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'ec_user' diff --git a/ec_user/forms.py b/ec_user/forms.py new file mode 100644 index 0000000..b072087 --- /dev/null +++ b/ec_user/forms.py @@ -0,0 +1,88 @@ + +from django import forms +from .models import EcUser, ContactInfo, SchoolInfo, AcademicInfo, HealthInfo, SelfEvaluation +from .models import HobbiesInterests, SocialPractice, FamilyInfo, AwardsPunishments + +class BaiscInfoForm(forms.ModelForm): + class Meta: + model = EcUser + fields = ['name', 'gender', 'age', 'ethnicity', 'dob', 'id_card_number', 'political_status'] + +class ContactInfoForm(forms.ModelForm): + class Meta: + model = ContactInfo + fields = ['home_address', 'parent_contact', 'student_contact', 'email'] + +# SchoolInfo 表单 +class SchoolInfoForm(forms.ModelForm): + class Meta: + model = SchoolInfo + fields = ['school_name', 'grade', 'class_name', 'admission_date', 'expected_graduation_date'] + +# AcademicInfo 表单 +class AcademicInfoForm(forms.ModelForm): + class Meta: + model = AcademicInfo + fields = ['last_semester_score', 'this_semester_score', 'class_ranking', 'strong_subject', 'weak_subject'] + +# HealthInfo 表单 +class HealthInfoForm(forms.ModelForm): + class Meta: + model = HealthInfo + fields = ['height', 'weight', 'blood_type', 'medical_history', 'disability_status', 'disability_category', 'disability_grade'] + +# SelfEvaluation 表单 +class SelfEvaluationForm(forms.ModelForm): + class Meta: + model = SelfEvaluation + fields = ['strengths', 'weaknesses', 'study_attitude', 'future_plans'] + +# HobbiesInterests 表单 +class HobbiesInterestsForm(forms.ModelForm): + class Meta: + model = HobbiesInterests + fields = ['interests', 'extracurricular_activities'] + +# SocialPractice 表单 +class SocialPracticeForm(forms.ModelForm): + class Meta: + model = SocialPractice + fields = ['activity_name', 'activity_date', 'activity_location', 'activity_description', 'activity_outcome'] + +# FamilyInfo 表单 +class FamilyInfoForm(forms.ModelForm): + class Meta: + model = FamilyInfo + fields = ['family_member', 'economic_status'] + +# AwardsPunishments 表单 +class AwardsPunishmentsForm(forms.ModelForm): + class Meta: + model = AwardsPunishments + fields = ['award_name', 'award_date', 'award_organization', 'discipline_date', 'discipline_issue', 'discipline_outcome'] + + +# 获取或创建各个信息模型实例 +g_models = { + 'contact_info': ContactInfo, + 'school_info': SchoolInfo, + 'academic_info': AcademicInfo, + 'health_info': HealthInfo, + 'self_evaluation': SelfEvaluation, + 'hobbies_interests': HobbiesInterests, + 'social_practice': SocialPractice, + 'family_info': FamilyInfo, + 'awards_punishments': AwardsPunishments +} + +g_form_classes = { + 'contact_info_form': ContactInfoForm, + 'school_info_form': SchoolInfoForm, + 'academic_info_form': AcademicInfoForm, + 'health_info_form': HealthInfoForm, + 'self_evaluation_form': SelfEvaluationForm, + 'hobbies_interests_form': HobbiesInterestsForm, + 'social_practice_form': SocialPracticeForm, + 'family_info_form': FamilyInfoForm, + 'awards_punishments_form': AwardsPunishmentsForm +} diff --git a/ec_user/migrations/0001_initial.py b/ec_user/migrations/0001_initial.py new file mode 100644 index 0000000..de369ae --- /dev/null +++ b/ec_user/migrations/0001_initial.py @@ -0,0 +1,155 @@ +# Generated by Django 4.2.19 on 2025-02-25 02:55 + +from django.conf import settings +import django.contrib.auth.models +import django.contrib.auth.validators +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='EcUser', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('name', models.CharField(max_length=100)), + ('gender', models.CharField(choices=[('M', 'Male'), ('F', 'Female'), ('O', 'Other')], default='M', max_length=1)), + ('ethnicity', models.CharField(default='Unknown', max_length=50)), + ('dob', models.DateField(blank=True, null=True)), + ('id_card_number', models.CharField(max_length=18, unique=True)), + ('political_status', models.CharField(default='Unknown', max_length=50)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.CreateModel( + name='SocialPractice', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('activity_name', models.CharField(max_length=100)), + ('activity_date', models.DateField()), + ('activity_location', models.CharField(max_length=255)), + ('activity_description', models.TextField()), + ('activity_outcome', models.TextField()), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='SelfEvaluation', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('strengths', models.TextField()), + ('weaknesses', models.TextField()), + ('study_attitude', models.TextField()), + ('future_plans', models.TextField()), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='SchoolInfo', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('school_name', models.CharField(max_length=255)), + ('grade', models.CharField(max_length=50)), + ('class_name', models.CharField(max_length=50)), + ('admission_date', models.DateField()), + ('expected_graduation_date', models.DateField()), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='HobbiesInterests', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('interests', models.TextField()), + ('extracurricular_activities', models.TextField()), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='HealthInfo', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('height', models.DecimalField(decimal_places=2, max_digits=5)), + ('weight', models.DecimalField(decimal_places=2, max_digits=5)), + ('blood_type', models.CharField(max_length=3)), + ('medical_history', models.TextField(blank=True, null=True)), + ('disability_status', models.BooleanField()), + ('disability_category', models.CharField(blank=True, max_length=50, null=True)), + ('disability_grade', models.CharField(blank=True, max_length=50, null=True)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='FamilyInfo', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('family_member', models.JSONField()), + ('economic_status', models.CharField(max_length=50)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='ContactInfo', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('home_address', models.CharField(max_length=255)), + ('parent_contact', models.CharField(max_length=15)), + ('student_contact', models.CharField(max_length=15)), + ('email', models.EmailField(max_length=254)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='AwardsPunishments', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('award_name', models.CharField(max_length=100)), + ('award_date', models.DateField()), + ('award_organization', models.CharField(max_length=100)), + ('discipline_date', models.DateField(blank=True, null=True)), + ('discipline_issue', models.CharField(blank=True, max_length=255, null=True)), + ('discipline_outcome', models.CharField(blank=True, max_length=255, null=True)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='AcademicInfo', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('last_semester_score', models.DecimalField(decimal_places=2, max_digits=5)), + ('this_semester_score', models.DecimalField(decimal_places=2, max_digits=5)), + ('class_ranking', models.IntegerField()), + ('strong_subject', models.CharField(max_length=50)), + ('weak_subject', models.CharField(max_length=50)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/ec_user/migrations/0002_alter_socialpractice_user.py b/ec_user/migrations/0002_alter_socialpractice_user.py new file mode 100644 index 0000000..804906b --- /dev/null +++ b/ec_user/migrations/0002_alter_socialpractice_user.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.19 on 2025-02-25 03:28 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('ec_user', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='socialpractice', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/ec_user/migrations/0003_alter_awardspunishments_user_alter_familyinfo_user_and_more.py b/ec_user/migrations/0003_alter_awardspunishments_user_alter_familyinfo_user_and_more.py new file mode 100644 index 0000000..e37e268 --- /dev/null +++ b/ec_user/migrations/0003_alter_awardspunishments_user_alter_familyinfo_user_and_more.py @@ -0,0 +1,40 @@ +# Generated by Django 4.2.19 on 2025-02-25 04:22 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('ec_user', '0002_alter_socialpractice_user'), + ] + + operations = [ + migrations.AlterField( + model_name='awardspunishments', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='familyinfo', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='hobbiesinterests', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='schoolinfo', + name='admission_date', + field=models.DateField(blank=True, null=True), + ), + migrations.AlterField( + model_name='schoolinfo', + name='expected_graduation_date', + field=models.DateField(blank=True, null=True), + ), + ] diff --git a/ec_user/migrations/0004_alter_academicinfo_last_semester_score_and_more.py b/ec_user/migrations/0004_alter_academicinfo_last_semester_score_and_more.py new file mode 100644 index 0000000..0646ea2 --- /dev/null +++ b/ec_user/migrations/0004_alter_academicinfo_last_semester_score_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.19 on 2025-02-25 04:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ec_user', '0003_alter_awardspunishments_user_alter_familyinfo_user_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='academicinfo', + name='last_semester_score', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True), + ), + migrations.AlterField( + model_name='academicinfo', + name='this_semester_score', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True), + ), + ] diff --git a/ec_user/migrations/0005_alter_academicinfo_class_ranking.py b/ec_user/migrations/0005_alter_academicinfo_class_ranking.py new file mode 100644 index 0000000..de85dad --- /dev/null +++ b/ec_user/migrations/0005_alter_academicinfo_class_ranking.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.19 on 2025-02-25 04:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ec_user', '0004_alter_academicinfo_last_semester_score_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='academicinfo', + name='class_ranking', + field=models.IntegerField(blank=True, null=True), + ), + ] diff --git a/ec_user/migrations/0006_alter_healthinfo_height_alter_healthinfo_weight.py b/ec_user/migrations/0006_alter_healthinfo_height_alter_healthinfo_weight.py new file mode 100644 index 0000000..fdaee10 --- /dev/null +++ b/ec_user/migrations/0006_alter_healthinfo_height_alter_healthinfo_weight.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.19 on 2025-02-25 04:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ec_user', '0005_alter_academicinfo_class_ranking'), + ] + + operations = [ + migrations.AlterField( + model_name='healthinfo', + name='height', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True), + ), + migrations.AlterField( + model_name='healthinfo', + name='weight', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True), + ), + ] diff --git a/ec_user/migrations/0007_alter_healthinfo_disability_status.py b/ec_user/migrations/0007_alter_healthinfo_disability_status.py new file mode 100644 index 0000000..2428558 --- /dev/null +++ b/ec_user/migrations/0007_alter_healthinfo_disability_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.19 on 2025-02-25 04:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ec_user', '0006_alter_healthinfo_height_alter_healthinfo_weight'), + ] + + operations = [ + migrations.AlterField( + model_name='healthinfo', + name='disability_status', + field=models.BooleanField(blank=True, null=True), + ), + ] diff --git a/ec_user/migrations/0008_alter_familyinfo_family_member.py b/ec_user/migrations/0008_alter_familyinfo_family_member.py new file mode 100644 index 0000000..7c1a049 --- /dev/null +++ b/ec_user/migrations/0008_alter_familyinfo_family_member.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.19 on 2025-02-25 05:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ec_user', '0007_alter_healthinfo_disability_status'), + ] + + operations = [ + migrations.AlterField( + model_name='familyinfo', + name='family_member', + field=models.JSONField(blank=True, null=True), + ), + ] diff --git a/ec_user/migrations/0009_alter_academicinfo_last_semester_score_and_more.py b/ec_user/migrations/0009_alter_academicinfo_last_semester_score_and_more.py new file mode 100644 index 0000000..a84c5e4 --- /dev/null +++ b/ec_user/migrations/0009_alter_academicinfo_last_semester_score_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.19 on 2025-02-25 14:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ec_user', '0008_alter_familyinfo_family_member'), + ] + + operations = [ + migrations.AlterField( + model_name='academicinfo', + name='last_semester_score', + field=models.JSONField(blank=True, null=True), + ), + migrations.AlterField( + model_name='academicinfo', + name='this_semester_score', + field=models.JSONField(blank=True, null=True), + ), + ] diff --git a/ec_user/migrations/0010_subject_ecuser_subjects.py b/ec_user/migrations/0010_subject_ecuser_subjects.py new file mode 100644 index 0000000..91dc5f4 --- /dev/null +++ b/ec_user/migrations/0010_subject_ecuser_subjects.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.19 on 2025-02-26 03:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ec_user', '0009_alter_academicinfo_last_semester_score_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='Subject', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ], + ), + migrations.AddField( + model_name='ecuser', + name='subjects', + field=models.ManyToManyField(blank=True, related_name='users', to='ec_user.subject'), + ), + ] diff --git a/ec_user/migrations/0011_ecuser_subject_usage_count.py b/ec_user/migrations/0011_ecuser_subject_usage_count.py new file mode 100644 index 0000000..5f1c350 --- /dev/null +++ b/ec_user/migrations/0011_ecuser_subject_usage_count.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.19 on 2025-02-26 04:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ec_user', '0010_subject_ecuser_subjects'), + ] + + operations = [ + migrations.AddField( + model_name='ecuser', + name='subject_usage_count', + field=models.IntegerField(default=0), + ), + ] diff --git a/ec_user/migrations/0012_ecuser_age.py b/ec_user/migrations/0012_ecuser_age.py new file mode 100644 index 0000000..c13c69a --- /dev/null +++ b/ec_user/migrations/0012_ecuser_age.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.19 on 2025-02-27 11:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ec_user', '0011_ecuser_subject_usage_count'), + ] + + operations = [ + migrations.AddField( + model_name='ecuser', + name='age', + field=models.IntegerField(blank=True, null=True), + ), + ] diff --git a/ec_user/migrations/__init__.py b/ec_user/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ec_user/models.py b/ec_user/models.py new file mode 100644 index 0000000..e56339c --- /dev/null +++ b/ec_user/models.py @@ -0,0 +1,244 @@ +from django.db import models + +# Create your models here. + +from django.contrib.auth.models import AbstractUser +from django.db import models + + +""" +1. UserProfile(用户扩展信息表)继承AbstractUser +name: 姓名 +gender: 性别 +ethnicity: 民族 +dob: 出生日期 +id_card_number: 身份证号码 +political_status: 政治面貌 + +2. ContactInfo(联系方式表) +user: 外键关联到 UserProfile 表。 +home_address: 家庭住址 +parent_contact: 家长联系电话 +student_contact: 本人联系电话 +email: 电子邮箱 + +3. SchoolInfo(就读信息表) +user: 外键关联到 UserProfile 表。 +school_name: 学校名称 +grade: 年级 +class: 班级 +admission_date: 入学时间 +expected_graduation_date: 预计毕业时间 + +4. AcademicInfo(学习情况表) +user: 外键关联到 UserProfile 表。 +last_semester_score: 上学期期末成绩 +this_semester_score: 本学期期中成绩 +class_ranking: 班级排名 +strong_subject: 特长学科 +weak_subject: 薄弱学科 + +5. FamilyInfo(家庭情况表) +user: 外键关联到 UserProfile 表。 +family_member: 家庭成员信息(可以是多个外键或 JSON 字段来存储) +economic_status: 家庭经济状况(是否低保、是否建档立卡等) + +6. AwardsPunishments(奖惩情况表) +user: 外键关联到 UserProfile 表。 +award_name: 奖项名称 +award_date: 奖项颁发时间 +award_organization: 奖项颁发单位 +discipline_date: 违纪时间 +discipline_issue: 违纪事由 +discipline_outcome: 处理结果 + +7. HealthInfo(健康状况表) +user: 外键关联到 UserProfile 表。 +height: 身高 +weight: 体重 +blood_type: 血型 +medical_history: 既往病史(重大疾病、过敏史等) +disability_status: 是否残疾 +disability_category: 残疾类别 +disability_grade: 残疾等级 + +8. HobbiesInterests(兴趣爱好表) +user: 外键关联到 UserProfile 表。 +interests: 兴趣爱好(如阅读、绘画等) +extracurricular_activities: 参加的社团或课外兴趣班 + +9. SocialPractice(社会实践表) +user: 外键关联到 UserProfile 表。 +activity_name: 活动名称 +activity_date: 活动时间 +activity_location: 活动地点 +activity_description: 活动内容 +activity_outcome: 活动收获 + +10. SelfEvaluation(自我评价表) +user: 外键关联到 UserProfile 表。 +strengths: 优点 +weaknesses: 不足 +study_attitude: 学习态度 +future_plans: 未来规划 +""" + + +class Subject(models.Model): + name = models.CharField(max_length=100) # 科目名称 + + def __str__(self): + return self.name + + +# EcUser(用户表) +class EcUser(AbstractUser): + GENDER_CHOICES = [ + ('M', 'Male'), + ('F', 'Female'), + ('O', 'Other'), + ] + + name = models.CharField(max_length=100) + gender = models.CharField(max_length=1, choices=GENDER_CHOICES, default='M') # 默认值设置为'M' + age = models.IntegerField(null=True, blank=True) # 允许为空,可以为null + ethnicity = models.CharField(max_length=50, default='Unknown') # 默认值设置为'Unknown' + dob = models.DateField(null=True, blank=True) # 允许为空,可以为null + id_card_number = models.CharField(max_length=18, unique=True) + political_status = models.CharField(max_length=50, default='Unknown') # 默认值设置为'Unknown' + + # 多选字段:开放权限的科目 + subjects = models.ManyToManyField(Subject, related_name='users', blank=True) # 允许为空,表示可以选择多个科目 + # 使用次数限制 + subject_usage_count = models.IntegerField(default=0) + + def __str__(self): + return self.name + +# ContactInfo(联系方式表) +class ContactInfo(models.Model): + user = models.OneToOneField(EcUser, on_delete=models.CASCADE) + home_address = models.CharField(max_length=255) + parent_contact = models.CharField(max_length=15) + student_contact = models.CharField(max_length=15) + email = models.EmailField() + + def __str__(self): + return f"Contact Info of {self.user.name}" + +# SchoolInfo(就读信息表) +class SchoolInfo(models.Model): + user = models.OneToOneField(EcUser, on_delete=models.CASCADE) + school_name = models.CharField(max_length=255) + grade = models.CharField(max_length=50) + class_name = models.CharField(max_length=50) # Changed 'class' to 'class_name' to avoid conflict + admission_date = models.DateField(null=True, blank=True) + expected_graduation_date = models.DateField(null=True, blank=True) + + def __str__(self): + return f"School Info of {self.user.name}" + +# AcademicInfo(学习情况表) +class AcademicInfo(models.Model): + user = models.OneToOneField(EcUser, on_delete=models.CASCADE) + # last_semester_score = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) + # this_semester_score = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) + last_semester_score = models.JSONField(null=True, blank=True) + this_semester_score = models.JSONField(null=True, blank=True) + + class_ranking = models.IntegerField(null=True, blank=True) + strong_subject = models.CharField(max_length=50) + weak_subject = models.CharField(max_length=50) + + def __str__(self): + return f"Academic Info of {self.user.name}" + +# HealthInfo(健康状况表) +class HealthInfo(models.Model): + user = models.OneToOneField(EcUser, on_delete=models.CASCADE) + height = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) + weight = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) + blood_type = models.CharField(max_length=3) + medical_history = models.TextField(null=True, blank=True) + disability_status = models.BooleanField(null=True, blank=True) + disability_category = models.CharField(max_length=50, null=True, blank=True) + disability_grade = models.CharField(max_length=50, null=True, blank=True) + + def __str__(self): + return f"Health Info of {self.user.name}" + +# SelfEvaluation(自我评价表) +class SelfEvaluation(models.Model): + user = models.OneToOneField(EcUser, on_delete=models.CASCADE) + strengths = models.TextField() + weaknesses = models.TextField() + study_attitude = models.TextField() + future_plans = models.TextField() + + def __str__(self): + return f"Self Evaluation of {self.user.name}" + + + +# HobbiesInterests(兴趣爱好表) +class HobbiesInterests(models.Model): + # 定义一个与EcUser一对多关联的外键 + user = models.ForeignKey(EcUser, on_delete=models.CASCADE) + # 定义一个文本字段,用于存储用户的兴趣爱好 + interests = models.TextField() + # 定义一个文本字段,用于存储用户的课外活动 + extracurricular_activities = models.TextField() + + def __str__(self): + # 返回用户的名字和兴趣爱好 + return f"Hobbies and Interests of {self.user.name}" + +# SocialPractice(社会实践表) +class SocialPractice(models.Model): + # 定义一个与EcUser一对多关联的外键 + user = models.ForeignKey(EcUser, on_delete=models.CASCADE) + # 定义一个活动名称的字符型字段,最大长度为100 + activity_name = models.CharField(max_length=100) + # 定义一个活动日期的日期型字段 + activity_date = models.DateField() + # 定义一个活动地点的字符型字段,最大长度为255 + activity_location = models.CharField(max_length=255) + # 定义一个活动描述的文本型字段 + activity_description = models.TextField() + # 定义一个活动结果的文本型字段 + activity_outcome = models.TextField() + + def __str__(self): + # 返回一个字符串,表示该社交实践的用户名称 + return f"Social Practice of {self.user.name}" + +# FamilyInfo(家庭情况表) +class FamilyInfo(models.Model): + # 定义一个与EcUser一对多关联的外键 + user = models.ForeignKey(EcUser, on_delete=models.CASCADE) + family_member = models.JSONField(null=True, blank=True) # Store family member info as JSON or use a related model for complex relationships + economic_status = models.CharField(max_length=50) + + def __str__(self): + x = { + "members": [ + {"relationship": "Father", "name": "John", "age": 45, "occupation": "Engineer"}, + {"relationship": "Mother", "name": "Jane", "age": 43, "occupation": "Doctor"}, + {"relationship": "Brother", "name": "Mike", "age": 20, "occupation": "Student"} + ] + } + return f"Family Info of {self.user.name}" + +# AwardsPunishments(奖惩情况表) +class AwardsPunishments(models.Model): + # 定义一个与EcUser一对多关联的外键 + user = models.ForeignKey(EcUser, on_delete=models.CASCADE) + award_name = models.CharField(max_length=100) + award_date = models.DateField() + award_organization = models.CharField(max_length=100) + discipline_date = models.DateField(null=True, blank=True) + discipline_issue = models.CharField(max_length=255, null=True, blank=True) + discipline_outcome = models.CharField(max_length=255, null=True, blank=True) + + def __str__(self): + return f"Awards and Punishments of {self.user.name}" diff --git a/ec_user/templates/all_info_form.html b/ec_user/templates/all_info_form.html new file mode 100644 index 0000000..cdfd3b2 --- /dev/null +++ b/ec_user/templates/all_info_form.html @@ -0,0 +1,296 @@ + + + + + ++ 注意管理员实际不应直接修改用户数据 +
++ 对id为 {{ user.id }} 的用户进行了修改。 + {{ formset.errors }} + 修改了 {{ model_name }} 的信息。 +
diff --git a/ec_user/templates/api_academic_info_test.html b/ec_user/templates/api_academic_info_test.html new file mode 100644 index 0000000..3ceea93 --- /dev/null +++ b/ec_user/templates/api_academic_info_test.html @@ -0,0 +1,129 @@ + + + + + +