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 @@ + + + + + + All Information Form + + + + + + {% extends 'admin/base_site.html' %} + + + {% block branding %} +

诊断应用后台

+ {% endblock %} + + + {% block content %} + +
+

填写信息 - {{ user.name }}

+ +
+ +
+

联系方式

+ {{ contact_info_form.as_p }} +
+ + +
+

就读信息

+ {{ school_info_form.as_p }} +
+ + +
+

学习情况

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
生成json字符串:{}
+ + +
+ + +
+ +
+ + +
+ {{ academic_info_form.as_p }} +
+ + +
+

健康状况

+ {{ health_info_form.as_p }} +
+ + +
+

自我评价

+ {{ self_evaluation_form.as_p }} +
+ +
+ +
+ +
+

兴趣爱好

+ {{ hobbies_interests_form.management_form }} + {% for form in hobbies_interests_form %} +
+ {{ form.as_p }} +
+ {% empty %} +

没有兴趣爱好信息。

+ {% endfor %} + +
+
+ +
+ +
+

社会实践

+ {{ social_practice_form.management_form }} + {% for form in social_practice_form %} +
+ {{ form.as_p }} +
+ {% empty %} +

没有社会实践记录。

+ {% endfor %} + +
+
+ +
+ +
+

家庭情况

+ {{ family_info_form.management_form }} + {% for form in family_info_form %} +
+ {{ form.as_p }} +
+ {% empty %} +

没有家庭情况记录。

+ {% endfor %} + +
+
+ +
+ +
+

奖惩情况

+ {{ awards_punishments_form.management_form }} + {% for form in awards_punishments_form %} +
+ {{ form.as_p }} +
+ {% empty %} +

没有奖惩情况记录。

+ {% endfor %} + +
+
+ +
+ + {% endblock %} + + + + + + diff --git a/ec_user/templates/all_info_result.html b/ec_user/templates/all_info_result.html new file mode 100644 index 0000000..5992787 --- /dev/null +++ b/ec_user/templates/all_info_result.html @@ -0,0 +1,9 @@ + +

+ 注意管理员实际不应直接修改用户数据 +

+

+ 对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 @@ + + + + + + Test Academic Info API + + + +

Test Academic Info API

+ +

Get Academic Info

+ + + + +
+ +

Update Academic Info

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +
+ + + + diff --git a/ec_user/templates/api_awards_punishments_test.html b/ec_user/templates/api_awards_punishments_test.html new file mode 100644 index 0000000..fc2de8e --- /dev/null +++ b/ec_user/templates/api_awards_punishments_test.html @@ -0,0 +1,134 @@ + + + + + + Test Awards and Punishments API + + + +

Test Awards and Punishments API

+ +

Get Awards and Punishments

+ + + + +
+ +

Update Awards and Punishments

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +
+ + + + diff --git a/ec_user/templates/api_contact_info_test.html b/ec_user/templates/api_contact_info_test.html new file mode 100644 index 0000000..ed58006 --- /dev/null +++ b/ec_user/templates/api_contact_info_test.html @@ -0,0 +1,124 @@ + + + + + + Test Contact Info API + + + +

Test Contact Info API

+ +

Get Contact Info

+ + + + +
+ +

Update Contact Info

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +
+ + + + diff --git a/ec_user/templates/api_family_info_test.html b/ec_user/templates/api_family_info_test.html new file mode 100644 index 0000000..fd70fe1 --- /dev/null +++ b/ec_user/templates/api_family_info_test.html @@ -0,0 +1,121 @@ + + + + + + Test Family Info API + + + +

Test Family Info API

+ +

Get Family Info

+ + + + +
+ +

Update Family Info

+ + +

+ + +

+ + +

+ + +
+ + + + diff --git a/ec_user/templates/api_health_info_test.html b/ec_user/templates/api_health_info_test.html new file mode 100644 index 0000000..7419d29 --- /dev/null +++ b/ec_user/templates/api_health_info_test.html @@ -0,0 +1,139 @@ + + + + + + Test Health Info API + + + +

Test Health Info API

+ +

Get Health Info

+ + + + +
+ +

Update Health Info

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +
+ + + + diff --git a/ec_user/templates/api_hobbies_interests_test.html b/ec_user/templates/api_hobbies_interests_test.html new file mode 100644 index 0000000..d741c45 --- /dev/null +++ b/ec_user/templates/api_hobbies_interests_test.html @@ -0,0 +1,118 @@ + + + + + + Test Hobbies and Interests API + + + +

Test Hobbies and Interests API

+ +

Get Hobbies and Interests

+ + + + +
+ +

Add Hobbies and Interests

+ + +

+ + +

+ + +

+ + +
+ + + + diff --git a/ec_user/templates/api_school_info_test.html b/ec_user/templates/api_school_info_test.html new file mode 100644 index 0000000..3a20611 --- /dev/null +++ b/ec_user/templates/api_school_info_test.html @@ -0,0 +1,129 @@ + + + + + + Test School Info API + + + +

Test School Info API

+ +

Get School Info

+ + + + +
+ +

Update School Info

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +
+ + + + diff --git a/ec_user/templates/api_self_evaluation_test.html b/ec_user/templates/api_self_evaluation_test.html new file mode 100644 index 0000000..26c7238 --- /dev/null +++ b/ec_user/templates/api_self_evaluation_test.html @@ -0,0 +1,124 @@ + + + + + + Test Self Evaluation API + + + +

Test Self Evaluation API

+ +

Get Self Evaluation

+ + + + +
+ +

Update Self Evaluation

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +
+ + + + diff --git a/ec_user/templates/api_social_practice_test.html b/ec_user/templates/api_social_practice_test.html new file mode 100644 index 0000000..4fd89f7 --- /dev/null +++ b/ec_user/templates/api_social_practice_test.html @@ -0,0 +1,133 @@ + + + + + + Test Social Practice API + + + +

Test Social Practice API

+ +

Get Social Practice Records

+ + + + +
+ +

Add Social Practice Record

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +
+ + + + diff --git a/ec_user/templates/index.html b/ec_user/templates/index.html new file mode 100644 index 0000000..fae8a9d --- /dev/null +++ b/ec_user/templates/index.html @@ -0,0 +1,12 @@ + + + 联系方式 + 就读信息 + 学习情况 + 健康状况 + 自我评价 + 兴趣爱好 + 社会实践 + 家庭状况 + 奖惩情况 + 信息总览 diff --git a/ec_user/templates/testlogin.html b/ec_user/templates/testlogin.html new file mode 100644 index 0000000..40719ea --- /dev/null +++ b/ec_user/templates/testlogin.html @@ -0,0 +1,56 @@ + + + + + + Login Test + + + + + + + +

Login

+
+
+
+
+

+ +
+ + diff --git a/ec_user/tests.py b/ec_user/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/ec_user/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/ec_user/urls.py b/ec_user/urls.py new file mode 100644 index 0000000..cc718ec --- /dev/null +++ b/ec_user/urls.py @@ -0,0 +1,58 @@ + + +from django.urls import path +from . views import ec_login, MyProtectedUserLogout, index_test + +from . views import MyProtectedApiContactInfo, api_contact_info_test, MyProtectedApiSchoolInfo, api_school_info_test +from . views import MyProtectedApiAcademicInfo, api_academic_info_test, MyProtectedApiHealthInfo, api_health_info_test +from . views import MyProtectedApiSelfEvaluationInfo, api_self_evaluation_test, MyProtectedFamilyInfo, api_family_info_test +from . views import MyProtectedApiHobbiesInterests, api_hobbies_interests_test, MyProtectedSocialPractice, api_social_practice_test +from . views import MyProtectedAwardsPunishments, api_awards_punishments_test , MyProtectedBasicUserInfo + +from . views import all_info_form, all_info_edit + +from . views import MyProtectedUserInfo + +urlpatterns = [ + path('', index_test, name='index_test'), + + path('login/', ec_login, name='login'), + path('logout/', MyProtectedUserLogout.as_view(), name='logout'), + + + path('contact_info/api/', MyProtectedApiContactInfo.as_view(), name='api_contact_info'), + path('api_contact_info_test/', api_contact_info_test, name='api_contact_info_test'), + + path('school_info/api/', MyProtectedApiSchoolInfo.as_view(), name='api_school_info'), + path('api_school_info_test/', api_school_info_test, name='api_school_info_test'), + + path('academic_info/api/', MyProtectedApiAcademicInfo.as_view(), name='api_academic_info'), + path('api_academic_info_test/', api_academic_info_test, name='api_academic_info_test'), + + path('health_info/api/', MyProtectedApiHealthInfo.as_view(), name='api_health_info'), + path('api_health_info_test/', api_health_info_test, name='api_health_info_test'), + + path('self_evaluation/api/', MyProtectedApiSelfEvaluationInfo.as_view(), name='api_self_evaluation'), + path('api_self_evaluation_test/', api_self_evaluation_test, name='api_self_evaluation_test'), + + + path('hobbies_interests/api/', MyProtectedApiHobbiesInterests.as_view(), name='api_hobbies_interests'), + path('api_hobbies_interests_test/', api_hobbies_interests_test, name='api_hobbies_interests_test'), + + path('social_practice/api/', MyProtectedSocialPractice.as_view(), name='api_social_practice'), + path('api_social_practice_test/', api_social_practice_test, name='api_social_practice_test'), + + path('family_info/api/', MyProtectedFamilyInfo.as_view(), name='api_family_info'), + path('api_family_info_test/', api_family_info_test, name='api_family_info_test'), + + path('awards_punishments/api/', MyProtectedAwardsPunishments.as_view(), name='api_awards_punishments'), + path('api_awards_punishments_test/', api_awards_punishments_test, name='api_awards_punishments_test'), + + + path('all_info_form//', all_info_form, name='all_info_form'), + path('all_info_edit///', all_info_edit, name='all_info_edit'), + + path('api_user_info/', MyProtectedUserInfo.as_view(), name='api_user_info'), + path('basic_user_info/api/', MyProtectedBasicUserInfo.as_view(), name='api_basic_user_info'), + +] diff --git a/ec_user/views.py b/ec_user/views.py new file mode 100644 index 0000000..cd3cc21 --- /dev/null +++ b/ec_user/views.py @@ -0,0 +1,659 @@ +from django.http import JsonResponse, HttpResponse +from django.contrib.auth import authenticate, login, logout +from django.shortcuts import render, redirect +from django.forms import modelformset_factory +from rest_framework.authtoken.models import Token # 导入 Token 模型 +import json +from datetime import timedelta +from django.utils import timezone +from authentication.models import CustomToken +from django.contrib.auth.hashers import check_password, make_password # 导入密码哈希比较方法 + + +from .models import EcUser, ContactInfo, SchoolInfo, AcademicInfo, HealthInfo, SelfEvaluation +from .models import HobbiesInterests, SocialPractice, FamilyInfo, AwardsPunishments + +from .forms import g_models, g_form_classes + +from rest_framework.views import APIView +from rest_framework.permissions import IsAuthenticated +from authentication.authentication import CustomTokenAuthentication + + + +def index_test(request): + return render(request, 'index.html') + + +def ec_login(request): + if request.method != 'POST': + return render(request, 'testlogin.html') + + try: + data = json.loads(request.body) + username = data.get('username') + password = data.get('password') + except json.JSONDecodeError: + return JsonResponse({'error': 'Invalid JSON'}, status=400) + + if not username or not password: + return JsonResponse({'error': 'Missing username or password'}, status=400) + + user = authenticate(username=username, password=password) + if user is not None: + login(request, user) + + # 生成 Token(你可以选择使用自定义的 CustomToken) + # token, created = Token.objects.get_or_create(user=user) # 使用 DRF 提供的 Token 模型 + # 如果你使用的是自定义的 CustomToken + token, created = CustomToken.objects.get_or_create(user=user, defaults={'extra_info': 'your_value', 'expiration_date': timezone.now() + timedelta(days=7)}) + if not created: + token.expiration_date = timezone.now() + timedelta(days=7) + token.save() + + return JsonResponse({'message': 'Login Success!', 'token': token.key}) # 返回 token + + else: + return JsonResponse({'error': 'Login Failed!'}, status=401) + +class MyProtectedUserLogout(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def get(self, request): + user = request.user + if user.is_authenticated: + # 删除与当前用户相关的 Token + CustomToken.objects.filter(user=user).delete() + + # 进行登出操作 + logout(request) + + return JsonResponse({'message': 'Logout Successful'}, status=200) + else: + return JsonResponse({'error': 'User not authenticated'}, status=400) + + + +def all_info_form(request, user_id): + try: + user = EcUser.objects.get(id=user_id) + except EcUser.DoesNotExist: + return render(request, 'error.html', {"message": "User not found"}) + + instances = {} + for key, model in g_models.items(): + if key in ['hobbies_interests', 'social_practice', 'family_info', 'awards_punishments']: + instances[key] = model.objects.filter(user=user) # 处理一对多关系 + else: + instance, _ = model.objects.get_or_create(user=user) + instances[key] = instance + + if request.method == 'GET': + # 初始化所有表单 + forms = {} + for form_key, form_class in g_form_classes.items(): + if form_key in ['hobbies_interests_form', 'social_practice_form', 'family_info_form', 'awards_punishments_form']: + queryset = instances[form_key[:-5]] # 获取对应模型的查询集 + + # 如果查询集为空,设置 extra=1 来允许添加空表单 + if queryset.exists(): + formset_class = modelformset_factory(queryset.model, form=form_class, extra=0) + else: + formset_class = modelformset_factory(queryset.model, form=form_class, extra=1) + + # 使用 formset 渲染表单 + forms[form_key] = formset_class(queryset=queryset) + else: + forms[form_key] = form_class(instance=instances[form_key[:-5]]) + + return render(request, 'all_info_form.html', {'user': user, **forms}) + + else: + return HttpResponse("Not implemented") + + +def all_info_edit(request, user_id, model_name): + + try: + user = EcUser.objects.get(id=user_id) + except EcUser.DoesNotExist: + return render(request, 'error.html', {"message": "User not found"}) + + instances = g_models[model_name].objects.filter(user=user) # 处理一对多关系 + print(instances) + formset_class = modelformset_factory(model=g_models[model_name], form=g_form_classes[model_name + '_form'], extra=0) + formset = formset_class(request.POST, queryset=instances) + print(instances) + if formset.is_valid(): + instances = formset.save(commit=False) + instances[0].user = user + formset.save() + else: + return render(request, 'all_info_result.html', {'user': user, "model_name": model_name, "formset": formset}) + + return render(request, 'all_info_result.html', {'user': user, "model_name": model_name, "formset": None}) + + +# ================= API 接口 ================= +# +++++++++++++++++++++++++++++++++++++++++++= + + +class MyProtectedUserInfo(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def get(self, request): + # 返回用户的 ID、用户名、剩余次数和主题列表 + return JsonResponse({"user_id": request.user.id, + "name": request.user.name, + "username": request.user.username, + "remain_count": request.user.subject_usage_count, + "subject": list(request.user.subjects.values_list('name', flat=True)), + "fill_status": + { + "contact_info": ContactInfo.objects.filter(user=request.user).exists(), + "scholar_info": SchoolInfo.objects.filter(user=request.user).exists(), + "academic_info": AcademicInfo.objects.filter(user=request.user).exists(), + "health_info": HealthInfo.objects.filter(user=request.user).exists(), + "self_evaluation": SelfEvaluation.objects.filter(user=request.user).exists(), + "family_info": FamilyInfo.objects.filter(user=request.user).exists(), + "hobbies_interests": HobbiesInterests.objects.filter(user=request.user).exists(), + "social_practice": SocialPractice.objects.filter(user=request.user).exists(), + "awards_punishments": AwardsPunishments.objects.filter(user=request.user).exists(), + } + }) + + + +class MyProtectedBasicUserInfo(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def get(self, request): + user = request.user + # 获取用户信息并返回 JSON 格式 + user_info = EcUser.objects.filter(id=user.id).first() + if user_info: + response_data = { + "name": user_info.name, + "gender": user_info.get_gender_display(), + "age": user_info.age, + "ethnicity": user_info.ethnicity, + "dob": user_info.dob, + "id_card_number": user_info.id_card_number, + "political_status": user_info.political_status + } + return JsonResponse(response_data) + else: + return JsonResponse({"error": "No user info found"}, status=404) + + def post(self, request): + user = request.user + # 处理 POST 请求,更新用户信息 + try: + data = json.loads(request.body) + name = data.get("name") + gender = data.get("gender") + age = data.get("age") + ethnicity = data.get("ethnicity") + dob = data.get("dob") + id_card_number = data.get("id_card_number") + political_status = data.get("political_status") + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON format"}, status=400) + + # 查找或创建 EcUser + try: + user_info = EcUser.objects.get(id=user.id) + except EcUser.DoesNotExist: + return JsonResponse({"error": "User not found"}, status=404) + + # 更新用户信息 + user_info.name = name + user_info.gender = gender + user_info.age = age + user_info.ethnicity = ethnicity + user_info.dob = dob + user_info.id_card_number = id_card_number + user_info.political_status = political_status + user_info.save() + + return JsonResponse({"message": "User info updated successfully"}) + + +def api_contact_info_test(req): + return render(req, 'api_contact_info_test.html') + +class MyProtectedApiContactInfo(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def get(self, request): + user = request.user + # 获取联系信息并返回 JSON 格式 + contact_info = ContactInfo.objects.filter(user=user).first() + if contact_info: + response_data = { + "home_address": contact_info.home_address, + "parent_contact": contact_info.parent_contact, + "student_contact": contact_info.student_contact, + "email": contact_info.email + } + return JsonResponse(response_data) + else: + return JsonResponse({"error": "No contact info found"}, status=404) + + def post(self, request): + user = request.user + # 处理 POST 请求,更新用户的联系信息 + try: + data = json.loads(request.body) + home_address = data.get("home_address") + parent_contact = data.get("parent_contact") + student_contact = data.get("student_contact") + email = data.get("email") + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON format"}, status=400) + + # 查找或创建 ContactInfo + contact_info, created = ContactInfo.objects.get_or_create(user=user) + contact_info.home_address = home_address + contact_info.parent_contact = parent_contact + contact_info.student_contact = student_contact + contact_info.email = email + contact_info.save() + + return JsonResponse({"message": "Contact info updated successfully"}) + +def api_school_info_test(req): + return render(req, 'api_school_info_test.html') + +class MyProtectedApiSchoolInfo(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def get(self, request): + user = request.user + # 获取学校信息并返回 JSON 格式 + school_info = SchoolInfo.objects.filter(user=user).first() + if school_info: + response_data = { + "school_name": school_info.school_name, + "grade": school_info.grade, + "class_name": school_info.class_name, + "admission_date": school_info.admission_date, + "expected_graduation_date": school_info.expected_graduation_date + } + return JsonResponse(response_data) + else: + return JsonResponse({"error": "No school info found"}, status=404) + def post(self, request): + user = request.user + # 处理 POST 请求,更新学校信息 + try: + data = json.loads(request.body) + school_name = data.get("school_name") + grade = data.get("grade") + class_name = data.get("class_name") + admission_date = data.get("admission_date") + expected_graduation_date = data.get("expected_graduation_date") + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON format"}, status=400) + + # 查找或创建 SchoolInfo + school_info, created = SchoolInfo.objects.get_or_create(user=user) + school_info.school_name = school_name + school_info.grade = grade + school_info.class_name = class_name + school_info.admission_date = admission_date + school_info.expected_graduation_date = expected_graduation_date + school_info.save() + + return JsonResponse({"message": "School info updated successfully"}) + + + +def api_academic_info_test(req): + return render(req, 'api_academic_info_test.html') + + +class MyProtectedApiAcademicInfo(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def get(self, request): + user = request.user + + # 获取学习情况信息并返回 JSON 格式 + academic_info = AcademicInfo.objects.filter(user=user).first() + if academic_info: + response_data = { + "last_semester_score": str(academic_info.last_semester_score), + "this_semester_score": str(academic_info.this_semester_score), + "class_ranking": academic_info.class_ranking, + "strong_subject": academic_info.strong_subject, + "weak_subject": academic_info.weak_subject + } + return JsonResponse(response_data) + else: + return JsonResponse({"error": "No academic info found"}, status=404) + + def post(self, request): + user = request.user + # 处理 POST 请求,更新用户的学习情况信息 + try: + data = json.loads(request.body) + last_semester_score = data.get("last_semester_score") + this_semester_score = data.get("this_semester_score") + class_ranking = data.get("class_ranking") + strong_subject = data.get("strong_subject") + weak_subject = data.get("weak_subject") + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON format"}, status=400) + + # 查找或创建 AcademicInfo + academic_info, created = AcademicInfo.objects.get_or_create(user=user) + academic_info.last_semester_score = last_semester_score + academic_info.this_semester_score = this_semester_score + academic_info.class_ranking = class_ranking + academic_info.strong_subject = strong_subject + academic_info.weak_subject = weak_subject + academic_info.save() + + return JsonResponse({"message": "Academic info updated successfully"}) + + + +def api_health_info_test(req): + return render(req, 'api_health_info_test.html') + + +class MyProtectedApiHealthInfo(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def get(self, request): + user = request.user + + # 获取健康信息并返回 JSON 格式 + health_info = HealthInfo.objects.filter(user=user).first() + if health_info: + response_data = { + "height": str(health_info.height), + "weight": str(health_info.weight), + "blood_type": health_info.blood_type, + "medical_history": health_info.medical_history, + "disability_status": health_info.disability_status, + "disability_category": health_info.disability_category, + "disability_grade": health_info.disability_grade + } + return JsonResponse(response_data) + else: + return JsonResponse({"error": "No health info found"}, status=404) + + def post(self, request): + + user = request.user + # 处理 POST 请求,更新用户的健康信息 + try: + data = json.loads(request.body) + height = data.get("height") + weight = data.get("weight") + blood_type = data.get("blood_type") + medical_history = data.get("medical_history") + disability_status = data.get("disability_status") + disability_category = data.get("disability_category") + disability_grade = data.get("disability_grade") + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON format"}, status=400) + + # 查找或创建 HealthInfo + health_info, created = HealthInfo.objects.get_or_create(user=user) + health_info.height = height + health_info.weight = weight + health_info.blood_type = blood_type + health_info.medical_history = medical_history + health_info.disability_status = disability_status + health_info.disability_category = disability_category + health_info.disability_grade = disability_grade + health_info.save() + + return JsonResponse({"message": "Health info updated successfully"}) + + +def api_self_evaluation_test(req): + return render(req, 'api_self_evaluation_test.html') + +class MyProtectedApiSelfEvaluationInfo(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def get(self, request): + user = request.user + + # 获取自我评价信息并返回 JSON 格式 + self_evaluation = SelfEvaluation.objects.filter(user=user).first() + if self_evaluation: + response_data = { + "strengths": self_evaluation.strengths, + "weaknesses": self_evaluation.weaknesses, + "study_attitude": self_evaluation.study_attitude, + "future_plans": self_evaluation.future_plans + } + return JsonResponse(response_data) + else: + return JsonResponse({"error": "No self evaluation found"}, status=404) + + def post(self, request): + user = request.user + + # 处理 POST 请求,更新用户的自我评价信息 + try: + data = json.loads(request.body) + strengths = data.get("strengths") + weaknesses = data.get("weaknesses") + study_attitude = data.get("study_attitude") + future_plans = data.get("future_plans") + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON format"}, status=400) + + # 查找或创建 SelfEvaluation + self_evaluation, created = SelfEvaluation.objects.get_or_create(user=user) + self_evaluation.strengths = strengths + self_evaluation.weaknesses = weaknesses + self_evaluation.study_attitude = study_attitude + self_evaluation.future_plans = future_plans + self_evaluation.save() + + return JsonResponse({"message": "Self evaluation updated successfully"}) + + +# =================== 以下字段是 一对多 =================== +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +def api_hobbies_interests_test(req): + return render(req, 'api_hobbies_interests_test.html') + +class MyProtectedApiHobbiesInterests(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + def get(self, request): + user = request.user + + # 获取该用户的所有兴趣爱好信息 + hobbies_interests = HobbiesInterests.objects.filter(user=user) + if hobbies_interests.exists(): + response_data = [] + for entry in hobbies_interests: + response_data.append({ + "interests": entry.interests, + "extracurricular_activities": entry.extracurricular_activities + }) + return JsonResponse(response_data, safe=False) + else: + return JsonResponse({"error": "No hobbies and interests found"}, status=404) + + def post(self, request): + user = request.user + # 处理 POST 请求,创建新的兴趣爱好记录 + try: + data = json.loads(request.body) + interests = data.get("interests") + extracurricular_activities = data.get("extracurricular_activities") + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON format"}, status=400) + + # 创建新的 HobbiesInterests 记录 + new_entry = HobbiesInterests.objects.create( + user=user, + interests=interests, + extracurricular_activities=extracurricular_activities + ) + return JsonResponse({"message": "Hobbies and interests added successfully"}) + + +def api_social_practice_test(req): + return render(req, 'api_social_practice_test.html') + +class MyProtectedSocialPractice(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def get(self, request): + user = request.user + + # 获取该用户的所有社会实践记录 + social_practices = SocialPractice.objects.filter(user=user) + if social_practices.exists(): + response_data = [] + for entry in social_practices: + response_data.append({ + "activity_name": entry.activity_name, + "activity_date": entry.activity_date, + "activity_location": entry.activity_location, + "activity_description": entry.activity_description, + "activity_outcome": entry.activity_outcome + }) + return JsonResponse(response_data, safe=False) + else: + return JsonResponse({"error": "No social practice records found"}, status=404) + + def post(self, request): + user = request.user + # 处理 POST 请求,创建新的社会实践记录 + try: + data = json.loads(request.body) + activity_name = data.get("activity_name") + activity_date = data.get("activity_date") + activity_location = data.get("activity_location") + activity_description = data.get("activity_description") + activity_outcome = data.get("activity_outcome") + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON format"}, status=400) + + # 创建新的 SocialPractice 记录 + new_entry = SocialPractice.objects.create( + user=user, + activity_name=activity_name, + activity_date=activity_date, + activity_location=activity_location, + activity_description=activity_description, + activity_outcome=activity_outcome + ) + return JsonResponse({"message": "Social practice record added successfully"}) + + +def api_family_info_test(req): + return render(req, 'api_family_info_test.html') + +class MyProtectedFamilyInfo(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def get(self, request): + user = request.user + + # 获取该用户的家庭情况记录 + family_info = FamilyInfo.objects.filter(user=user).first() + if family_info: + response_data = { + "family_member": family_info.family_member, + "economic_status": family_info.economic_status + } + return JsonResponse(response_data) + else: + return JsonResponse({"error": "No family info found"}, status=404) + + def post(self, request): + user = request.user + # 处理 POST 请求,创建或更新家庭情况记录 + try: + data = json.loads(request.body) + family_member = data.get("family_member") + economic_status = data.get("economic_status") + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON format"}, status=400) + + # 创建或更新 FamilyInfo 记录 + family_info, created = FamilyInfo.objects.get_or_create(user=user) + family_info.family_member = family_member + family_info.economic_status = economic_status + family_info.save() + + return JsonResponse({"message": "Family info updated successfully"}) + + +def api_awards_punishments_test(req): + return render(req, 'api_awards_punishments_test.html') + +class MyProtectedAwardsPunishments(APIView): + authentication_classes = [CustomTokenAuthentication] # 使用自定义的 Token 认证 + permission_classes = [IsAuthenticated] # 需要用户认证才能访问 + + def get(self, request): + user = request.user + + # 获取该用户的奖惩情况记录 + awards_punishments = AwardsPunishments.objects.filter(user=user) + if awards_punishments.exists(): + response_data = [ + { + "award_name": ap.award_name, + "award_date": ap.award_date, + "award_organization": ap.award_organization, + "discipline_date": ap.discipline_date, + "discipline_issue": ap.discipline_issue, + "discipline_outcome": ap.discipline_outcome + } for ap in awards_punishments + ] + return JsonResponse(response_data, safe=False) + else: + return JsonResponse({"error": "No awards or punishments found"}, status=404) + + def post(self, request): + user = request.user + # 处理 POST 请求,创建或更新奖惩情况记录 + try: + data = json.loads(request.body) + award_name = data.get("award_name") + award_date = data.get("award_date") + award_organization = data.get("award_organization") + discipline_date = data.get("discipline_date") + discipline_issue = data.get("discipline_issue") + discipline_outcome = data.get("discipline_outcome") + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON format"}, status=400) + + # 创建或更新 AwardsPunishments 记录 + awards_punishment = AwardsPunishments.objects.create( + user=user, + award_name=award_name, + award_date=award_date, + award_organization=award_organization, + discipline_date=discipline_date, + discipline_issue=discipline_issue, + discipline_outcome=discipline_outcome + ) + + return JsonResponse({"message": "Awards and punishments info updated successfully"}) diff --git a/educheck/__init__.py b/educheck/__init__.py new file mode 100644 index 0000000..7962f87 --- /dev/null +++ b/educheck/__init__.py @@ -0,0 +1,8 @@ + + +from __future__ import absolute_import, unicode_literals + +# 在 Django 启动时加载 Celery +from .celery import app as celery_app + +__all__ = ('celery_app',) diff --git a/educheck/asgi.py b/educheck/asgi.py new file mode 100644 index 0000000..52ae578 --- /dev/null +++ b/educheck/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for educheck 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/4.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'educheck.settings') + +application = get_asgi_application() diff --git a/educheck/celery.py b/educheck/celery.py new file mode 100644 index 0000000..a207493 --- /dev/null +++ b/educheck/celery.py @@ -0,0 +1,19 @@ +from __future__ import absolute_import, unicode_literals +import os +from celery import Celery + +# 设置 Django 的 settings 模块 +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'educheck.settings') + +# 创建 Celery 应用 +app = Celery('educheck') + +# 使用 Django 配置中的 Celery 配置 +app.config_from_object('django.conf:settings', namespace='CELERY') + +# celery.py +app.conf.broker_connection_retry_on_startup = True + + +# 自动发现所有的 tasks.py 文件 +app.autodiscover_tasks() diff --git a/educheck/settings.py b/educheck/settings.py new file mode 100644 index 0000000..a682123 --- /dev/null +++ b/educheck/settings.py @@ -0,0 +1,184 @@ +""" +Django settings for educheck project. + +Generated by 'django-admin startproject' using Django 4.2.19. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.2/ref/settings/ +""" + +from pathlib import Path + +# __init__.py + +import pymysql +pymysql.install_as_MySQLdb() + + +# 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/4.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-x!)cojyro)mcft%4r^8&wsn9f8o!5px1=@6mx4eu73ydczx&)j' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ['*', 'a.debin.cc'] + +# Application definition +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'ec_user', + 'authentication', + 'diagnose' +] + +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', +] + +ROOT_URLCONF = 'educheck.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 = 'educheck.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.2/ref/settings/#databases + +# DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.sqlite3', +# 'NAME': BASE_DIR / 'db.sqlite3', +# } +# } + +# settings.py + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', # 使用 MySQL 数据库 + 'NAME': 'educheck', # 数据库名称 + 'USER': 'root', # 数据库用户名 + 'PASSWORD': 'pacpac123.', # 数据库密码 + 'HOST': '127.0.0.1', # 数据库主机(如果数据库在本地就用 localhost) + 'PORT': '3306', # MySQL 默认端口是 3306 + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.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/4.2/topics/i18n/ + +LANGUAGE_CODE = 'zh-hans' + +TIME_ZONE = 'Asia/Shanghai' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.2/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + + +AUTH_USER_MODEL = 'ec_user.EcUser' + + +REST_FRAMEWORK = { + 'DEFAULT_RENDERER_CLASSES': [ + 'rest_framework.renderers.JSONRenderer', # 只返回 JSON 格式的响应 + ], + 'DEFAULT_AUTHENTICATION_CLASSES': [ + 'authentication.authentication.CustomTokenAuthentication', + ], +} + +ERROR_CODE_MAP = { + # 1000 - 用户相关错误 + + # 2000 - 生成试卷相关错误 + '2001': 'user not authorized to generate paper for this subject', + '2002': 'user has no remaining usage count for this subject', + '2003': 'user has no school info', + # 试卷查看 + '2004': 'record file not found', + # 试卷下载 + '2005': 'requested file not found' +} + + +OPAI_API_KEY = 'e119bd6c-a22a-404e-a002-6d72a2cea65d' +OPAI_BASE_URL = 'https://ark.cn-beijing.volces.com/api/v3' + +WKHTMLTOPDF_PATH = r'C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe' + +# settings.py +# celery -A educheck worker -l info -P eventlet +# celery -A educheck beat -l info + +CELERY_BROKER_URL = 'redis://127.0.0.1:6379/0' # 使用 Redis 作为消息中间件 +CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/0' # Redis 存储任务结果 +CELERY_ACCEPT_CONTENT = ['json'] +CELERY_TASK_SERIALIZER = 'json' diff --git a/educheck/urls.py b/educheck/urls.py new file mode 100644 index 0000000..4baff1b --- /dev/null +++ b/educheck/urls.py @@ -0,0 +1,34 @@ +""" +URL configuration for educheck project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path +from django.urls import include +from .views import user_profile +from .views import health_check +from .views import error_code_map + +urlpatterns = [ + path('admin/', admin.site.urls), + path('ec_user/', include('ec_user.urls')), + path('diagnose/', include('diagnose.urls')), + + path('health/', health_check, name='health_check'), + path('error_code_map/', error_code_map, name='error_code_map'), + + path('user_profile//', user_profile, name='user_profile'), + path('auth/', include('authentication.urls')), +] diff --git a/educheck/views.py b/educheck/views.py new file mode 100644 index 0000000..60311ab --- /dev/null +++ b/educheck/views.py @@ -0,0 +1,15 @@ +# views.py +from django.shortcuts import render, get_object_or_404 +from ec_user.models import EcUser +from django.http import JsonResponse +from django.conf import settings + +def health_check(request): + return JsonResponse({"status": "ok", "version": "0.0.1"}) + +def error_code_map(request): + return JsonResponse(settings.ERROR_CODE_MAP) + +def user_profile(request, user_id): + user = get_object_or_404(EcUser, id=user_id) + return render(request, 'user_profile.html', {'user': user}) diff --git a/educheck/wsgi.py b/educheck/wsgi.py new file mode 100644 index 0000000..0b79d61 --- /dev/null +++ b/educheck/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for educheck 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/4.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'educheck.settings') + +application = get_wsgi_application() diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..299e402 --- /dev/null +++ b/manage.py @@ -0,0 +1,28 @@ +#!/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', 'educheck.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() + + +# from django.contrib.auth.hashers import make_password + +# celery -A educheck worker -l info -P eventlet +# celery -A educheck beat -l info \ No newline at end of file diff --git a/test_api.py b/test_api.py new file mode 100644 index 0000000..9c8ba51 --- /dev/null +++ b/test_api.py @@ -0,0 +1,18 @@ + + +import requests + +# 设置请求头 +headers = { + 'token': 'Token 486ef8198bb10ba6878eba95771c064ff64db81a' # 替换 your_token_here 为实际的 token +} + +# 发送 GET 请求 +url = 'http://a.debin.cc:55800/auth/' # 目标 URL +response = requests.get(url, headers=headers) + +# 输出响应 +if response.status_code == 200: + print("Response JSON:", response.json()) # 输出响应的 JSON 数据 +else: + print(f"Request failed with status code {response.status_code}")