Dr.WalletのデータからCSVを作る(解説編)


「実践編」からの続き。

2016/11/03:Chrome拡張機能ができました。期間を指定して一括取得できます。

Chrome拡張機能でDr.WalletのデータからCSVを作る

Dr.WalletにはCSV出力(エクスポート)機能がないので、自分でやってしまおうという話です。
前回紹介したプログラムの解説編です。あんまり解説してません。

ソースを貼っておきますので、自由に改変して使いやすいようにしてどうぞ。当たり前だけど商用利用とかしないでね。他のウェブサイトにまるまるコピペするのも悲しいのでやめてね。
転載する場合は、このページを貼ってくださるとニッコリします。

Sponsored Link

ただしjavascript書いたのも久しぶりなので、基本的にクソコードです。「undefined?よくわかんね。」レベルなので変なところは多めに見てね。最近VBAerなので、にじみ出ているかもしれません。
※支出と収入のラベルがあべこべになっていましたので、修正しました。

<textarea id="jinput" placeholder='{ "aaData":[[ ... というようなDr.Walletのデータをペーストして下さい。'></textarea><br>
<input id="outputsColumnName" value="true" type="checkbox" checked="checked"/>列名を出力する<br/>
<input id="splitsInOut" value="true" type="checkbox" checked="checked"/>収支は別の列に出力する(割引(マイナスの支出)とかのデータも、よくわからないのでそのまま出力します。)<br/>
<input id="outputsItems" value="true" type="checkbox" checked="checked"/>明細を出力する(明細を出力しないの場合、複数カテゴリーがあるときは「食費/日用雑貨」という形になります。)<br/>
<input id="outputsTransfers" value="true" type="checkbox" checked="checked"/>振替を出力する<br/>
<input value="CSVにしてみる" onclick="joutput.value = dw_parse()" type="button"><br>
<textarea id="joutput" placeholder="ここにCSVが出力されます。"></textarea></p>
<script>var dw_parse = function() {
    //unicodeをデータをデコードして、JSONをパースする
    var data = JSON.parse( uDecode( document.getElementById('jinput').value) );
    
    //明細に突合させるためのレシートデータ
    var receiptList = {};
    
    //CSVを作るための連想配列オブジェクト
    var csvData;

    //csvDataのリスト
    var csvList = [];

    //処理対象の年月
    var currentYM;
    
    //JSONデータのaaData (レシートデータ)
    for(var i in data.aaData){
        
        //transferのデータが、当月以外も取得されているので、
        //当月のデータのみCSVにする。
        //receiptが存在せず、transferしかない場合は、たぶんうまく動かない。
        if(!currentYM){
            currentYM = data.aaData[i][0].substr(0,7);
        }else if(currentYM != data.aaData[i][0].substr(0,7)){
            continue;
        }

        if(data.aaData[i][8] == 'receipt') {
            var receiptData = new Object();
            receiptData['date'] = data.aaData[i][0];
            receiptData['store'] = data.aaData[i][1];
            receiptData['amount'] = data.aaData[i][2];
            receiptData['category'] = data.aaData[i][3];
            receiptData['assets'] = data.aaData[i][4];
            receiptData['sortkey'] = data.aaData[i][5];
            receiptData['isexpense'] = data.aaData[i][6];
            
            receiptList[data.aaData[i][5]] = receiptData;
        }else if(data.aaData[i][8] == 'transfer'){
            if(document.getElementById('outputsTransfers').checked) {
                csvData = new Object();
                csvData['date'] = data.aaData[i][0];
                csvData['store'] = data.aaData[i][1];
                csvData['item'] = '';
                csvData['amount'] = data.aaData[i][2];
                csvData['category'] = data.aaData[i][3];
                csvData['assets'] = data.aaData[i][4];
                csvData['sortkey'] = data.aaData[i][5];
                csvData['isexpense'] = (data.aaData[i][6] == null) ? '' : data.aaData[i][6];
                
                csvList.push(csvData);
            }
        }
    };
    
    if(document.getElementById('outputsItems').checked) {
        //JSONデータのtransactions (明細データ)
        for( var t in data.transactions){
            for( var u in data.transactions[t]){
            
                csvData = new Object();
                csvData['date'] = receiptList[t]['date'];
                csvData['store'] = receiptList[t]['store'];
                csvData['item'] = (data.transactions[t][u]['name'] == null) ? '' : data.transactions[t][u]['name'];
                csvData['amount'] = data.transactions[t][u]['amount'];
                csvData['category'] = data.transactions[t][u]['category_name'];
                csvData['assets'] = receiptList[t]['assets'];
                csvData['sortkey'] = u;
                csvData['isexpense'] = data.transactions[t][u]['is_expense'];
            
                csvList.push(csvData);
            };
        }
    }else{
        //明細不要の場合はreceiptListをそのままcsvDataに追加
        for( r in receiptList){
            csvData = new Object();
            csvData['date'] = receiptList[r]['date'];
            csvData['store'] = receiptList[r]['store'];
            csvData['item'] = '';
            csvData['amount'] = receiptList[r]['amount'];
            csvData['category'] = receiptList[r]['category'].replace(/<br>/g, '/');
            csvData['assets'] = receiptList[r]['assets'];
            csvData['sortkey'] = receiptList[r]['sortkey'];
            csvData['isexpense'] = receiptList[r]['isexpense'];
            
            csvList.push(csvData);
        }
    }
    
    //ソートしてみる
    csvList.sort(function(a, b){
        if(a['date'] < b['date']) {return -1;}
        if(a['date'] > b['date']) {return  1;}
        if(a['sortkey'] < b['sortkey']) {return -1;}
        if(a['sortkey'] > b['sortkey']) {return  1;}
    });
    
    var ret = '';
    
    //列名の出力
    if(document.getElementById('outputsColumnName').checked){ 
        ret += '日付,口座,店名,';
        if(document.getElementById('outputsItems').checked){ 
            ret += '品名,';
        }
        ret += 'カテゴリー,';
        if(document.getElementById('splitsInOut').checked){ 
            ret += '支出金額,収入金額';
        }else{
            ret += '収支,金額';
        }
        ret += '\r\n';
    }
    
    //CSV出力
    for( var c in csvList ){
        ret += escapeComma(csvList[c]['date']) + ',';
        ret += escapeComma(csvList[c]['assets']) + ',';
        ret += escapeComma(csvList[c]['store']) + ',';
        if(document.getElementById('outputsItems').checked){ 
            //明細
            ret += escapeComma(csvList[c]['item']) + ',';
        }
        ret += escapeComma(csvList[c]['category']) + ',';
        
        if(document.getElementById('splitsInOut').checked){ 
            //収支を別の列に出力
            if(csvList[c]['isexpense'] === true){
                ret += escapeComma(csvList[c]['amount']) + ',';
            }else if(csvList[c]['isexpense'] === false){
                ret += ',' + escapeComma(csvList[c]['amount']);
            }else{
                //振替
                ret += escapeComma(csvList[c]['amount']) + ',' + escapeComma(csvList[c]['amount']);
            }
        }else{
            //収支の別を出力
            if(csvList[c]['isexpense'] === true){
                ret += '支出';
            }else if(csvList[c]['isexpense'] === false){
                ret += '収入';
            }else{
                ret += '';
            }
            ret += ',' + escapeComma(csvList[c]['amount']);
        }
        
        ret += '\r\n';
    }
    
    return ret;
};
//unicode表現の文字列をデコードする
var uDecode = function(str) {
    return str.replace(/\\u([a-fA-F0-9]{4})/g, function(a, b) {
        return String.fromCharCode(parseInt(b, 16));
    });
};
//カンマを全角にする
var escapeComma = function(str){
    return String(str).replace(',', ',');
};
</script>

Sponsored Link

  • JSONは、aaDataとtransactionsに大きく分かれています。
    • aaDataはレシートデータと口座連携データ。
      • 2重配列で、コアのデータは引用符付きのCSVのようです。
      • CSVなのでどういう意味のデータかは想像する必要がありました。間違ってたらごめんね!
      • 「分析編」の通り、当月以外の口座連携データが含まれています。なぜ…。
    • transactionsは、aaDataに対応する明細データ。
      • こちらはaaDataのコアデータの一つの項目がキーとなっており、突合できるようになっています。
      • 深部まで潜っていくと、こちらは連想配列になっています。なぜこっちはCSV…。
  • javascriptで複数項目でソートできるの初めて知った。気持ちいい!
  • CSV出力の部分、ごり押しです。ごめんね!

実践編はこちら↓です。ブラウザ上で動作します。

Dr.WalletのデータからCSVを作る(実践編)


以上!