4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Excelのシートの内容をjavascriptでjsonに変換してpug用のファイルとして出力したメモ

Posted at

動機

ホームページ作成にpugを使うようになったが、表を作成するためのjsonを手打ちするのが面倒になってきた。
Excelで作成した表をコマンド一発で取り込めるようにする。

動作環境

  • windows10
  • Excel 2016 MSO(16.0.9001.2080) 32bit
  • vagrant1.9.7
  • virtualbox5.1.26
  • ubuntu-16.04
  • Docker version 17.09.0-ce, build afdb6d4
  • docker-compose version 1.17.1, build 6d101fb

フォルダ構成

- bin
  - excel-to-data.sh
- docker
  - excel
    - app.js
    - Dockerfile
  - docker-compose.yml
- data
  - chart.js
- htdocs
  - pug
    - rulebook.pug
    - includes
      - common
        - chart.pug

最終的なソース

docker設定

docker/excel/Dockerfile
FROM node:9.5.0

# コンテナ上の作業ディレクトリ作成
WORKDIR /app

# 後で確認出来るようにpackage.jsonを作成
RUN npm init -y

# エクセルを扱う
RUN npm i -D exceljs
docker/excel/app.js
const fs = require('fs');
const excel = require('exceljs');

(async _ =>{
  // コンテナ上のパスを記述
  const src = `/app/data/chart.xlsx`;
  const dest = `/app/dest/chart.pug`;

  // エクセルの内容を取得
  const sheets = await getSheets(src);

  // 書き出すファイルの内容
  let content = ``;

  // シート名を変数名、変数の値をレコードとして、pugファイルの内容を作成
  sheets.forEach(sheet=>{
    content += `- ${sheet.name}=${JSON.stringify(sheet.records)}\n`;
  });

  // ファイルに書き出し
  fs.writeFile(dest, content, (err)=>console.log(err));

  console.log(`Excel: ${src} to Pug: ${dest} `);
})();

/**
 * エクセルファイルを読み込み、シートごとのデータの配列を返す
 * @param {*} filepath 
 * @returns シートごとのデータの配列
 */
async function getSheets(filepath){
  const dataArr = [];
  const workbook = await readWorkbookAsync(filepath);
  workbook.eachSheet(function(worksheet, sheetId){
    const data = getSheetData(worksheet);
    dataArr.push(data);
  });
  return dataArr;
}

/**
 * シートのデータを取得
 * @param {*} sheet 
 * @returns {name:"シート名", records: [1行目をプロパティ名としたオブジェクトの配列]}
 */
function getSheetData(sheet){
  let columIndex=1;
  let rowIndex=1;
  const headerRow =[];

  // 1行目をヘッダとする
  while(sheet.getCell(1, columIndex).value !== null){
    headerRow.push(sheet.getCell(1, columIndex++).value);
  }

  const columnCount = headerRow.length;
  const dataArr = [];

  // 二行目からのデータを取り込み
  rowIndex++;
  while(sheet.getCell(rowIndex, 1).value !== null){
    const data ={};

    // ヘッダをプロパティ名として値を設定
    for(let col = 0; col < columnCount; col++){
      data[headerRow[col]] =sheet.getCell(rowIndex, col + 1).value;
    }

    dataArr.push(data);
    rowIndex++;
  }
  
  return {name: sheet.name, records: dataArr};
}

/**
 * エクセルファイルを非同期で読み込む
 * @param {*} filePath 
 */
function readWorkbookAsync(filePath){
  return new Promise((resolve, reject) => {
    const workbook = new excel.Workbook();
    workbook.xlsx.readFile(filePath).then(()=>{
      resolve(workbook);
    });
  });
}
docker-compose.yml
version: '3'
services:
  # エクセル取込
  excel:
    build: ./excel
    volumes:
      - ./excel/app.js:/app/app.js
      - ../data:/app/data
      # pugの変数定義ファイルに書き出し
      - ../htdocs/pug/includes/common:/app/dest

実行

bin/excel-to-data.sh
#!/bin/bash

# このシェルスクリプトのディレクトリの絶対パスを取得。
bin_dir=$(cd $(dirname $0) && pwd)

cd $bin_dir/../docker && docker-compose run excel /bin/bash -c "node app.js"

エクセルファイル

  • data/chart.xlsxを読み込む
  • シート名を変数名とする
  • 1行目をヘッダとして、オブジェクトのプロパティとする。

2018-02-10.png

出力結果

htdocs/pug/includes/common/chart.pug
- classList=[{"name":"ビッグ","image":"big.png","description":"体が大きいことを表すクラス。恵まれた体格を活かしたアビリティを習得できる。チビと同時に選ぶことはできない。"},{"name":"チビ","image":"tibi.png","description":"体が小さいことを表すクラス。 小器用な立ち回りを活かしたアビリティを習得できる。 ビッグと同時に選ぶことはできない。"},{"name":"オトナ","image":"otona.png","description":"オトナの立ち位置であることを表すクラス。 経験に裏打ちされたアビリティを習得できる。 25歳以上でなければ取得できない。"},{"name":"ニューエイジ","image":"new.png","description":"10年前の災害により、変異を起こしたことを表すクラス。 超能力のアビリティを習得できる。 15歳以下でなければ取得できない。"},{"name":"キズモノ","image":"kizu.png","description":"消えない傷を受けてしまったことを表すクラス。 その不利を補い生きていくためのアビリティを習得できる。 このクラスを選択した場合、部位ダメージを1受けている状態でスタートする。 この、部位ダメージの入っている「身体部位」を<キズ>と呼ぶ。 <キズ>はいかなる手段でも回復しない。"},{"name":"センシ","image":"sensi.png","description":"戦闘が得意なことを表すクラス。 戦闘に必要なアビリティを習得できる。"},{"name":"シノビ","image":"sukauto.png","description":"偵察・調査が得意なことを表すクラス。 探索を有利にするアビリティを習得できる。"},{"name":"ハンター","image":"hunter.png","description":"狩りが得意なことを表すクラス。 飛び道具や罠を用いたアビリティを習得できる。"},{"name":"ハカセ","image":"hakase.png","description":"物知りであることを表すクラス。 知識を活かしたアビリティを習得できる。"},{"name":"ショクニン","image":"syokunin.png","description":"手先が器用であることを表すクラス。 モノづくりに関するアビリティを習得できる。"},{"name":"ホープ","image":"kibou.png","description":"皆の希望である表すクラス。 希望を持つことで運命を変えるアビリティを習得できる。"},{"name":"ママ","image":"mama.png","description":"おかんな立ち位置を表すクラス。 周囲に活力を与えるアビリティを習得できる。"}]

pugで使用する例

htdocs/rulebook.pug
extends includes/common/_layout
block title
  include includes/common/variables
  include includes/rulebook/variables
  title= title

block headjs
  include includes/common/chart
block body
  body.nohero
    header
      include includes/common/header
    main#main
      section.content
        .container
          h1 終末旅行TRPG 基本ルールブック
          include includes/rulebook/first
          h2 キャラクター
          p あなたの分身となる旅人の作り方
          h2 クラス
          .listB
            .container
              each obj in classList
                article
                  a(href="#")
                    .image(style="background-image: url('../assets/images/" + obj.image + "');")
                    .text
                      h2= obj.name
                      p= obj.description
    include includes/common/footer

参考

[Excel] Excel で JSON データを読み込む
Node.jsでエクセルファイルを読み込む
【JavaScript】Excelで読み込んだデータを文字化けさせずにCSVで書き出す
Node.jsでmerged cellしたり色塗ったりしたExcelを出力する

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?