かざいむ日誌

IT関係で知ったことなどを記事としてあげていきます。内容に不備や質問などあればぜひコメントをよせてください。

Pythonでurllib.requestでmethodを渡すとunexpected keyword argument 'method'でエラーになる。

PythonJSONファイルをPOSTする処理を書いたが、以下のエラーで異常終了。

request = urllib.request.Request(url, data=json_data, method="POST", headers=headers)

TypeError: __init__() got an unexpected keyword argument 'method' 

 ググったらPython3ではmethodは良しなにしてくれるから入れなくてもいいらしい。

stackoverflow.com

Moodleをインストールする。

MoodleをCentOS7にセットアップした。

基本はこのサイトに従って、モジュールの更新、PHP5.6のインストールMariaDBのインストールMoodleのインストールを行えば完了。

Moodle: CentOS7にMoodle3.2をインストール

 

Moodleのインストールはモジュールを圧縮ファイルでダウンロードして展開しようと思ったけどなんかうまく行かなかったので、GitでCloneの方を試したら結構すんなりできた。

Git for Administrators - Moodle

f:id:name_untitled:20170426164244p:plain

 

次は使い方をもう少し勉強する。

Docs

ChatBotで位置情報をとって、それをGoogle Map上に表示。

活動でデモをする必要があり、チャットボットいろいろと作ろうと思っている。

今日作ったのは、送信された位置情報を受け取って、DBに保存し、Webサイト上でそれを見れるようにするというもの。簡単なChatBotを作成している想定で話をします。

手順としては、

1.ユーザーから受け取ったメッセージから位置情報を抜き出す

2.DBにその情報を保存

3.Google Map上に表示

 

1.ユーザーから受け取ったメッセージから位置情報を抜き出す

受け取ったメッセージから位置情報をとるのは下記のようにします。

 lat = event.message.attachments[0].payload.coordinates.lat
lng = event.message.attachments[0].payload.coordinates.long

 

stackoverflow.com

2.DBにその情報を保存

このChatBotではMongoDBをMongoose経由で利用しているのですが、位置情報の入れ方がよく分からず時間がかかってしまいました。

var location;
var locationSchema = mongoose.Schema({
title: String,
coordinates: [Number]
}); 

 

new location(
{"title": title
, "coordinates":[lat,lng]}).save();

こんな感じで定義して、保存したら問題なく行けました。

 

3.Google Map上に表示

地図を表示するには、、、

あとから見つけましたがGoogleのサイトが丁寧で分かりやすかったです。

Adding a Google Map with a Marker to Your Website  |  Google Maps JavaScript API  |  Google Developers

吹き出しを追加したい場合は以下のサイトを参考に。

http://www.tam-tam.co.jp/tipsnote/javascript/post7755.html

 

JSONデータを取得する処理久しぶりに書いて、はてさてとなったので今どきの書き方のリンクも貼りつけます。

developer.mozilla.org

 

色々とあさってたらこういうGitHubのレポジトリが。ChatBotのコードを丁寧に説明してくれている。また読んでみよう。

github.com

FirebaseでトランプのドボンのWebアプリを作る。(レイアウト)

色々と手を出し過ぎなんだけど、今FirebaseでWebアプリを作ろうとしている。トランプのドボンが出来るアプリ。

ステップはこんな感じと思う。

1.FirebaseのWebアプリのサンプルの確認

2.Firebaseから手札のデータを取得して表示する

3.選んだ手札をFirebaseDBに登録する

4.新しいゲームのボタンを押したら、カードをランダムに配る

5.順番に手札を台札に乗せる

6.手札が出せる札かチェックする

7.得点計算をする

 

ちょっとずつ更新する予定

1.FirebaseのWebアプリのサンプルの確認

 

Firebaseのチャットアプリのサンプルの動作や作りを一通り眺める。

Firebase Web Codelab

 

2.Firebaseから手札のデータを取得して表示する

これはコードを眺めて対応するところをコピペした。

Firebaseの方で手札データを登録して、それを表示させる。

 

3.選んだ手札をFirebaseに登録する

ここで、bindしないとつらそうな気がしたので、ちょっとググる

firebase.googleblog.com

これを参考にVueを導入してみた。まずは、クライアントでrequireを使うので、browserifyが必要らしい。

>npm install -g browserify

>npm install --save promise-polyfill

>browserify ./public/scripts/main.js -o ./public/scripts/bundle.js

>firebase serve

で、これ以降はmain.jsではなく、browserifyで生成したbundle.jsをindex.htmlで読み込んで使う。

arata.hatenadiary.com

動かしてみたがエラー。メッセージからすると、エレメントが見つからないからエラー。2行目は分かりにくいけどたぶん関係ない。

[Vue warn]: Cannot find element: #cards

[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
(found in <Root>)

stackoverflow.com

確認して、bundle.jsを作り直して、もう一度表示。

bundle.js:2302 [Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build. (found in <Root>) 

やっぱりまだエラー。

これVueのバージョンアップに絡んでいそうなので、ちょっとここでいったんストップ。同じ感じで表示だけ行う処理を組み込んだ。

f:id:name_untitled:20170414151028p:plain

 

2017/04/23 更新

Vueはちょっとお預け。

データはまずは以下の形で作っている自分のカードを表示して、こんな感じにした。

root

┣messages
┣current

┃┣mark

┃┗num

┗cards

 ┗id

┣mark

 ┃┗num

 ┣id

 

手札を出す=cardsから1つ削除&currentを更新。

main.js →browserify → bundle.js

this.cardsRef.child(key).remove().then(function() {
    // Clear message text field and SEND button state.
    Dobon.resetMaterialTextfield(this.messageInput);
    this.toggleButton();
    }.bind(this)).catch(function(error) {
        console.error('Error writing new message to Firebase Database', error);
});

このコードでカードデータを削除。

Dobon.prototype.loadCards = function() {
    // // Reference to the /messages/ database path.
    this.cardsRef = this.database.ref('cards');

    // Make sure we remove all previous listeners.
    this.cardsRef.off();

    var remCard = function(data) {
        var val = data.val();
        var elm = document.getElementById(data.key);
        elm.parentNode.removeChild(elm);
    }.bind(this);
    // Loads the last 12 messages and listen for new ones.
    var setCard = function(data) {
        var val = data.val();
        this.displayCard(data.key, val.mark, val.num);
    }.bind(this);
    this.cardsRef.limitToLast(12).on('child_added', setCard);
    this.cardsRef.limitToLast(12).on('child_changed', setCard);
    this.cardsRef.limitToLast(12).on('child_removed', remCard);
};

上記はDB変更時のイベントの設定。元々のサンプルはchild_added, child_changedだけだけど、child_removedを追加。

 

次に、FirebaseのFunctionを追加する。

index.js

exports.makeUppercase = functions.database.ref('/cards/{pushId}')
    .onWrite(event => {

    // Only edit data when it is deleted
    if *1
        || event.data.exists()) {
        console.log('end trigger');
        return;
    }
    // Grab the current value of what was written to the Realtime Database.
    var mark = event.data.previous.val().mark;
    var num = event.data.previous.val().num;
    event.data.ref.parent.parent.child('current').child('mark').set(mark);
    return event.data.ref.parent.parent.child('current').child('num').set(num);
});

previousにあって、今のデータにない=削除されたデータ。

で、上記の処理でノードを更新。

Realtime Database Triggers  |  Firebase

このサイトがとても詳しく書かれていてありがたい。

Read and Write Data on the Web  |  Firebase

 

2017/04/24 追記

4.新しいゲームのボタンを押したら、カードをランダムに配る

カードデータを作成して、ランダムにシャッフルする処理は以下のような感じ。

ランダムに並べ替えるアルゴリズムがあるそうで助かりました。

h2ham.net

このサイトが見ててきれい。

Fisher–Yates Shuffle

function getCards(){
    var marks = ["spade","clover","heart","diamond"];
    var cards = [];
    for(var i = 0; i < marks.length; i++){
        for(var j = 1; j <= 13; j++){
            var node = {"mark":marks[i], "num": j};
            cards.push(node);
        }
    }
    return cards;
}
function shuffle(array) {
    var n = array.length, t, i;

    while (n) {
        i = Math.floor(Math.random() * n--);
        t = array[n];
        array[n] = array[i];
        array[i] = t;
    }

    return array;
}

var test_array = getCards();
var cards =shuffle(test_array);
console.log(cards.length);
console.log(cards);

 2017/04/25 追記

4.新しいゲームのボタンを押したら、カードをランダムに配る

の続き。

Firebaseで手札を配る際に、RESTでGETしたら手札を配るようにしたいため、FirebaseのFunctionsという機能を利用する。これはREST APIっぽいことが出来るみたい。index.jsでの書き方はこんな感じ。

exports.date = functions.https.onRequest((req, res) => {
 
// ...
});

ここで、dataがHTTPのTriggerの名前。reqからJSONデータを取ることもできるらしい。詳しくは公式をご覧ください。

HTTP Triggersの書き方はこんな感じ。

HTTP Triggers  |  Firebase

Databaseへの登録の書き方はこんな感じ。

Read and Write Data on the Web  |  Firebase

var functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

 

exports.newgame = functions.https.onRequest((req, res) => {
    var getCards = function(){
        var marks = ["spade","clover","heart","diamond"];
        var cards = [];
        for(var i = 0; i < marks.length; i++){
            for(var j = 1; j <= 13; j++){
                var node = {"mark":marks[i], "num": j};
                cards.push(node);
            }
        }
        return cards;
    };
    var shuffle = function(array) {
        var n = array.length, t, i;

        while (n) {
            i = Math.floor(Math.random() * n--);
            t = array[n];
            array[n] = array[i];
            array[i] = t;
        }
        return array;
    };
    var newGame = function(cards){
        for(var i = 0; i < cards.length; i++){
            // A card entry.
            var cardData = {
                mark: cards[i].mark,
                num: cards[i].num
            };

            var newCardKey = admin.database().ref().child('cards').push().key;

            var updates = {};
            updates['/cards/' + newCardKey] = cardData;
            admin.database().ref().update(updates);
        }
    };
    newGame(shuffle(getCards()));
    res.send(200);
});

JSっぽくない書き方になってるのでどう直したらいいか教えてください!

これをdeployして、GETリクエストを投げたらこんな感じでデータが出来ました。

f:id:name_untitled:20170426004141p:plain

うれしい、、、。

 

2017/04/27 追記

カードを配る前に今のカードを全部削除する処理を追加した。

上の処理を1行追加してこんな感じ。

admin.database().ref().child('cards').remove();
newGame(shuffle(getCards()));
res.send(200);

removeで配下のものを全部削除してくれるらしい。

 

2017/04/28 追記

ユーザーごとにカードを配る処理を実装したが、アクティブユーザー一覧が取れなさそうなので、ゲームの流れとしては、開始を呼びかける→他のユーザーが参加表明→ゲーム開始という流れにして、表明のタイミングでUIDを登録するようにする。

参加表明はクライアントからIDを送る。ちょっと微妙な気もするが、HTTP Requestのトリガーではuidが取れなさそう。

Dobon.prototype.enrollGame = function() {

    this.playersRef = this.database.ref('players');
    // Make sure we remove all previous listeners.
    var user = firebase.auth().currentUser;
    var data = {
        uid: user.uid
        ,name: user.displayName
    };
    this.playersRef.push(data);
};

ゲーム開始時の処理も修正。カードをユーザーごとに配る。

exports.newgame = functions.https.onRequest((req, res) => {
  var getCards = function(){
    var marks = ["spade","clover","heart","diamond"];
    var cards = [];
    for(var i = 0; i < marks.length; i++){
      for(var j = 1; j <= 13; j++){
        var node = {"mark":marks[i], "num": j};
        cards.push(node);
      }
    }
    return cards;
  };
  var shuffle = function(array) {
    var n = array.length, t, i;

    while (n) {
      i = Math.floor(Math.random() * n--);
      t = array[n];
      array[n] = array[i];
      array[i] = t;
    }
    return array;
  };
  var newGame = function(cards){
    //TODO: get list of players
    admin.database().ref().child('players').once('value').then(function(snapshot) {
      //TODO: deal cards to each players(hands)
      var users = snapshot.val();
      for(key in users){
        var user = users[key];
        for(var j = 0; j < 5; j++){
          var last = cards.pop();
          var cardData = {
            mark: last.mark,
            num: last.num
          };
          admin.database().ref().child('hands').child(user.uid).child('cards').push(cardData);
       }
      }

      //TODO: first card will be upcard
      cards.pop();
      //TODO: the rest will be talon
      var card = cards.pop();
      while(cards != undefined){
        var cardData = {
          mark: card.mark,
          num: card.num
        };

         var newCardKey = admin.database().ref().child('cards').push().key;
         var updates = {};
         updates['/cards/' + newCardKey] = cardData;
         admin.database().ref().update(updates);
        card = cards.pop();
      }
    });
  };
  admin.database().ref().child('cards').remove();
  newGame(shuffle(getCards()));
  res.send(200);
});

ユーザーIDのJSONが配列じゃなかったから結構はまってしまったし、探し方が悪かった。やりたいことをきっちり言語化すべきだった。ここだと、JSONのキーを取得して、そこからそれぞれの要素を取得する、だった。

for(key in users){
}

でできちゃうんだ、、、。

 

2017/05/05 追記

ちょっと飽きたのでレイアウトをいじることにした。

HTML見てると、classにmdl~とかいう記述があったけど、どうもそれは、Material Design LiteというGoogle謹製のライブラリらしい。これを使うと、WebでもAndroid同様に面とインクのメタファーに倣ったデザインができるらしい。ちょっと触ってみたけど、影を出すのが精いっぱいでリップルは出せなかった。とりあえず、台とカードは出た。

f:id:name_untitled:20170505210314p:plain

クラスを複数指定することによりデザインをきれいに出来る。でもそもそもCSSが全然わかってないし、MDLもよく分かってないのでなんでこんな書き方になるのか説明できない。1行めはグリッド表示用、2行目が各カードの親DIVのクラス。

<div class="mdl-grid">

<div class="mdl-play-card mdl_supporting-text mdl-color--white mdl-shadow--2dp">

youtu.be

getmdl.io

概要や使い方は上記で。

色の指定はこのサイトがいろいろと見本を載せてくれていて良い。

blog2.gods.holy.jp

 

2017/05/27 追記

カードを引く処理を以下の流れで想定していたがうまく行かなかった。

JavascriptでuidをPOSTして、Functionsでuidを受け取って、カードを1件取得して、uidをキーにカードをpushし直す。

問題は2つあった。1.クロスドメインNG。2.1件データが取得できない。

まず1.についてはHostingとFunctionsのドメインが異なるため。ただGETでは問題ないため、メソッドをGETに変更し、クエリにuidを含めるよう変更。
2.については何か書き方間違えているかもだけど分からないからとりあえず力技でしのいだ。

exports.drawcard = functions.https.onRequest((req, res) => {
    admin.database().ref().child('cards').once('value').then(function(snapshot) {
        var allData = snapshot.val();
        var keys = Object.keys(allData);
        var val = allData[keys[0]];
        var uid = req.query.uid;
        var data = {"mark": val.mark, "num": val.num};
        admin.database().ref('hands').child(uid).child('cards').push(data);
        admin.database().ref('cards').child( keys[0]).remove();
    });

    res.status(200).send();
});

developer.mozilla.org

www.aipacommander.com

 

2017/06/18 追記

8を出したときにマークを選ぶダイアログを表示させるようにした。

Dialogはまだ一般的じゃないみたいだけどChrome限定だから良しとする。

getmdl.io

マークを選ぶのはラジオボタンから。

www.tutorialspoint.com

最近はブラウザでdialogがサポートされるようになってきている。

HTML DOM Dialog showModal() Method

 

ドキュメントとかでPromiseというのがでてきたけど、まだちょっと内容が分かってない。

developer.mozilla.org

 

まだ機能足りてないけどとりあえずGitHubに上げました。

github.com

*1:!event.data.previous.exists(

教育サイト用のソフトウェア Moodle。

この間Moodleというソフトを知った。

https://moodle.org/

これはLinux上でオンラインコースのWebサイトを作成できる、CMSソフトらしい。Linux上に、Apache2、DB(MySQLMariaDB、PostGresDB)、PHPを入れて、Moodleのモジュールを入れると出来るとか。

f:id:name_untitled:20170414125510p:plain

機能を色々と見てみたけどなかなか良い感じ。

テストだけじゃなく、学習も出来るみたいで、字幕付きのVideoを入れたりもできる。またランダムな出題も出来るみたいで、一斉テストでも使えそう。

こんなアプリがあるのね。

今まで全く知らなかったけどこんなアプリがあるんだと思った話。

マチコミというAndroidiPhoneアプリがあって、幼稚園とかと保護者の情報共有をするアプリ。

f:id:name_untitled:20170411114940p:plain

多分、元々Webサービスがあって、それをionicとかのフレームワークで移植してアプリとして公開してるんだと思う。

ユーザーは施設運営者と保護者の2パターン。施設運営者は事前にマチコミに登録が必要と思われる。で、学年ごととかでグループを作成する。保護者はユーザー登録をした後で、施設運営者から学年ごとに振られるIDを教えてもらい、こどもの教室を登録する。

SNS、ニュースなども見ることが出来て、広告が入っている。

収入は広告らしい。利用者はアンケートに答える必要があるらしく、これもデータを売ってたりするのかな。意外とダウンロードされててびっくり。

クローズなコミュニティ向けのサービスって意外と需要があるのね。

Androidでテストを導入したい。Contextの受け渡し。

Androidでちっちゃいアプリを作っている。機能を追加する前にユニットテストを組み込みたいと思ってお勉強中。

Googleのサンプルに従って導入しているところだけどデータアクセスのところでつまづいた。辞書機能を作るのにSQLiteを使っているのだが、DBアクセスにContextが必要。ただDBアクセス部はコードを分離したい、、、。ちょっと探してみると2つくらい選択肢があるっぽい。1、Dagger2というDIライブラリを使う。2、Contextをアプリ内で参照できるようにする。

1、を選ぶと収集がつかなくなりそうだったので、2、のContextをアプリ内で参照できるように修正。テスト用のコードとリリースとで実装を分けて、テストしやすくなりそうな気がする。

qiita.com

 

ありがとうございました。