Projeto Stream Three: Desenvolvimento Centrado em Dados - Code Institute.
Este projeto foi construído usando o Flask Microframework e pode ser usado como um cronômetro manual para cronometrar vários atletas na natação e atletismo. O objetivo desta aplicação é melhorar a eficiência na cronometragem esportiva, diminuindo o número de pessoas cronometrando separadamente e armazenando diretamente os tempos, em vez de manter uma documentação escrita.
ESTE APLICATIVO É APENAS PARA USO EDUCACIONAL. ESTE APLICATIVO NÃO É PARA USO COMERCIAL.
Uma demonstração ao vivo deste projeto pode ser encontrada aqui. Este aplicativo está hospedado no Heroku. Os horários são armazenados no MongoDB.
A ideia para este aplicativo Timing Assistant veio da minha exposição à natação durante minha infância. Percebi que era necessário que várias pessoas estivessem envolvidas no processo de cronometragem, já que os touchpads do pool nem sempre produzem tempos precisos ou legíveis por vários motivos. Além disso, o treinador e os atletas não podem ver os tempos finais ou parciais até o final da competição, a menos que haja várias pessoas no convés para cada raia anotando os tempos em uma prancheta.
Vi essa oportunidade de criar um aplicativo que diminuísse o número de pessoas envolvidas no processo de cronometragem, na esperança de melhorar a eficiência das competições e o feedback aos atletas e treinadores. Esta aplicação pode ser otimizada para qualquer esporte cronometrado, e esta versão funciona tanto com natação quanto com pista.
Treinadores e cronometristas podem escolher um esporte, competição, evento, bateria e números de pista, o que os ajuda a acompanhar o que está acontecendo em uma competição. Também permite que eles forneçam feedback instantâneo aos atletas após uma corrida, já que os tempos aparecem depois que você os salva e clica em 'visualizar tempos'. Eu também queria ter certeza de que eles conseguiriam ver os resultados cumulativos do encontro, em vez de apenas um evento por vez.
Nenhum tema foi utilizado para este design, o design moderno foi escolhido porque com muitos temporizadores e dados em uma página, ela pode parecer confusa e desorganizada. Eu não queria isso, então, embora quisesse os temporizadores, horários e opções de eventos em uma página, pensei que dividir a página em terços verticalmente seria a melhor maneira de fazer isso, criando uma diferença notável entre cada terço.
O Timing Assistant foi construído usando o Flask Microframework em Python com banco de dados NoSQL (MongoDB) no backend, com HTML, CSS, Javascript e jQuery no frontend e para a funcionalidade de cronômetro, conectado ao backend com AJAX.
Na página inicial do aplicativo, o usuário insere o esporte que deseja cronometrar, o nome do time, o nome de usuário e o nome do encontro. Esta ação os leva à página do cronômetro, onde podem escolher o evento e a bateria que desejam cronometrar. O usuário pode cronometrar até três pistas e fazer divisões para todas as três pistas. O usuário terá que parar o temporizador principal manualmente depois que todos os três temporizadores pequenos forem parados, pois eles são configurados individualmente. Isto poderia ser particularmente útil, no entanto, se o treinador quisesse saber quanto tempo durou a corrida e comparar os tempos dos nadadores com os tempos do finalista. A hora do cronômetro principal não será salva no banco de dados.
Assim que clicarem em "enviar", o evento e a bateria aparecerão. Eles podem então começar a cronometrar até três pistas pressionando 'START' no cronômetro principal. Isto iniciará todos os quatro cronômetros, porém, apenas os três últimos serão salvos no banco de dados. Existe a opção de coletar tempos parciais para cada pista individualmente com o botão 'SPLIT'. Cada cronômetro pode ser parado e iniciado individualmente, com o cronômetro principal controlando todos os cronômetros (iniciar, parar e reiniciar).
Ao clicar em 'SALVAR TEMPOS', os tempos serão salvos usando a chamada AJAX no arquivo Javascript para enviar os tempos para o Flask, com o Flask conectado ao MongoDB. Uma vez salvos os tempos, clicando em 'VER TEMPOS', os tempos aparecerão abaixo dos cronômetros.
Os dados também serão salvos sem um evento ou bateria especificada (esses campos ficarão em branco quando os tempos forem visualizados). Isso pode ser útil se um treinador quiser usar os cronômetros no treino e não em uma competição.
Gostaria de poder dar aos treinadores a oportunidade de baixar os dados em PDF ou outro formato de arquivo. No longo prazo, isso permitiria que eles mantivessem todos os tempos manuais em seus registros, sem ter que depender de dados manuscritos ou deste site toda vez que quisessem voltar e ver encontros antigos.
Gostaria também de permitir que o treinador selecione o número de cronômetros que deseja visualizar com base no número de atletas em cada bateria. Atualmente, você só pode cronometrar 3 atletas por vez e não pode optar por cronometrar menos de 3.
Eu também gostaria de implementar um 'modo de prática' e um 'modo de encontro' que permitiriam um timing mais sofisticado para encontros e práticas. O modo Meet criaria mais restrições na escolha de um evento ou bateria e permitiria ao treinador escolher para quantas pistas gostaria de cronometrar. O modo de treino permitiria ao treinador fazer anotações sobre os tempos que está salvando (para um exercício específico, etc.), sem precisar especificar um evento ou bateria.
Todos os testes para este projeto foram feitos manualmente. O formulário da landing page possui atributos obrigatórios nas tags de entrada para evitar que o usuário não preencha algum campo do formulário, pois isso resultará em um erro 400, já que a rota do aplicativo Flask depende dessas entradas.
A função Ajax e o botão Save Times foram testados via console e verificando se os dados apareceram formatados corretamente no MongoDB. Os dados coletados dos temporizadores e a estrutura de dados pretendida também foram testados.
Economia de tempo individualmente com um documento por via:
Tempos para exibição de pistas no mesmo documento (nota: para fins de teste, apenas duas pistas foram salvas aqui para garantir que duas apareceriam no mesmo documento):
Estrutura de dados correta:
O teste dos cronômetros também foi feito manualmente para garantir que o botão de reinicialização principal zerasse o cronômetro e apagasse os parciais de todos os cronômetros, enquanto cada cronômetro individual apagava apenas seu próprio tempo e parciais. Além disso, isso também foi testado para a função start/stop, já que o cronômetro principal controla todos os cronômetros, enquanto os individuais devem controlar apenas suas próprias funções start/stop.
Todos os caminhos do Flask também foram testados para garantir que todos os links funcionassem e que pudessem lidar com quaisquer valores incomuns na entrada, e exibiriam as entradas corretamente via Jinja no arquivo HTML.
Durante o processo de teste, percebi que seria possível que dois usuários tivessem o mesmo nome de encontro ou o mesmo nome de clube, possibilitando assim que o usuário encontrasse os dados de outra pessoa. Portanto, para visualizar os horários no modelo, incluí o seguinte para garantir que três campos de entrada na página de destino devem corresponder para exibir os horários correspondentes. Isso requer que o nome da equipe, o nome de usuário e o nome do encontro corretos correspondam para evitar o salvamento ou visualização cruzada de tempos.
{% if time.team == team %}
{% if time.username == username %}
{% if time.meet == meets %}
As divisões para cada pista apareciam originalmente no seguinte formato:
split: ["00:02.2300:01.45"]
No entanto, eu queria que eles aparecessem em uma lista como esta:
split: ["00:02.23", "00:01.45"]
Então eu tive que implementar uma compreensão de lista para separar essa string em várias strings em uma lista (se mais de uma divisão fosse feita para uma pista) a cada 9 caracteres.
No HTML timer_page.html, o formulário que está enviando dados para a função AJAX parece ter uma tag de final perdida, mas é necessário abranger todos os tempos finais, divisão e dados de pista para salvar os tempos no MongoDB . Devido ao estilo e outros elementos que precisam ser exibidos, parece que o formulário está fora de ordem com os outros elementos. Além disso, olhando para o HTML existem tags vazias, porém é aqui que os tempos parciais são inseridos no HTML usando jQuery.
Ao salvar os tempos, se você escolher a mesma pista para cada cronômetro (pista 1, por exemplo), apenas um dos tempos da pista 1 aparecerá nos tempos de visualização. Porém, você deve escolher uma via, pois não é possível visualizar os tempos salvos sem ela devido à natureza da estrutura de dados. O menu suspenso da pista está configurado para salvar na pista 1, terreno 2 e terreno 3 por padrão, caso o usuário não especifique uma pista. Espero implementar uma validação que proíba a economia de tempos se o usuário escolher o mesmo número de pista para duas pistas em uma bateria.
As funções Javascript que executam o cronômetro são modificadas no tutorial do cronômetro Codificando com Sara para este aplicativo. Parte do HTML também foi modelado a partir do exemplo dela, mas modificado para se adequar ao estilo, vários botões, divisões e faixas.
Para o JavaScript, as funções de reset foram modificadas para o botão de reset para zerar todos os cronômetros em vez de atualizar a página, e os botões de reset individuais para cada cronômetro pequeno foram removidos por ser um recurso desnecessário para a UX deste projeto. Funções de divisão também foram adicionadas. As funções Start/Stop foram modificadas para uma mudança de estilo nos botões usando jQuery. Foi adicionado um cronômetro principal para UX e para que o treinador pudesse ver o tempo total decorrido junto com os tempos individuais, semelhante a um placar de natação. O botão Salvar para passar valores para o Flask e para o MongoDB foi adicionado usando Ajax.
A função Ajax foi modelada após esta postagem do Stack Overflow e modificada para se adequar a este projeto, observando padrões de outros usos e sintaxe do Ajax. Um preventDefault foi adicionado para evitar que a página seja recarregada quando a chamada AJAX for feita.
A recursão no Jinja foi usada para iterar os dicionários aninhados em Python para renderizar os tempos e encontrar os dados corretamente, garantindo que todas as pistas fossem percorridas e exibidas. Este método do Stack Overflow foi seguido como diretriz e modificado de acordo com a natureza da minha estrutura de dados.
A função javascript para os cronômetros foi tentada a ser refatorada para que o usuário pudesse decidir quantos cronômetros deseja exibir na tela com base no número de pistas escolhidas.
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 ) ;
}
Ao tentar escrevê-los em um loop for como este, os cronômetros[i].start() não leriam i
como uma variável que poderia mudar, no entanto, quando era codificado, não havia problema:
document . getElementById ( "reset" + i ) . onclick = function ( ) {
stopwatches [ 0 ] . reset ( ) ;
}
document . getElementById ( "split" + i ) . onclick = function ( ) {
stopwatches [ 0 ] . split ( ) ;
}
Tentei abordar isso de uma maneira diferente, o que envolveu instruções if para fazer com que os cronômetros correspondentes aparecessem.
Tentativa de passar o i
através de uma função como um argumento retirado do loop for acima, mas não teve sucesso:
function chooseNumberOfStopwatches ( i ) {
if ( i == 1 ) {
stopwatches_one . start ( ) ;
}
else if ( i == 2 ) {
stopwatches_one . start ( ) ;
stopwatches_two . start ( ) ;
} else {
console . log ( 'else' ) ;
}
}
Se você estiver interessado em clonar este repositório, para configurar e instalar tudo no requirements.txt execute o seguinte comando no terminal:
$ sudo pip3 -r install requirements.txt
Observe que usei Cloud9 para este projeto, portanto, se você estiver usando um editor diferente, os comandos do terminal podem ser diferentes. Consulte a documentação do editor que você está usando para obter mais informações sobre comandos de terminal específicos do editor. Todas as chaves secretas do MongoDB precisarão ser obtidas individualmente, pois estão ocultas e são específicas para mim.