第三流项目:以数据为中心的开发 - 代码研究所。
该项目是使用 Flask Microframework 构建的,它可以用作手动秒表,为多名运动员的游泳和田径比赛计时。该应用程序的目标是通过减少单独计时的人数并直接存储时间(而不是保留书面文档)来提高运动计时的效率。
此应用程序仅供教育用途。此应用程序不可用于商业用途。
可以在此处找到该项目的现场演示。这个应用程序托管在 Heroku 上。时间存储在 MongoDB 中。
这个计时助手应用程序的想法来自于我童年时接触游泳的经历。我注意到需要有很多人参与计时过程,因为池中的触摸板由于各种原因并不总是能产生准确或可读的时间。此外,教练和运动员直到比赛结束后才能看到最后的时间或分段,除非有多个人在甲板上为每条泳道计时,并将时间写在剪贴板上。
我看到了这个机会,创建了一个应用程序,可以减少参与计时过程的人员数量,希望提高比赛效率以及对运动员和教练的反馈。该应用程序可以针对任何计时运动进行优化,并且该版本适用于游泳和田径运动。
教练和计时员都可以选择运动项目、比赛、赛事、预赛和泳道号码,这些都有助于他们跟踪比赛中发生的情况。它还允许他们在比赛结束后向运动员提供即时反馈,因为时间会在您保存它们然后点击“查看时间”后显示。我还想确保他们能够看到会议的累积结果,而不是一次只能看到一项活动。
此设计没有使用主题,之所以选择现代设计,是因为页面上有大量计时器和数据,可能会显得混乱且无组织。我不想要这个,所以虽然我想要在一个页面上显示计时器、时间和事件选项,但我认为将页面垂直分成三部分是实现此目的的最佳方法,在每个三分之一之间产生明显的差异。
Timing Assistant 是使用 Python 中的 Flask Microframework 构建的,后端有 NoSQL (MongoDB) 数据库,前端有 HTML、CSS、Javascript 和 jQuery,并且用于秒表功能,通过 AJAX 连接到后端。
在应用程序的主页上,用户输入他们想要参加的运动、球队名称、用户名和比赛名称。此操作会将他们带到秒表页面,然后他们可以在其中选择他们想要计时的赛事和热度。用户最多可以为三个车道计时,并为所有三个车道进行分段。在所有三个小计时器停止后,用户必须手动停止主计时器,因为它们是单独设置的。然而,如果教练想知道比赛持续了多长时间并比较游泳运动员的时间与最终完赛者的时间,这可能特别有用。主定时器的时间不会保存到数据库中。
一旦他们点击“提交”,事件和热度就会出现。然后,他们可以通过按主秒表上的“开始”开始最多三个车道的计时。这将启动所有四个秒表,但是,只有最后三个会保存在数据库中。可以选择使用“SPLIT”按钮单独收集每个车道的分割时间。每个计时器都可以单独停止和启动,主计时器控制所有秒表(启动、停止和重置)。
通过单击“保存时间”,将使用 Javascript 文件中的 AJAX 调用将时间推送到 Flask 来保存时间,同时 Flask 连接到 MongoDB。保存时间后,通过单击“查看时间”,时间将显示在秒表下方。
数据也将在没有指定赛事或预赛的情况下保存(查看时间时这些字段将为空白)。如果教练想在练习中而不是在比赛中使用秒表,这可能会很有用。
我希望教练能够有机会下载 PDF 或其他文件格式的数据。从长远来看,这将使他们能够保留所有手动计时记录,而不必依赖手写数据或每次想回去查看旧比赛时都依赖该网站。
我还想让教练根据每场预赛的运动员人数来选择他们想要查看的秒表数量。目前,您一次只能为 3 名运动员计时,并且不能选择为少于 3 名的运动员计时。
我还想实现“练习模式”和“比赛模式”,以便为比赛和练习提供更复杂的计时。比赛模式将对选择赛事或预赛产生更多限制,并允许教练选择他们想要参加多少条泳道。练习模式将允许教练记录他们节省的时间(针对特定的训练等),而不必指定赛事或预赛。
该项目的所有测试都是手动完成的。登陆页面上的表单在输入标签上具有必需的属性,以防止用户不填写表单中的字段,因为这将导致 400 错误,因为 Flask 应用程序路由依赖于这些输入。
通过控制台测试 Ajax 功能和“保存时间”按钮,并验证数据在 MongoDB 中的显示格式是否正确。还测试了从计时器收集的数据和预期的数据结构。
每个通道一个文档单独节省时间:
同一文档中显示泳道的时间(注意:出于测试目的,此处仅保存了两个泳道,以确保两个泳道会显示在同一文档中。):
正确的数据结构:
对秒表的测试也是手动完成的,以确保主重置按钮重置秒表并清除所有计时器的分段,而每个单独的秒表仅清除自己的时间和分段。此外,还针对启动/停止功能进行了测试,因为主秒表控制所有秒表,而各个秒表应仅控制自己的启动/停止功能。
所有 Flask 路径也都经过测试,以确保所有链接都有效,并且可以处理输入中的任何不常见值,并且可以通过 HTML 文件中的 Jinja 正确显示输入。
在测试过程中,我意识到两个用户可能具有相同的会议名称或相同的俱乐部名称,因此使用户有可能遇到其他人的数据。因此,要查看模板中的时间,我添加了以下内容,以确保登录页面上的三个输入字段必须匹配才能显示相应的时间。这需要正确的团队名称、用户名和会议名称进行匹配,以防止交叉保存或交叉查看时间。
{% if time.team == team %}
{% if time.username == username %}
{% if time.meet == meets %}
每个车道的分割最初以以下格式显示:
split: ["00:02.2300:01.45"]
但是,我希望它们出现在这样的列表中:
split: ["00:02.23", "00:01.45"]
因此,我必须实现一种列表理解,以每 9 个字符将该字符串分成列表中的多个字符串(如果对一条通道进行多次分割)。
在 HTML timer_page.html 中,为 AJAX 函数发送数据的表单看起来有一个杂散结束标记,但是,需要包含所有最终时间、分割和车道数据,以便将时间保存到 MongoDB 。由于需要显示样式和其他元素,表单看起来确实与其他元素不符。另外,查看 HTML 有空标签,但是,这是使用 jQuery 将分割时间插入到 HTML 中的地方。
保存时间时,如果为每个计时器选择相同的泳道(例如泳道 1),则查看时间中只会显示泳道 1 时间之一。但是,您必须选择一条通道,因为由于数据结构的性质,如果没有它,您将无法查看保存的时间。车道下拉列表默认设置为保存在车道 1、区域 2 和区域 3,以防用户未指定车道。我希望实现一个验证,如果用户在预赛中为两个泳道选择相同的泳道编号,将禁止节省时间。
运行秒表的 Javascript 函数是根据 Sara 的针对此应用程序的秒表编码教程进行修改的。一些 HTML 也是按照她的示例建模的,但进行了修改以适应样式、多个按钮、分割和通道。
对于 JavaScript,修改了重置按钮的重置功能,以重置所有秒表而不是刷新页面,并且删除了每个小秒表的单独重置按钮,因为它对于该项目的用户体验来说是不必要的功能。还添加了拆分功能。使用 jQuery 修改开始/停止函数以更改按钮的样式。为用户体验添加了一个主秒表,因此教练可以看到总时间以及个人时间,类似于游泳记分牌。使用 Ajax 添加了用于将值传递到 Flask 和 MongoDB 的“保存”按钮。
Ajax 函数是根据 Stack Overflow 上的这篇文章建模的,并通过查看其他 Ajax 使用和语法的模式进行了修改以适应该项目。添加了 PreventDefault 以防止在进行 AJAX 调用时重新加载页面。
Jinja 中的递归用于迭代 Python 中的嵌套字典,通过确保所有通道都循环并显示来正确渲染时间和满足数据。遵循 Stack Overflow 中的这种方法作为指导,并根据我的数据结构的性质进行修改。
尝试重构秒表的 javascript 函数,以便用户可以根据所选车道的数量决定在屏幕上显示多少个秒表。
var stopwatches = [ ] ;
var i ;
for ( i = 0 ; i <= 1 ; i ++ ) {
var stopwatch = new timing ( "timerLabel" + i , "start" + i , "splitLabel" + i ) ;
stopwatches . push ( stopwatch ) ;
console . log ( i ) ;
document . getElementById ( "start" + i ) . onclick = function ( ) {
stopwatches [ i ] . start ( ) ;
}
document . getElementById ( "reset" + i ) . onclick = function ( ) {
stopwatches [ i ] . reset ( ) ;
}
document . getElementById ( "split" + i ) . onclick = function ( ) {
stopwatches [ i ] . split ( ) ;
}
console . log ( stopwatches ) ;
}
当尝试在这样的 for 循环中写入这些内容时,stopwatches[i].start() 不会将i
读取为可以更改的变量,但是,当它被硬编码时,就没有问题了:
document . getElementById ( "reset" + i ) . onclick = function ( ) {
stopwatches [ 0 ] . reset ( ) ;
}
document . getElementById ( "split" + i ) . onclick = function ( ) {
stopwatches [ 0 ] . split ( ) ;
}
我尝试以不同的方式处理它,其中涉及 if 语句来显示相应的秒表。
尝试将i
作为从上面的 for 循环获取的参数传递给函数,但没有成功:
function chooseNumberOfStopwatches ( i ) {
if ( i == 1 ) {
stopwatches_one . start ( ) ;
}
else if ( i == 2 ) {
stopwatches_one . start ( ) ;
stopwatches_two . start ( ) ;
} else {
console . log ( 'else' ) ;
}
}
如果您有兴趣克隆此存储库,请在终端中运行以下命令来设置和安装requirements.txt中的所有内容:
$ sudo pip3 -r install requirements.txt
请注意,我在这个项目中使用了 Cloud9,因此如果您使用不同的编辑器,终端命令可能会有所不同。请参阅您所使用的编辑器的文档,以获取有关特定于编辑器的终端命令的更多信息。 MongoDB 的所有密钥都需要单独获取,因为它们是隐藏的并且是特定于我的。