鶏頭のプログラム

Ruby, Javascript, C言語, SQLなどのプログラミング

RubyでGoogleカレンダーの情報を引き抜いてみる

5,6年前に作ったGoogleカレンダーと会社のカレンダーを同期するシステムが
突然動かなくなったので修正しました。
理由は簡単で
Google Calendar Api V2がサポート切れになったから!

私は最近このプログラムを使ってなかったのでまったく困ってなかったのですが
他の人が結構ヘビーに使っていた人がいたので
重い腰をあげてGoogle Calendar API v3 に対応しました。

もともとこのコードはJavaで書いていたのですが、せっかくなのでRubyで書きなおしを
行いました。

そこそこ面倒だったので、手順を記載します。

基本は下記サイトの手順で行いました。


rubyにてgoogleカレンダーの情報を取得する - Qiita

しかしながら
下記のコマンドを実行を実行した際に問題が発生しました。

google-api oauth-2-login --client-id="<your client id>" --client-secret="<your client secret>" --scope="https://www.googleapis.com/auth/calendar"

サイト内ではこのコマンドを実行するとブラウザが立ち上がり
OAuthの認証の承認をすると ~/.google-api.yaml がダウンロードされるという旨が記載されているが
一向にダウンロードされません。
理由は簡単で、Google ChromeおよびFireFoxでは.google-api.yamlファイルがダウンロード
されるのですがなぜかIEだとされないというよくわからない不具合のせい。

なので、IEでも問題ないようにコードを書きました。

# coding: utf-8

require 'google/api_client'
require 'google/api_client/client_secrets'
require 'google/api_client/auth/installed_app'
require 'json'
require 'yaml'

client = Google::APIClient.new(
  :application_name => 'Example Ruby application',
  :application_version => '1.0.0'
)
plus = client.discovered_api('plus')

flow = Google::APIClient::InstalledAppFlow.new(
  :client_id => 'クライアントID',
  :client_secret => 'クライアントシークレット',
  :scope => ['https://www.googleapis.com/auth/calendar']
)

client.authorization = flow.authorize
File.open("./.google-api.yaml", "w"){|f|
	x = {
		"mechanism" => "oauth_2",
		"scope" => "https://www.googleapis.com/auth/calendar",
		"client_id" => client_secrets.client_id,
		"client_secret" => client_secrets.client_secret,
		"access_token" => client.authorization.access_token,
		"refresh_token" => client.authorization.refresh_token
	}
	f.puts(x.to_yaml)
}

上記コードを実行すると.google-api.yamlファイルが実行ディレクトリに作成されます。

あとはgoogleカレンダーからデータを取得します。

oauth_yaml = YAML.load_file('./.google-api.yaml')
client = Google::APIClient.
client.authorization.client_id = oauth_yaml["client_id"]
client.authorization.client_secret = oauth_yaml["client_secret"]
client.authorization.scope = oauth_yaml["scope"]
client.authorization.refresh_token = oauth_yaml["refresh_token"]
client.authorization.access_token = oauth_yaml["access_token"]
al = client.discovered_api('calendar', 'v3')

# 時間を格納 向こう一か月分
today = Date.today
time_min = Time.utc(today.year,today.month, today.day, 0).iso8601
time_max = Time.utc(onemonth.year, onemonth.month, onemonth.day, 0).iso8601

# イベントの取得
params = {'calendarId' => conf["calid"],
          'orderBy' => 'startTime',
          'timeMax' => time_max,
          'timeMin' => time_min,
          'singleEvents' => 'True'}

result = client.execute(:api_method => cal.events.list,
                        :parameters => params)

# イベントの取得
params = {'calendarId' => conf["calid"],
          'orderBy' => 'startTime',
          'timeMax' => time_max,
          'timeMin' => time_min,
          'singleEvents' => 'True'}


result.data.items.each do |event|
        p event.start.dateTime # 開始時間
        p event.end,dateTime # 終了時間
        p event.summary # タイトル
        p event.location # 場所
end

こんな感じになります。

人材募集企画 2011年版を解いてみた

ぷりんさんのプロフィール - はてなさんの記事を見て
自分ならどう書くかなと試してみました。

内容はこちら


ぷよぷよの問題(人材募集企画 2011年版 問題2)をC++解いてみました。 - Gobble up pudding

試験内容はこちら

人材募集企画 2011年版: 人生を書き換える者すらいた。

プログラミング言語: Ruby
所要時間: 1時間ちょい

正直速度面ではループが多いため少し遅い気がします。
最適解とは言いがたいですが、まずまずと言ったところでしょうか。
追記:ちょっと修正しました。

N=0
G=1
Y=2
R=3
CHECKED=4

class PuyoPuyo
  def initialize list
    @list = list
  end

  def delete chain = 1
    clone = Array.new 
    @list.each{|line| clone << line.clone }
    points = get_del_points(clone)
    return if points.length == 0
    points.each{|point|
      point[0].upto(@list.length - 1){|n|
       @list[n][point[1]] = @list[n + 1] ? @list[n + 1][point[1]] : N
      }
    }
    show chain
    delete chain + 1
  end

  def get_del_points list
    del_points = Array.new
    0.upto(list.length - 1){|n|
      0.upto(5){|m|
        c = list[n][m]
        next if c == N || c == CHECKED
        list[n][m] = CHECKED
        points = check_all_side(c, n, m, list)
        next if(points.length < 4)
        del_points += points
      }
    }
    del_points.sort! {|a, b| b[0] <=> a[0] }
    return del_points
  end

  def check_all_side c, n, m, list
    points = Array.new
    points << [n,m]
    # 下
    if n != 0 && list[n-1][m] == c
      list[n-1][m] = CHECKED
      points += check_all_side(c, n-1, m, list)
    end

    # 上
    if n+1 < list.length && list[n+1][m] == c
      list[n+1][m] = CHECKED
      points += check_all_side(c, n+1, m, list)
    end

    # 左
    if m != 0 && list[n][m-1] == c
      list[n][m-1] = CHECKED
      points += check_all_side(c, n, m-1, list)
    end

    # 右
    if list[n][m+1] == c
      list[n][m+1] = CHECKED
      points += check_all_side(c, n, m+1, list)
    end
    return points
  end

  def show chain
    p '-------------------------'
    p "#{chain}連鎖"
    p '-------------------------'
    (@list.length - 1).downto(0){|n|
      0.upto(5){|m|
        output =   @list[n][m] == R ? 'R' :
                   @list[n][m] == Y ? 'Y' :
                   @list[n][m] == G ? 'G' : ' '
        print(output)
      }
      print "\n"
    }
    p '-------------------------'
  end

end

puyo_list = Array.new

puyo_list << [G,R,Y,G,Y,R]
puyo_list << [G,R,Y,G,Y,R]
puyo_list << [G,R,Y,G,Y,R]
puyo_list << [R,Y,G,Y,G,G]
puyo_list << [Y,R,R,G,R,G]
puyo_list << [Y,G,Y,R,Y,R]
puyo_list << [Y,G,Y,R,Y,R]
puyo_list << [G,Y,R,Y,R,G]
puyo_list << [Y,G,Y,R,Y,G]
puyo_list << [R,Y,G,Y,R,G]
puyo_list << [G,Y,G,Y,R,R]
puyo_list << [R,Y,Y,G,Y,G]
puyo_list << [N,N,G,Y,R,R]

puyo = PuyoPuyo.new puyo_list
puyo.delete

SJISの文字コード表

SJIS文字コード表を出力するプログラムを作成しました。
理由は、ただ自分の環境でどういった文字コードが表示でき
どういった文字コードが文字化けするのかを検査する必要があったためです。


tebakane/SJISCharCode · GitHub

とりわけたいしたコードでもなく
効率的に処理をしているわけでもないのですが、
どこかで使う場面もあるかなと思いGitHubにあげました。

bashの脆弱性

bash脆弱性が発見されました。

かなり緊急度の高い障害ですね。
bashのコマンドが自由に実行できてしまうからかなり厄介です。
というわけで
MacCentOSbashのupdateをかけました。

とりあえず、自分のbash脆弱性が存在するかどうかの確認から。
下記コマンドを実行。

$ env x='() { :;}; echo vulnerable' bash -c 'echo hello'

そうすると下記の様に表示されました。

vulnerable
hello

というわけでばりばり脆弱性まっただ中なわけですが
とりあえずCentOSのほうは

$ yum update bash

でOK。一応サーバー再起動が吉。

Macのほうはportとかhomwbrewとか使って最新のbash入れるのが
よいです。そのうちAppleも出すだろうけど現状は出てないので。

$ sudo port install bash

上記コマンドでインストールされたbash
/bin/bashと入れ替えるのもお忘れなく。

これで先ほどのコマンド

$ env x='() { :;}; echo vulnerable' bash -c 'echo hello'

を実行すると

bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
hello

と表示されます。

しかし、対応が速いですね。

備忘録(PostgreSQLで様々な一覧を取得)

今回の記事はPostgreSQLのよく使うわりにいつも調べているSQLの一覧について
備忘録として記載します。

テーブル一覧

select 
  relname as TABLE_NAME 
from 
  pg_stat_user_tables

テーブルとカラム一覧

select 
  cl.*
from 
  information_schema.columns cl
  inner join pg_stat_user_tables ut
    on cl.table_name = ut.relname
order by 
  ordinal_position;


外部キーの一覧を取得するSQL

SELECT
    tc.constraint_name, 
    tc.table_name, 
    kcu.column_name, 
    ccu.table_name AS foreign_table_name,
    ccu.column_name AS foreign_column_name 
FROM 
    information_schema.table_constraints  tc 
    INNER JOIN information_schema.key_column_usage  kcu
       ON tc.constraint_name = kcu.constraint_name
    INNER JOIN information_schesema.constraint_column_usage ccu
       ON ccu.constraint_name = tc.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY'


ログインユーザーのテーブル、カラムの一覧および
主キー及び外部キーの情報一覧

SELECT
        cl.table_name,
        cl.column_name,
        fk.constraint_name,
        fk.foreign_table_name,
        fk.foreign_column_name,
        fk.constraint_type
FROM
        information_schema.columns cl
        INNER JOIN pg_stat_user_tables ut
        ON ut.relname = cl.table_name
        LEFT OUTER JOIN 
           (SELECT     
                 tc.constraint_name, 
                 tc.table_name, 
                 kcu.column_name, 
                 ccu.table_name AS foreign_table_name,
                 ccu.column_name AS foreign_column_name,
                 tc.constraint_type
            FROM    
                 information_schema.table_constraints  tc 
                 INNER JOIN information_schema.key_column_usage  kcu
                 ON tc.constraint_name = kcu.constraint_name
                 INNER JOIN information_schema.constraint_column_usage ccu
                 ON ccu.constraint_name = tc.constraint_name
                 WHERE tc.constraint_type IN ('FOREIGN KEY','PRIMARY KEY')) fk
            ON fk.table_name = cl.table_name AND
                 fk.column_name = cl.column_name
ORDER BY
      cl.table_name,
      cl.ordinal_position;

なにか間違いやもっといい書き方等あれば教えていただけると有り難いです。

Webスクレイピング(imgタグのURLの相対パスについて)

Webスクレイピングしている時に
imgタグのURLを取得したい場合というのはよくあります。

ただそういう際に困るのはimgタグのsrcが下記のように相対パスで書かれている場合です。

<img src="../img/hello.png">

今時のHTMLでは普通相対パスで書くことはあまりないのですが
レガシーなシステムではよくあることです。
このように相対パスから正確なURLを取得する際になかなか面倒です。

しかしRubyの場合は下記のように一行でいけます。

require 'uri'
URI.join("http://hogepiyo.jp/path/to/index.html", "../../img/hello.png").to_s

とすると

http://hogepiyo.jp/img/hello.png

とURLを取得することが可能です。
今回は以上です。

printfは遅い

C言語デバッグの時とかに使われるprintfはとてつもなく便利で
とりあえず、一番最初に使われる関数だと思います。

#include <stdio.h>
int main(int argc, char **argv){
  printf("Hello World!\n");
  return 0;
}

私個人としては、どんな言語を始めるにしても
とりあえず "Hello World!" を出力するプログラムを書くのがプログラミングの神様への
礼儀だと思っています。

というのはどうでもいい話なのですが、今回はprintfはとてつもなく遅いというお話です。

私もあまり気にしたことはないのですが
今回、WindowsSQLServerをOLE DB経由で呼び出すプログラミングを作成している際に気がつきました。

printfがあるせいで大量データを取得する際に異常に遅くなるのです。

というわけでこんなサンプルを作成しました。

#include <stdio.h>
#include <time.h>

void method1();
void method2();

int main(){
        FILE *fp = fopen("./time.txt", "w");
        clock_t start, end;
        start = clock();
        method1();
        end = clock();
        fprintf(fp, "時間:%.10f\n", (double)(end-start)/CLOCKS_PER_SEC);

        start = clock();
        method2();
        end = clock();
        fprintf(fp, "時間:%.10f\n", (double)(end-start)/CLOCKS_PER_SEC);
        fclose(fp);
        return 0;
}

void method1(){
        int i =0;
        int sum = 0;
        for(i = 0; i < 10000000; i++){
                printf("%d\n", i);
                sum += i;
        }
}

void method2(){
        int i = 0;
        int sum = 0;
        for(i = 0; i < 10000000; i++){
                sum += i;
        }
}

これの結果は一目瞭然で
method1は
時間:2.1418420000
method2は
時間:0.0278430000

printfがないと0.03秒以下なのに
printfがあると2秒以上かかるという結果に。。。
最適化を行わずにコンパイルしたので、ループ処理はどちらも行っています。
(余談ですが最近のコンパイラは異常に賢いので最適化するとmethod2ではコンパイル時に
勝手に計算して値を作成するため実行時にはループしません。)

というわけで実行速度を重視する場合に正規のモジュールを作成する際は
printfを削除していきましょう。