ローイングファンのプログラミング日記

ボート競技やプログラミングについて書きます

レガッタファンのつかい方

Webサービスをアップしたのでつかい方について書きます。

レガッタ情報投稿サイト - レガッタファン
https://regattafan.herokuapp.com


目次


レガッタファンとは

レガッタファンはボート競技やローイングエルゴ競技の情報を発信するサイトです。

できることは次のとおりです。

  • 誰でもできること
    • 記事の閲覧
    • 記事からTweet(の下書き)を作成
  • 会員登録するとできること
    • 記事の投稿、編集、削除
    • 記事の内容をファイル(CSV, JSON)にしてダウンロード


記事の閲覧とTweet

記事の閲覧

読みたい記事を一覧表から選んでタイトルをクリック/タップすれば記事が読めます。
一覧表はTopページに最新20件、それ以前はアーカイブにあります。
クルー名や大会名から記事の検索もできます。

Tweetの下書き作成

記事のページからTweetの下書きをつくることができます。

Twitterのアイコンをクリック/タップ

f:id:rowingfan:20190122180356p:plain

Tweetの下書きが記入されたTwitter画面が開きます。

組合せの例

f:id:rowingfan:20190123091850j:plain

結果の例

f:id:rowingfan:20190122180527j:plain

記事を投稿するには

会員登録すると記事を投稿できます。
ログインしてからTopページにある投稿ボタンをクリック/タップすれば投稿を作成する画面に移ります。
投稿の際、次の3項目は必須です。その他の項目は自由です。

  • 組合せか結果の選択
  • 大会開催の年
  • 大会名


f:id:rowingfan:20190122180706j:plain


ひととおり記入したら投稿画面の下部にある投稿ボタンを押せば投稿完了です。

会員がつかえる機能

会員は自分が書いた記事の編集、削除ができます。また「結果」の記事は内容をファイルにしてダウンロードできます。

f:id:rowingfan:20190122182417p:plain

記事の編集、削除

投稿した記事は編集することができます。
ペンのアイコンをクリック/タップすると編集画面にすすみます。

組合せから結果に書き換えるときは、
まずラジオボタンのチェックを組合せから結果に変更、
レーン順のまま順位をつけて再投稿すれば自動で着順に並び替わります。

順位づけをしてタイムなどを入力する。

f:id:rowingfan:20190122180945j:plain

自動で表が着順に変わります。

f:id:rowingfan:20190122181036p:plain



記事を削除したいときはゴミ箱のアイコンをクリック/タップしてください。

記事の内容をダウンロード

組合せ/結果の選択で結果をチェックしていれば、記事の内容をファイルにしてダウンロードできます。
組合せの記事はダウンロードできません。
ファイル形式はCSVJSONがあります。

CSV表計算アプリで読み込んだ例

f:id:rowingfan:20190122181213p:plain

JSONをエディタで開いた例

f:id:rowingfan:20190122181330p:plain

(*改行と字下げはエディタであとから入れたものです。)


以上です。興味があれば使ってください。

レガッタ情報投稿サイト - レガッタファン
https://regattafan.herokuapp.com


前につくったこちらもよろしくお願いします。

レガッタ情報検索サイト - ローイングファン
https://rowingfan.herokuapp.com/

Heroku CLIログインがデフォルトでブラウザを開くようになった

ターミナルからHerokuにログインしようとしたらブラウザからログインするようにいわれた。

$ heroku login


返答

Press any key to open up the browser to login or q to exit:


調べてみたらHeroku CLI ログインがデフォルトでブラウザを開くようになっていた。

Heroku Dev Center
Heroku CLI login now opens the browser by default
https://devcenter.heroku.com/changelog-items/1530

By default, the heroku login command now opens your web browser to complete the login flow.


To continue using the interactive, terminal-based login flow, pass the --interactive option to heroku login.


指示通り--interactiveオプションをつけたらログインできた。

$ heroku login --interactive


環境
Heroku CLI 7.19.4

Vue.jsでストップウォッチ & ピッチ計(ボート競技用)をつくった

目次


Vue.js

Vue.jsはユーザーインターフェイスを構築するためのフレームワーク
Vue.jsを使うにはJavaScriptの知識が必要。

Vue.js公式ページ
https://jp.vuejs.org/


コード

前回書いたコードをVue.jsを使って書きかえた。

前回の記事
JavaScriptでストップウォッチ & ピッチ計(ボート競技用)をつくった https://rowingfan.hatenablog.jp/entry/2018/12/16/174428

今回Vue.jsを使って書いたコードは以下。
index.html

<!DOCTYPE html>
<html lang="ja">
<head>
   <meta charset="utf-8" />
   <title>ローイングピッチ計 Vue.js版</title>
</head>
<body>
    <div id="app">
        <p>{{ time }}</p>
        <p>{{ rate }}</p>
        <button v-on:click="countUp">{{ startBtn }}</button>
        <button v-on:click="timerStop">{{ stopBtn }}</button>
        <button v-on:click="timerReset">{{ resetBtn }}</button>
    </div>
    <!-- Vue.js CDN -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- JavaScript -->
    <script src="main.js"></script>
</body>
</html>


main.js

let id; //setTimeout動作の代入用
let isRunning = false; //計測中か否かの状態

// Vueインスタンスの作成
new Vue({
    el: '#app',

    data: {
        time: '00:00.000',
        rate: '00.0',
        startBtn: 'Start',
        stopBtn: 'Stop',
        resetBtn: 'Reset'
    },
    
    methods: {
        // Startボタンを押したときの処理
        countUp: function() {
            if (isRunning === false) {
                let vm = this;
                let startTime = Date.now();
                let count;
                
                count = function() {
                    let countup = Date.now() - startTime;
                    console.log(countup);
                    let min = Math.floor(countup / 60000);
                    let sec = Math.floor(countup % 60000 / 1000);
                    let msc = countup % 1000;

                    min = ('0'  + min).slice(-2);
                    sec = ('0'  + sec).slice(-2);
                    msc = ('00' + msc).slice(-3);
                    vm.time = `${min}:${sec}.${msc}`;

                    rateUp = (60000 / countup * 3).toFixed(1);
                    vm.rate = rateUp;
                    
                    id = setTimeout(count, 10);
                }
                count();
                isRunning = true;
            } // if (isRunning === false) 
        },

        // Stopボタンを押したときの処理
        timerStop: function() {
            if (isRunning === true) {
                clearTimeout(id);
                isRunning = false;
            }
        },

        // Resetボタンを押したときの処理
        timerReset: function() {
            if (isRunning === false) {
                this.time = '00:00.000'
                this.rate = '00.0'
            }
        }
    } // methods:
})


コードを書いた感想

前回のコードと概ね同じで動いた。
全体的にこれでいいのか?
setTimeoutの変数を定義する位置で悩んだ。
今回の規模ではVue.jsのメリットを感じないけれどメソッドの使い回しとかは便利そう。

今回の動作確認

Google Chrome 71.0
Safari 12.0.2

JavaScriptでストップウォッチ & ピッチ計(ボート競技用)をつくった

目次


はじめに

JavaScriptの勉強をはじめた。
当面は基礎を学んでいずれはVue.jsを覚えたい。
目標はシングルページアプリケーション(SPA)をつくること。

今回はボート競技(ローイング)用のピッチ計をつくってみた。

ピッチ計とは

1分間あたりの動作回数を計るストップウォッチ。
ボート競技では1分間に漕ぐ回数を計測する。
今回のピッチ計は3ストローク(漕ぎ)で計測する仕様にした。

計算式

レート(ピッチ)の計算式は次のとおり。

1分 ÷ 3ストロークにかかった時間 × 3本



6秒で3ストロークの場合は1分で30ストロークになる。この場合のレートは30となる。
JavaScriptの時間単位はミリ秒なので次のような式になる。

60000 ÷ 6000 × 3 = 30

完成品

実際につくったものをFirebaseで公開した。

ローイングピッチ計
https://rowingtool.firebaseapp.com

コード

基礎のコードは次のとおり。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
   <meta charset="utf-8">
   <title>ローイングピッチ計</title>
</head>
<body>
    <div id="timer">00:00.000</div>
    <div id="rate">00.0</div>
    <button id="start">Start</button>
    <button id="stop">Stop</button>
    <button id="reset">Reset</button>
    <script src="main.js"></script>
</body>
</html>


main.js

'use strict';

class StopWatch {
    constructor() {
        this.timer = document.getElementById('timer');
        this.rate  = document.getElementById('rate');
        this.start = document.getElementById('start');
        this.stop  = document.getElementById('stop');
        this.reset = document.getElementById('reset');
    }

    countUp() {
        //共通変数の定義
        let timer = this.timer;
        let rate = this.rate;
        let id; //setTimeout動作の代入用
        let isRunning = false; //計測中か否かの状態
        let rateUp;
        
        //スタートボタンを押したときの処理
        this.start.addEventListener('click', function() {
            if (isRunning === false) {
                let startTime = Date.now();
                let count = function() {
                    //タイムカウント
                    let countup = Date.now() - startTime;
                    //00:00.000表示を作成する
                    (function() {
                        let min = Math.floor(countup / 60000);
                        let sec = Math.floor(countup % 60000 / 1000);
                        let msc = countup % 1000;

                        min = ('0'  + min).slice(-2);
                        sec = ('0'  + sec).slice(-2);
                        msc = ('00' + msc).slice(-3);

                        timer.textContent = `${min}:${sec}.${msc}`
                    })();
                    id = setTimeout(count, 10);

                    // ピッチ計(1分当りのストローク数)
                    // 3ストロークで計測
                    rateUp = (60000 / countup * 3).toFixed(1);
                    rate.textContent = rateUp;
                }; //END count function
                count();
                isRunning = true;
            } //END if isRunning
        });

        //ストップボタンを押したときの処理
        this.stop.addEventListener('click', function() {
            if (isRunning === true) {
                clearTimeout(id);
                isRunning = false;
            }
        });

        //リセットボタンを押したときの処理
        this.reset.addEventListener('click', function() {
            if (isRunning === false) {
                timer.textContent = '00:00.000';
                rate.textContent = '00.0';
            }
        });
    } //END countUp method
}

let stopwatch = new StopWatch();
stopwatch.countUp();


コードを書いた感想

変数と関数の扱いが難しい。
前後に行ったり来たりできるので混乱する。

Vue.jsで書きかえ

Vue.jsを使って書きかえてみた。

Vue.jsでストップウォッチ & ピッチ計(ボート競技用)をつくった - ローイングファンの日記

動作確認

Google Chrome 71.0
Safari 12.0.2
Firebase CLI 6.1.2

Time差0.1秒未満のレースを選び出す

今回はデータベースに収録してあるボートレース(レガッタ)の記録から僅差のレースを選び出す。
データベースの操作にはActive Recordをつかう。

目次


条件

PostgreSQLをインストールして次のようなテーブルを用意しておく。

データベース名: result
テーブル名: results

year
(int)
tournament_name
(varchar(10))
raceno
(int)
event
(varchar(15))
group
(varchar(10))
rank1
(varchar(10))
time_1st
(varchar(10))
rank2
(varchar(10))
time_2nd
(varchar(10))
rank3
(varchar(10))
time_3rd
(varchar(10))
2000 全日本選手権 1 男子エイト 予選A組 Aクルー 05:50.12 Bクルー 05:55.21 Cクルー 06:00.33
・・・ ・・・ ・・・ ・・・ ・・・ ・・・ ・・・ ・・・ ・・・ ・・・ ・・・
2018 全日本選手権 137 男子エイト 予選A組 Xクルー 05:51.12 Yクルー 05:56.21 Zクルー 06:01.33


プログラムの内容

2014年から2018年の5年間で1着と2着のタイム差が0.1秒未満のレースを抽出する。

  • データベースから5年分のレース結果を抽出する
  • 文字列のタイムをparseメソッドでTimeオブジェクトにする
  • タイム差が0.1秒未満という条件をつける
  • 条件が真のデータを出力する


必要なGemのインストール

RubyGemsをアップデートする。

gem update --system


Bundlerをアップデートする。

gem update bundler

※ Bundlerがインストールされていない場合はgem install bundlerでインストールする。

Gemfileを作成する。

bundle init


Gemfileに下記を追記する。

gem 'activerecord'
gem 'pg'
gem 'dotenv'


Gemをインストールする。

bundle install --path vendor/bundle


.envファイルをつくる

コードにそのまま表記したくない情報を変数に代入する。
.envファイルをつくり、つぎのように記入する。

myadapter='postgresql'
myname='Username' #各々のユーザーネーム
mydatabase='result'


Rubyのコードを書く

activerecord_lesson.rb

require 'active_record'
require 'dotenv/load'
require 'time'

class Result < ActiveRecord::Base
    establish_connection(
        adapter:  ENV['myadapter'],
        host:     "",
        username: ENV['myname'],
        password: "",
        database: ENV['mydatabase']
    )
    #年代を絞り込むメソッド
    def select_races
        race_results = Result.where(
            "year = ? or year = ? or year = ? or year = ? or year = ?",
            "2014","2015","2016","2017","2018"
        )
        return race_results
    end
    #タイム差が0.1秒未満のレースだけ出力するメソッド
    def display_narrow_margin_races(race_results)
        race_results.each do |race_result|
            #2着と1着のタイムを文字列からTimeオブジェクトに変換する
            parsed_time_2nd = Time.parse("00:#{race_result['time_2nd']}")
            parsed_time_1st = Time.parse("00:#{race_result['time_1st']}")
            if (parsed_time_2nd - parsed_time_1st) < 0.1
                puts "#{race_result['year']}#{race_result['tournament_name']}"
                puts "RaceNo:#{race_result['raceno']}"
                puts "#{race_result['event']} #{race_result['group']}"
                puts "1着: #{race_result['rank1']} Time: #{race_result['time_1st']}"
                puts "2着: #{race_result['rank2']} Time: #{race_result['time_2nd']}"
                puts "Time差: #{parsed_time_2nd - parsed_time_1st}"
                puts "-----------------"
            end
        end
    end
end

#モデルを作成
result = Result.new
#年代を絞り込む
race_results = result.select_races
#出力する
result.display_narrow_margin_races(race_results)


実行する

PostgreSQLサーバーを起動する。

pg_ctl -D /usr/local/var/postgres -l logfile start


Rubyコードを実行する。

bundle exec ruby activerecord_lesson.rb


結果

次のようにターミナルに出力される。
- 例 -

2017全日本軽量級選手権
RaceNo:100
男子シングルスカル 準決C組
1着: Aクルー Time: 07:30.56
2着: Bクルー Time: 07:30.57
Time差: 0.01秒
-----------------
2018全日本大学選手権
RaceNo:193
男子舵手なしクォドルプル 決勝
1着: Cクルー Time: 06:21.39 
2着: Dクルー Time: 06:21.47 
Time差: 0.08秒


終了する

PostgreSQLサーバーを停止する。

pg_ctl -D /usr/local/var/postgres -l logfile stop


注意するところ

ActiveRecordを使うときはpgのインストールも必要(requireは不要)。

parseする文字列は時間:分:秒の書式になっている必要があるので、今回の場合は桁を合わせるために時間単位の00:を加える必要がある。

Timeオブジェクトの引き算で求めた差はTimeオブジェクトではなくFloatオブジェクトになる。
なので今回の条件式は(parsed_time_2nd - parsed_time_1st) < 0.1となる(Floatオブジェクトの0.1を使う)。

今回の環境

macOS 10.14.1
PostgreSQL 10.3
Ruby 2.5.1p57
RubyGems 2.7.8
Bundler 1.17.1
Active Record 5.2.1
pg 1.1.3
dotenv 2.5.0

Ruby 日時をつくるときの桁あわせ

Date.new

2018年11月9日を作りたい場合。

エラーになる例

require 'date'
p Date.new(2018,11,09)


エラー表示

Invalid octal digit
p Date.new(2018,11,09)
                   ^~


日にち09の0が余計なので0をとる
一桁月の場合も同様

修正後

require 'date'
p Date.new(2018,11,9)


#<Date: 2018-11-09 ((2458432j,0s,0n),+0s,2299161j)>


Time.parse

文字列からTimeオブジェクトの6分6秒57を作りたい場合。

エラーになる例

require 'time'
p Time.parse("06:06.57")


エラー表示

mday out of range (ArgumentError)


時間の桁が必要なのでつけ加える

修正後

require 'time'
p Time.parse("00:06:06.57")


2018-11-12 00:06:06 +0900


コンマ以下の秒が表示されていないが値は保持されているのでstrftimeで文字列に戻すときは表示できる。

require 'time'
goal_time = Time.parse("00:06:06.57")
p goal_time
p goal_time.strftime("%-M:%S.%2N")


2018-11-12 00:06:06 +0900
"6:06.57"


今回の環境

macOS 10.14.1
Ruby 2.5.1p57

久しぶりに動かすときは余裕をもって

目次


急いでいるときに動かないとあせる

あるシステムを久しぶりに使おうとしたとき、アップデートが必要だったり関連システムの再インストールが必要だったりで、すぐには動かないことがある。
アップデートやインストールに時間がかかることもあるので、慣れ親しんだシステムでも、ご無沙汰してしまった場合は早めに試運転しておいたほうがいい。

gitが動かずあせる

macOSを10.14 Mojaveにアップデートしたらgitが動かなくなった。
そのときのエラー表示

xcrun: error


調べたところコマンドラインデベロッパ・ツールのインストールが必要らしいのでターミナルで次のコマンドを入力

xcode-select --install


あとは指示にしたがってコマンドラインデベロッパ・ツールをインストールるすれば元の通りgitが使えるようになる。

Heroku CLIのアップデート

ターミナルからHerokuにログインしたとき、Heroku CLIのバージョンが古くなっていると次のような警告が出る。

Warning: heroku update available from 7.14.1 to 7.18.3


Heroku CLIは自動でアップデートしてくれるものと思っていたけれどそうではないらしい。
そのままでも操作はできるけれど早めにアップデートしたほうが望ましいだろうから、次のコマンドを入力する

heroku update


heroku: Updating CLI from 7.14.1 to 7.18.3... done
heroku: Updating CLI... done
Updating completions... done


これでアップデートできた。