- Published on
当软件解决学生难题:创建课程规划工具
- Authors

- Name
- Jack Qin
作为西澳大利亚大学信息技术硕士的学生,我很快发现了一个影响几乎所有同学的重大挑战:跨多个学期规划课程的复杂且常常令人困惑的过程。这种复杂性源于该项目灵活的结构——提供不同专业方向(AI、软件系统和应用计算)的众多选修课程,每个方向都有自己的先修要求、学期可用性限制和特定的学分要求。
理解问题
选课过程呈现出几个相互关联的挑战,使学生难以优化他们的学习旅程:
学生们在以下基本问题上苦苦挣扎,这些问题显著影响了他们的教育体验:
- "我应该先选哪些课程才能进入我特别感兴趣的高级 AI 课程?"
- "我如何在满足所有核心要求的同时,仍然探索与我职业目标一致的专业主题?"
- "如果我在第一学期选择这些特定课程,我还能在计划的时间内完成学位吗?"
- "转换学分和选修学分之间的最佳平衡是什么,以满足项目要求?"
我亲眼目睹同学们如何求助于临时解决方案——复杂的电子表格、手绘图表,以及频繁与学术顾问的咨询——仅仅是为了规划他们在项目中的路径。这些方法不仅耗时,而且由于忽略了先修要求或误解了要求,常常导致学习计划不够理想。
缺乏专门工具意味着学生们实际上是在单独重复相同的规划过程,导致整个群体的重复努力,并增加了选课错误的可能性。
概念解决方案
在我们的软件工程课程中,我特别被一个核心原则所打动:最有价值的软件解决真实的、定义明确的问题。这一认识激发了后来成为 UWA MIT 学习规划器的想法。
我设想了一个具有以下核心功能的交互式 Web 应用程序:
可视化学期规划:清晰的、按学期的可视化,让学生一目了然地看到整个学位路径。
智能验证:对先修要求的实时反馈,系统自动识别并提醒学生潜在问题。
需求跟踪:持续计算学位要求的完成进度,向学生准确显示他们在核心课程、专业要求和总学分方面的位置。
专业支持:为在 MIT 项目中追求特定方向的学生提供定制指导,包括量身定制的课程推荐。
可分享的学习计划:导出、保存和分享完成的学习计划的能力,便于与顾问和同学讨论。
最初作为解决我自己规划挑战的个人工具,很快就显示出其造福整个学生社区的潜力。我意识到这可以将压力来源转变为一个流畅、直观的过程。
技术挑战和架构决策
将这个概念转变为现实呈现出几个复杂的技术挑战,每个都需要仔细考虑和战略决策。
建模复杂的课程关系
第一个主要障碍是开发一个能够准确表示复杂课程关系网络的数据结构。MIT 项目中的每门课程都有多个影响其在学习计划中位置的属性:
先修要求:一些课程需要完成一个特定的先修课程,而其他课程接受多个替代方案中的一个,创建了复杂的依赖图。
学期可用性:某些课程仅在特定学期(第一或第二学期)提供,这显著限制了规划选项。
学分分类:课程需要被正确分类为核心、转换或选修学分,以准确跟踪学位要求。
学分值:大多数课程有标准学分值,但存在一些会影响总学分计算的变化。
专业相关性:根据学生选择的专业方向,课程具有不同的重要性。
经过多次迭代,我开发了一个全面的数据模型,可以表示这些关系,同时保持足够的性能用于实时验证。这个模型构成了应用程序智能的基础:
interface Course {
id: string
code: string
title: string
credits: number
category: 'Core' | 'Conversion' | 'Option'
prerequisites: Prerequisite[]
semesterAvailability: ('Semester 1' | 'Semester 2')[]
relevantSpecializations: Specialization[]
description: string
}
type Prerequisite = { type: 'course'; courseId: string } | { type: 'or'; options: Prerequisite[] }
这种结构允许我表示复杂的先修场景,如 "CITS5501 OR (CITS4401 AND CITS5502)",这对于准确验证至关重要。
用户界面设计理念
用户体验可能是采用的最关键元素。我的指导原则是界面应该直观到学生可以有效使用它,而不需要说明或文档。
我选择拖放范式作为主要交互方式,因为它与学生在心理上如何处理课程规划非常相似——在学期之间移动课程直到找到最佳安排。这种自然的交互模型显著降低了学习曲线。
对于视觉设计,我选择了 Shadcn/ui 组件和 Tailwind CSS,原因如下:
- 它们提供了与当代 Web 应用程序一致的简洁、现代的美学。
- 它们内置的响应能力确保规划器在各种设备上都能良好运行——从大学图书馆的台式电脑到学生在非正式规划会议期间可能使用的平板电脑和智能手机。
- 基于组件的架构促进了整个应用程序一致的样式和行为。
为了增强用户反馈,我实现了:
- 颜色编码以指示课程类别和验证状态
- 细微的动画以加强用户操作的结果
- 清晰的上下文错误消息,解释为什么某些课程放置是无效的
- 在不混乱主界面的情况下提供额外信息的工具提示
状态管理架构
课程规划的相互关联性质呈现出重大的状态管理挑战。当学生将课程添加到特定学期时,此操作可能影响:
- 其他计划课程的有效性(通过满足先修要求)
- 额外课程的可用性(通过满足先修要求)
- 已完成学分要求的计算
- 呈现给用户的视觉反馈
在最初尝试 React 的 Context API 后,我确定应用程序的复杂性需要更强大的解决方案。我最终选择了 Redux Toolkit,它提供了:
可预测的状态变化:使用具有明确定义的 reducer 的单一事实来源,使应用程序的行为更加一致且更易于调试。
性能优化:Redux 防止不必要重新渲染的能力在应用程序变得更复杂时变得至关重要。
开发工具:Redux DevTools 扩展对于在开发过程中跟踪状态变化和故障排除非常有价值。
中间件支持:这便于实现复杂功能,如撤消/重做功能和持久化。
这种状态管理架构在实现"自动建议"功能时特别有益,该功能分析学生当前的计划,并根据已完成的先修要求和项目要求推荐合乎逻辑的下一门课程。
开发之旅
原型阶段
我从低保真线框图开始验证核心概念,专注于基本用户流程而不是视觉效果。在与几位同学分享这些早期设计后,他们的热情反馈确认了该工具的潜在价值。
使用基本 React 组件和最少样式构建的初始原型展示了核心拖放功能和简单的先修要求检查。即使在这种初级形式下,原型清楚地说明了不同的课程选择将如何影响学生的整体学习计划。
迭代开发过程
我没有尝试一次性构建完整的应用程序,而是采用了增量方法,按优先级顺序添加功能:
- 核心拖放框架:建立基础交互模型
- 基本先修验证:实现防止无效课程选择的基本逻辑
- 学期兼容性检查:确保课程仅放置在实际提供的学期
- 需求跟踪:添加学位要求进度的实时计算
- 专业支持:为不同 MIT 方向实现个性化推荐
- 计划导出和保存:添加保存和分享学习计划的功能
- 视觉改进:通过动画和改进的反馈增强界面
在实现每个主要功能后,我与同学进行了非正式测试会议,收集直接影响后续开发的反馈。这种以用户为中心的方法带来了许多可能被忽视的改进。
技术实现亮点
创建无障碍拖放系统
拖放系统——规划器的核心交互方式——比预期更具挑战性。除了基本功能外,我需要确保:
- 无障碍性:系统需要可通过键盘导航和屏幕阅读器使用
- 触摸支持:许多学生将在平板电脑或触摸屏上访问规划器
- 视觉反馈:用户需要清楚指示拖动操作期间发生了什么
- 拖动时验证:系统应指示潜在的放置位置是否有效
我使用 React DnD 作为基础构建此系统,但需要通过自定义实现来扩展它以满足这些要求:
// 带有验证反馈的自定义拖动预览组件示例
const DragPreview = ({ course, isValid }) => (
<div className={`drag-preview ${isValid ? 'valid' : 'invalid'}`}>
<CourseCard
course={course}
isDragging={true}
validationMessage={!isValid ? '先修要求未满足' : null}
/>
</div>
)
// 与 React DnD 一起使用
const [{ isDragging }, drag, preview] = useDrag({
type: 'COURSE',
item: { id: course.id, type: 'COURSE' },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
})
// 创建自定义拖动预览
useEffect(() => {
if (preview) {
preview(getEmptyImage(), { captureDraggingState: true })
}
}, [preview])
这种方法允许高度自定义的拖动行为和反馈,显著增强了用户体验。
先修验证算法
先修验证系统需要处理几个复杂场景:
- 具有多个替代先修要求的课程
- 可以同时选修的先修课程
- 根据以前的经验可能豁免先修要求的特殊情况
我使用有向图方法实现了这一点,使用邻接列表来建模先修关系。验证算法递归检查先修要求,同时避免可能发生的循环依赖导致的无限循环:
function validatePrerequisites(courseId, plannedCourses, semesterIndex) {
const course = coursesData[courseId]
// 基本情况:课程没有先修要求
if (!course.prerequisites || course.prerequisites.length === 0) {
return true
}
return course.prerequisites.every((prereq) => {
// 处理 OR 条件
if (prereq.type === 'or') {
return prereq.options.some((option) =>
validateSinglePrerequisite(option, plannedCourses, semesterIndex)
)
}
// 处理单个课程先修要求
return validateSinglePrerequisite(prereq, plannedCourses, semesterIndex)
})
}
function validateSinglePrerequisite(prereq, plannedCourses, currentSemester) {
// 检查先修课程是否计划在更早的学期
return plannedCourses.some(
(semester, index) => index < currentSemester && semester.includes(prereq.courseId)
)
}
这个验证系统在学生构建学习计划时提供即时反馈,防止不可能的课程组合,同时解释为什么某些选择是无效的。
响应式设计实现
为了确保规划器在各种设备上都有用,我实现了一个响应式设计,可以智能地适应不同的屏幕尺寸:
- 桌面视图:所有学期以网格布局显示,提供整个学习计划的全面概览
- 平板视图:学期以可滚动的水平布局排列,具有优化的触摸目标
- 移动视图:一次显示一个学期,通过滑动在学期之间导航
这种响应式方法需要仔细管理组件大小和上下文感知的渲染决策。我没有简单地缩小桌面界面,而是为每种设备形态重新构想了交互模型,以确保无论设备如何都能获得最佳体验。
部署和社区采用
经过彻底的测试和改进,我将 UWA MIT 学习规划器部署为 Web 应用程序并与我的课程群体分享。反响超出了我的预期:
- "这个工具刚刚为我节省了几个小时的规划时间,可能还有一次与顾问的会面!"
- "我终于理解了 AI 专业的所有先修要求是如何组合在一起的。"
- "可视化布局使要求比仅阅读手册清晰得多。"
消息在学生社区中迅速传播,几周内,来自不同入学年份的学生都在使用规划器来规划他们的整个学位路径。也许最令人惊讶的是,甚至一些教师顾问开始向新生推荐该工具,将其纳入他们的迎新材料。
通过反馈持续改进
基于持续的用户反馈,我继续用额外功能增强规划器:
- 模板学习计划:常见专业和情况的预配置计划
- 课程描述集成:直接在规划器中提供每门课程的详细信息
- 性能优化:改进大型学习计划的渲染
- 无障碍增强:扩展键盘支持和屏幕阅读器兼容性
每一项改进都直接来自于观察学生如何使用该工具并听取他们的建议。
影响和经验教训
最初作为解决我自己课程规划挑战的个人解决方案,已经发展成为整个 UWA MIT 学生社区的宝贵资源。这段旅程教会了我关于软件开发和问题解决的几个宝贵教训:
从真正的问题开始:最有价值的应用程序解决影响真实人群的真正痛点。通过从我亲身经历并在他人身上观察到的问题开始,我创建了具有内在实用性的东西。
用户反馈是无价的:许多最重要的改进不是来自我最初的愿景,而是来自观察真实用户与应用程序交互并与我未预料到的方面作斗争。
隐藏技术复杂性:系统中最复杂的部分——先修验证、状态管理、数据建模——对用户完全不可见,用户只体验一个简单、直观的界面。
现代 Web 技术支持快速迭代:React、TypeScript 和现代 UI 库的组合使我能够比以前的技术更快地构建和改进复杂的应用程序。
社区需求推动有机采用:不需要营销——该工具自然传播,因为它解决了学生社区的真正需求。
UWA MIT 学习规划器不仅仅是一项技术成就——它展示了软件如何将一个令人沮丧、容易出错的过程转变为简单甚至令人愉快的东西。我继续维护和改进规划器,计划最终将管理权移交给未来的学生,他们可以将项目向前推进,并确保它与 MIT 项目本身一起发展。
技术实现
对于对技术细节感兴趣的人,UWA MIT 学习规划器使用以下技术构建:
- React 18 和 TypeScript 用于类型安全和改进的开发体验
- Redux Toolkit 用于状态管理
- Shadcn/ui 组件 和 Tailwind CSS 用于 UI 设计
- React DnD 用于拖放功能
- Framer Motion 用于细微、有目的的动画
应用程序架构强调:
- 关注点分离,具有明确定义的组件和服务
- 整个过程中使用强类型以在编译时捕获错误
- 响应式设计原则以实现跨设备兼容性
- 无障碍性作为核心要求而不是事后考虑
完整源代码可在 GitHub 上获取,供有兴趣探索实现或为其持续开发做出贡献的人使用。