当前位置: 首页 > news >正文

(九)学生写作画像可视化

在上次报告中提到的无法正确识别登录状态的问题已经解决,现在调用后端api时可以正确load_user并得到登录状态。
登录状态问题解决后,本次主要实现的是学生写作画像的数据可视化,学生可以登陆后查看自己之前的作文列表与历史各维度得分,获得得分雷达图,进行数据可视化。

一、学生写作中心后端

为了实现学生写作中心,主要是后端新增student_dashboard_routes.py。该文件定义了获取学生文章列表,学生作文分数,老师反馈等api供前端调用。
在get_student_essays中,通过获取currentuserid,进而使用models.py中已经定义的get_writing_history方法来查询每个studentid下所有的essay,并且在essay_list中读取每个essay的id来获取分数并计算平均分。

@student_dashboard.route('/student/essays', methods=['GET'])
@login_required
def get_student_essays():"""获取学生的所有作文列表"""try:user_model = UserModel(mysql)essays = user_model.get_writing_history(current_user.id)essay_list = []for essay in essays:essay_id = int(essay['essay_id'])  # 确保转为整数print(f"当前处理作文ID: {essay_id}")  # 使用f-string            # 获取作文评分profile = user_model.get_writing_profile(current_user.id,essay_id)# 获取教师评语feedback = user_model.get_teacher_feedback(current_user.id)essay_data = {'id': essay['essay_id'],'title': essay.get('title', ''),'createTime': essay['created_at'].isoformat(),'updateTime': essay['created_at'].isoformat(),'score': None,'scores': None,'comments': None}# 添加评分信息if profile:essay_data['score'] = round((profile['grammar_score'] + profile['idea_score'] + profile['structure_score'] + profile['rhetoric_score'] + profile['emotion_score'] + profile['innovation_score']) / 6, 1)essay_data['scores'] = {'grammar': round(profile['grammar_score'], 1),'idea': round(profile['idea_score'], 1),'structure': round(profile['structure_score'], 1),'rhetoric': round(profile['rhetoric_score'], 1),'emotion': round(profile['emotion_score'], 1),'innovation': round(profile['innovation_score'], 1)}# 添加教师评语if feedback:essay_data['comments'] = feedback[0]['feedback_content'] if feedback else Noneessay_list.append(essay_data)return jsonify({'success': True,'data': essay_list})except Exception as e:return jsonify({'success': False,'error': str(e)}), 500

而在调用get_student_essays之后,便可以使用get_essay_detail,通过essay_id来获取更为详细的作文内容,这些返回给前端,前端将这些数据生成雷达图进行可视化。

@student_dashboard.route('/student/essay/<int:essay_id>', methods=['GET'])
@login_required
def get_essay_detail(essay_id):"""获取单篇作文的详细信息"""try:user_model = UserModel(mysql)# 获取作文内容cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)cursor.execute('SELECT * FROM essays WHERE id = %s AND student_id = %s',(essay_id, current_user.id))essay = cursor.fetchone()cursor.close()if not essay:return jsonify({'success': False,'error': '作文不存在或无权访问'}), 404# 获取作文评分profile = user_model.get_writing_profile(current_user.id,essay_id)# 获取教师评语feedback = user_model.get_teacher_feedback(current_user.id)# 获取写作风格特征style = user_model.get_style_features(current_user.id)essay_detail = {'id': essay['id'],'title': essay['title'],'content': essay['content'],'createTime': essay['submission_date'].isoformat(),'updateTime': essay['submission_date'].isoformat(),'score': None,'scores': None,'comments': None,'styleFeatures': None}# 添加评分信息if profile:essay_detail['score'] = round((profile['grammar_score'] + profile['idea_score'] + profile['structure_score'] + profile['rhetoric_score'] + profile['emotion_score'] + profile['innovation_score']) / 6, 1)essay_detail['scores'] = {'grammar': round(profile['grammar_score'], 1),'idea': round(profile['idea_score'], 1),'structure': round(profile['structure_score'], 1),'rhetoric': round(profile['rhetoric_score'], 1),'emotion': round(profile['emotion_score'], 1),'innovation': round(profile['innovation_score'], 1)}# 添加教师评语if feedback:essay_detail['comments'] = feedback[0]['feedback_content'] if feedback else None# 添加写作风格特征if style:essay_detail['styleFeatures'] = {'vocabularyDiversity': style[0]['vocabulary_diversity'],'sentenceComplexity': style[0]['sentence_complexity'],'paragraphCoherence': style[0]['paragraph_coherence'],'argumentPatterns': style[0]['argument_patterns'],'wordChoiceFeatures': style[0]['word_choice_features'],'styleKeywords': style[0]['style_keywords']}return jsonify({'success': True,'data': essay_detail})except Exception as e:return jsonify({'success': False,'error': str(e)}), 500

这样就将之前所用的loginManager结合起来,实现用户登录查询。

二、学生写作中心前端

因为后端整个登录逻辑的更改,为了与后端登录认证相兼容,前端的登陆方法也进行一些改变。
通过在原始前端方法中加入withCredentials: true,使得浏览器能够保存并使用初始登录的session,进而确保之后每次调用api都会使用相同的session,这也也就使得后端能认证登录的状态。

const handleLogin = async () => {try {const response = await axios.post('http://localhost:5000/api/login', form.value, {withCredentials: true  // ✅ 关键设置});if (response.data.status === 'success') {router.push('/student-dashboard');}} catch (err) {error.value = err.response?.data?.message || '登录失败';}
};

前端为了获取文章,调用/api/student/essays,后端成功调用即可返回

async fetchEssays() {this.loading = true;this.error = null;try {const response = await axios.get('/api/student/essays', {withCredentials: true,headers: {'Accept': 'application/json','Content-Type': 'application/json'}});if (response.data.success) {this.essays = response.data.data;if (this.essays.length > 0) {this.selectEssay(this.essays[0]);}}} catch (err) {console.error('获取作文列表失败:', err);this.error = '获取作文列表失败,请稍后重试';} finally {this.loading = false;}},

获取文章数据后可使用getDimensionName,initRadarChart初始化可视化图表

        getDimensionName(dimension) {const dimensionMap = {grammar: '语法',idea: '内容',structure: '结构',rhetoric: '修辞',emotion: '情感',innovation: '创新'};return dimensionMap[dimension] || dimension;},initRadarChart() {if (!this.selectedEssay || !this.selectedEssay.scores) return;if (this.chart) {this.chart.dispose();}const chartDom = this.$refs.radarChart;this.chart = echarts.init(chartDom);const dimensions = Object.keys(this.selectedEssay.scores);const scores = Object.values(this.selectedEssay.scores);const option = {radar: {indicator: dimensions.map(dim => ({name: this.getDimensionName(dim),max: 100})),splitNumber: 4,axisName: {color: '#666'}},series: [{type: 'radar',data: [{value: scores,name: '能力维度',areaStyle: {color: 'rgba(74, 144, 226, 0.3)'},lineStyle: {color: '#4a90e2'},itemStyle: {color: '#4a90e2'}}]}]};this.chart.setOption(option);}},

三、结果展示

在这里插入图片描述

相关文章:

  • 【Unity】相机 Cameras
  • rabbitmq Direct交换机简介
  • React Hooks 与异步数据管理
  • Python-matplotlib中的Pyplot API和面向对象 API
  • SolidWorks建模(U盘)- 多实体建模拆图案例
  • STM32:CAN总线精髓:特性、电路、帧格式与波形分析详解
  • CppCon 2014 学习:Decomposing a Problem for Parallel Execution
  • Docker 安装 Redis 容器
  • 如何使用flask做任务调度
  • 机器学习算法:逻辑回归
  • 分布式锁优化:使用Lua脚本保证释放锁的原子性问题
  • 单元测试-断言常见注解
  • MCP还是A2A?AI未来技术选型深度对比分析报告
  • 解决:install via Git URL失败的问题
  • 电路图识图基础知识-高、低压供配电系统电气系统的继电自动装置(十三)
  • 【华为云Astro Zero】组装设备管理页面开发(图形拖拽 + 脚本绑定)
  • 使用 MCP 将代理连接到 Elasticsearch 并对索引进行查询
  • Kotlin 扩展函数详解
  • 【iOS(swift)笔记-14】App版本不升级时本地数据库sqlite更新逻辑二
  • 【数据分析】第四章 pandas简介(1)
  • 龙胜做网站的公司/长沙百度网站排名优化
  • 重庆网站建设齐重庆零臻科技/如何自创网站
  • 在线下单网站怎么做/官网seo关键词排名系统
  • 网站开发进度报告/深圳品牌seo
  • wordpress实现浮动联系/太原seo排名
  • 中华南大街网站建设/seo网站建设优化