스트림 3 프로젝트: 데이터 중심 개발 - Code Institute.
이 프로젝트는 Flask Microframework를 사용하여 구축되었으며 수영과 트랙에서 여러 선수의 시간을 측정하는 수동 스톱워치로 사용할 수 있습니다. 이 애플리케이션의 목표는 서면 문서를 보관하는 것이 아니라 별도로 시간을 측정하는 사람 수를 줄이고 시간을 직접 저장하여 스포츠 시간 측정의 효율성을 높이는 것입니다.
이 응용 프로그램은 교육용으로만 사용됩니다. 이 응용 프로그램은 상업적인 용도로 사용되지 않습니다.
이 프로젝트의 라이브 데모는 여기에서 찾을 수 있습니다. 이 앱은 Heroku에서 호스팅됩니다. 시간은 MongoDB에 저장됩니다.
이 Timing Assistant 앱에 대한 아이디어는 어린 시절 내내 수영에 대한 노출에서 비롯되었습니다. 수영장의 터치패드는 다양한 이유로 항상 정확하거나 읽을 수 있는 시간을 생성하지 않기 때문에 타이밍 프로세스에 많은 사람이 참여해야 한다는 것을 알았습니다. 더욱이, 코치와 선수들은 클립보드에 시간을 기록하는 각 레인마다 데크에서 시간을 재는 여러 사람이 있지 않는 한 대회가 끝날 때까지 최종 시간이나 분할을 볼 수 없습니다.
저는 대회의 효율성을 높이고 선수와 코치에 대한 피드백을 개선하기 위해 타이밍 프로세스에 참여하는 사람의 수를 줄이는 애플리케이션을 만들 수 있는 기회를 보았습니다. 이 애플리케이션은 모든 시간 제한 스포츠에 최적화될 수 있으며 이 버전은 수영과 트랙 모두에서 작동합니다.
코치와 타이머 모두 스포츠, 대회, 이벤트, 히트, 레인 번호를 선택할 수 있으며, 이는 모두 대회 진행 상황을 추적하는 데 도움이 됩니다. 또한 저장한 후 '시간 보기'를 누르면 시간이 표시되므로 경주 후 선수에게 즉각적인 피드백을 줄 수 있습니다. 또한 한 번에 하나의 이벤트만 볼 수 있는 것이 아니라 모임의 누적 결과를 볼 수 있는지 확인하고 싶었습니다.
이 디자인에는 테마가 사용되지 않았으며 페이지에 타이머와 데이터가 많으면 지저분하고 정리되지 않은 것처럼 보일 수 있으므로 현대적인 디자인을 선택했습니다. 저는 이것을 원하지 않았기 때문에 타이머, 시간, 이벤트 옵션을 한 페이지에 담고 싶었지만 페이지를 수직으로 3등분하여 각 1/3 사이에 눈에 띄는 차이를 만드는 것이 가장 좋은 방법이라고 생각했습니다.
Timing Assistant는 백엔드에 NoSQL(MongoDB) 데이터베이스가 있고 프런트엔드에 HTML, CSS, Javascript 및 jQuery가 있고 스톱워치 기능이 AJAX로 백엔드에 연결된 Python의 Flask Microframework를 사용하여 구축되었습니다.
앱 홈 페이지에서 사용자는 원하는 스포츠, 팀 이름, 사용자 이름, 대회 이름을 입력합니다. 이 작업을 수행하면 스톱워치 페이지로 이동하여 원하는 이벤트와 히트 시간을 선택할 수 있습니다. 사용자는 최대 3개 레인의 시간을 측정하고 3개 레인 모두에 대해 분할을 수행할 수 있습니다. 세 개의 작은 타이머가 모두 중지된 후에는 개별적으로 설정되므로 사용자는 기본 타이머를 수동으로 중지해야 합니다. 그러나 코치가 경주 시간을 알고 수영 기록과 최종 완주 기록을 비교하려는 경우 이는 특히 유용할 수 있습니다. 기본 타이머의 시간은 데이터베이스에 저장되지 않습니다.
'제출'을 클릭하면 이벤트와 히트가 표시됩니다. 그런 다음 메인 스톱워치의 'START'를 눌러 최대 3개의 레인에 대한 타이밍을 시작할 수 있습니다. 이렇게 하면 4개의 스톱워치가 모두 시작되지만 맨 아래 3개만 데이터베이스에 저장됩니다. 'SPLIT' 버튼을 사용하면 각 레인의 분할 시간을 개별적으로 수집할 수 있는 옵션이 있습니다. 각 타이머는 개별적으로 중지하고 시작할 수 있으며 기본 타이머는 모든 스톱워치(시작, 중지 및 재설정)를 제어합니다.
'SAVE TIMES'를 클릭하면 Javascript 파일의 AJAX 호출을 사용하여 Flask가 MongoDB에 연결된 상태에서 Flask로 시간을 푸시하여 시간이 저장됩니다. 시간이 저장되면 '시간 보기'를 클릭하면 스톱워치 아래에 시간이 표시됩니다.
데이터는 지정된 이벤트나 히트 없이도 저장됩니다(시간을 볼 때 이 필드는 비어 있음). 코치가 대회 환경이 아닌 연습에서 스톱워치를 사용하려는 경우 유용할 수 있습니다.
코치들에게 PDF나 다른 파일 형식으로 데이터를 다운로드할 수 있는 기회를 제공하고 싶습니다. 장기적으로 볼 때, 손으로 쓴 데이터에 의존하거나 과거 모임을 보고 싶을 때마다 이 사이트에 의존하지 않고도 기록에 대한 모든 수동 타이밍을 유지할 수 있게 될 것입니다.
또한 코치가 각 예선의 선수 수에 따라 보고 싶은 스톱워치 수를 선택할 수 있도록 허용하고 싶습니다. 현재는 한 번에 3명의 선수만 기록할 수 있으며, 3명 미만의 선수에 대해서는 기록을 선택할 수 없습니다.
그리고 좀 더 정교한 타이밍과 연습을 할 수 있는 '연습 모드'와 '미트 모드'를 구현하고 싶습니다. Meet 모드에서는 이벤트나 예선 선택에 더 많은 제한이 생기고 코치가 원하는 레인 수를 선택할 수 있습니다. 연습 모드에서는 코치가 이벤트나 히트를 지정할 필요 없이 저장한 시간(특정 훈련 등)을 메모할 수 있습니다.
이 프로젝트의 모든 테스트는 수동으로 수행되었습니다. 랜딩 페이지의 양식에는 사용자가 양식의 필드를 작성하지 않는 것을 방지하기 위해 입력 태그에 필수 속성이 있습니다. Flask 앱 경로가 이러한 입력에 의존하기 때문에 400 오류가 발생하게 됩니다.
Ajax 기능과 Save Times 버튼은 콘솔을 통해 테스트되었으며 데이터가 MongoDB에서 올바른 형식으로 표시되었는지 확인했습니다. 타이머에서 수집된 데이터와 의도된 데이터 구조도 테스트되었습니다.
레인당 하나의 문서로 개별적으로 시간 절약:
동일한 문서에 표시되는 레인 시간(참고: 테스트 목적으로 두 개의 레인만 동일한 문서에 표시되도록 여기에 저장되었습니다.):
올바른 데이터 구조:
스톱워치에 대한 테스트도 수동으로 수행하여 기본 재설정 버튼이 스톱워치를 재설정하고 모든 타이머에서 스플릿을 지우는 반면 각 개별 스톱워치는 자체 시간과 스플릿만 지웠는지 확인했습니다. 또한 기본 스톱워치는 모든 스톱워치를 제어하는 반면 개별 스톱워치는 자체 시작/중지 기능만 제어해야 하므로 시작/중지 기능에 대해서도 테스트되었습니다.
또한 모든 Flask 경로를 테스트하여 모든 링크가 작동하는지, 입력에서 일반적이지 않은 값을 처리할 수 있는지, HTML 파일에서 Jinja를 통해 입력이 제대로 표시되는지 확인했습니다.
테스트 과정에서 두 명의 사용자가 동일한 모임 이름이나 동일한 클럽 이름을 가질 수 있으므로 사용자가 다른 사람의 데이터를 접할 수 있다는 것을 깨달았습니다. 따라서 템플릿의 시간을 보기 위해 랜딩 페이지의 3개 입력 필드가 일치해야 해당 시간을 표시할 수 있도록 다음을 포함했습니다. 교차 저장이나 시간 교차 조회를 방지하려면 올바른 팀 이름, 사용자 이름, 모임 이름이 일치해야 합니다.
{% 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 타이머_페이지.html에서 AJAX 함수에 대한 데이터를 전송하는 양식에는 끝 태그가 있는 것처럼 보이지만 시간을 MongoDB에 저장하려면 최종 시간, 분할 및 레인 데이터를 모두 포함해야 합니다. . 표시해야 하는 스타일 및 기타 요소로 인해 양식이 다른 요소와 순서가 맞지 않는 것처럼 보입니다. 또한 HTML을 보면 빈 태그가 있지만 여기에서 jQuery를 사용하여 분할 시간이 HTML에 삽입됩니다.
시간 저장 시 각 타이머(예: 1번 레인)에 동일한 레인을 선택하면 1번 레인 중 하나만 조회 시간에 표시됩니다. 하지만 데이터 구조의 특성상 레인이 없으면 저장된 시간을 볼 수 없으므로 레인을 선택해야 합니다. 차선 드롭다운은 사용자가 차선을 지정하지 않을 경우 기본적으로 레인 1, 랜드 2, 랜드 3에 저장되도록 설정되어 있습니다. 사용자가 히트에서 두 레인에 대해 동일한 레인 번호를 선택한 경우 시간이 저장되지 않도록 하는 유효성 검사를 구현하고 싶습니다.
스톱워치를 실행하는 Javascript 함수는 이 애플리케이션에 대한 Sara의 스톱워치 튜토리얼을 사용한 코딩에서 수정되었습니다. 일부 HTML도 그녀의 예를 따라 모델링되었지만 스타일, 다중 버튼, 분할 및 레인에 맞게 수정되었습니다.
JavaScript의 경우 페이지를 새로 고치는 대신 모든 스톱워치를 재설정하도록 재설정 버튼의 재설정 기능을 수정했으며, 작은 스톱워치 각각의 개별 재설정 버튼은 본 프로젝트의 UX에 불필요한 기능이므로 제거했습니다. 분할 기능도 추가되었습니다. jQuery를 사용하여 버튼의 스타일 변경을 위해 시작/중지 기능을 수정했습니다. UX를 위한 메인 스톱워치가 추가되어 코치는 수영 점수판과 유사하게 개별 시간과 함께 총 경과 시간을 확인할 수 있습니다. Ajax를 사용하여 Flask와 MongoDB에 값을 전달하는 저장 버튼이 추가되었습니다.
Ajax 함수는 Stack Overflow의 이 게시물을 모델로 삼았고 다른 Ajax 사용 및 구문의 패턴을 살펴봄으로써 이 프로젝트에 맞게 수정되었습니다. AJAX 호출이 이루어질 때 페이지가 다시 로드되지 않도록 방지Default가 추가되었습니다.
Jinja의 재귀는 Python의 중첩된 사전을 반복하여 모든 레인이 반복되고 표시되도록 함으로써 시간을 렌더링하고 데이터를 적절하게 충족시키는 데 사용되었습니다. Stack Overflow의 이 방법을 지침으로 따랐으며 내 데이터 구조의 특성에 맞게 수정되었습니다.
사용자가 선택한 차선 수에 따라 화면에 표시할 스톱워치 수를 결정할 수 있도록 스톱워치에 대한 자바스크립트 기능을 리팩토링하려고 시도했습니다.
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 문을 포함하는 다른 방식으로 접근하려고 했습니다.
위의 for 루프에서 가져온 인수 대신 함수를 통해 i
를 전달하려고 시도했지만 실패했습니다.
function chooseNumberOfStopwatches ( i ) {
if ( i == 1 ) {
stopwatches_one . start ( ) ;
}
else if ( i == 2 ) {
stopwatches_one . start ( ) ;
stopwatches_two . start ( ) ;
} else {
console . log ( 'else' ) ;
}
}
이 저장소를 복제하는 데 관심이 있는 경우 요구사항.txt의 모든 항목을 설정하고 설치하려면 터미널에서 다음 명령을 실행하세요.
$ sudo pip3 -r install requirements.txt
이 프로젝트에서는 Cloud9을 사용했기 때문에 다른 편집기를 사용하는 경우 터미널 명령이 다를 수 있습니다. 편집기별 터미널 명령에 대한 자세한 내용은 사용 중인 편집기의 문서를 참조하세요. MongoDB의 모든 비밀 키는 숨겨져 있고 나에게만 국한되므로 개별적으로 얻어야 합니다.