命名って難しい

変数、関数、クラスなどなど実装より命名に毎回悩むタイプの人間による技術についてのメモ。

弊社現場レベルで使えるであろう簡単な社内FAQページを作った

経緯

新しい施策を社内で行うと問い合わせが増える。 その問い合わせがある程度蓄積してきたのでFAQを立てようと思い作りました。 あくまで弊社現場レベルなのでレベルは高くありません。

環境

  • Windows Server + IIS
  • 社内イントラのサーバーに間借りして設置
  • 社内イントラ関連ファイル(html/css/jsなど)のフォルダは共有されており、許可された者のみ編集可能
  • なんでもExcelで管理しているからメンテはExcelでできるとよい。
    • 弊社現場レベルで使えるのはExcelくらい

作ったもの

  1. FAQページ(Bootstrap4 + jQuery 3.3 + Vue.js)
  2. ExcelのFAQ台帳(CSVを吐くマクロ付き)
  3. PowerShellスクリプト(CSVjson形式に変換)

まずはFAQページを作り、Vue.jsでうまいことデータを表示し、
メンテに気を使いつつ、Vue.jsにわたすjsonを作れるExcelマクロとPowerShellを組みました。

全体像

こんな感じのExcelにFAQを追加して更新すると。。。 f:id:NotShown:20180820221646p:plain

FAQページがこうなります。 f:id:NotShown:20180820222047p:plain

こんな環境を作りました。

ExcelのFAQ台帳

  • FAQの源流
  • 保存前タイミングでCSVを出力、そのCSVjsonに変換するPowerShell起動
    • マクロボタンとかを用意するとメンテする人が押し忘れる可能性があるので保存前
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)

    Dim sheet As Worksheet
    Dim name As String

    Dim csv As String: csv = ThisWorkbook.Path & "\qna.csv"
    Dim ps As String: ps = ThisWorkbook.Path & "\convertQnACSV2JSON.ps1"
    
    
    Set sheet = Worksheets("Q&A")
        
        ' ファイル名
        name = ThisWorkbook.Path & "\qna.csv"

        ' シートを別のワークブックにコピーする
        sheet.Copy

        ' コピーしたワークブックを上書き保存
        Application.DisplayAlerts = False
        ActiveWorkbook.SaveAs Filename:=name, FileFormat:=xlCSV
        Application.DisplayAlerts = True

        ' コピーしたワークブックを閉じる
        ActiveWorkbook.Close SaveChanges:=False

    strCommand = "Powershell -File """ & ps & """"
    Set WshShell = CreateObject("WScript.Shell")
    WshShell.Exec (strCommand)
End Sub

PowerShellスクリプト

Excelブックマクロから出力されたCSVjson形式に変換します。

FAQデータは以下の構造として捉えています(わかりにくい)

  • ジャンル別Q&Aのリスト
    • 単一Q&A
      • Question
      • Answer
      • 参考リンクのリスト
        • タイトル
        • URL
<#
 # Q&AコンテンツのCSVをJson形式に変換する
 
 CSVレイアウトは以下
 | genre | id | q | a | title1 | url1 | title2 | url2 | ~ | url5 |
 
 #> 
Push-Location -Path (Split-Path $MyInvocation.MyCommand.Path)

$csv = Import-Csv ".\qna.csv" -Encoding Default

# スコープはjson全体
$json = @{content=(New-Object System.Collections.ArrayList)}

# スコープはジャンル別のグループ
foreach($gen in ($csv | group "genre")){

    # スコープは各Q&A
    $jgen=@{genre=$gen.Name;qnas=(New-Object System.Collections.ArrayList)}
    foreach($qna in $gen.Group){

        # Q&A参考リンクは列でタイトルとURLを表現している。
        # 5つまでのタイトルとURLの組み合わせを配列に変換する。
        $tmpLinks = @(
            @{title=$qna.title1; url=$qna.url1},
            @{title=$qna.title2; url=$qna.url2},
            @{title=$qna.title3; url=$qna.url3},
            @{title=$qna.title4; url=$qna.url4},
            @{title=$qna.title5; url=$qna.url5}
            )

        # リンクは自由入力欄のため、ある場合のみリストに加える
        $jqna = @{id=$qna.id;q=$qna.q;a=$qna.a;keywords=$qna.keywords;links=(New-Object System.Collections.ArrayList)}
        foreach($link in $tmpLinks){
            if($link.url -ne ""){
                # タイトルなしの場合URLにする
                if($link.title -eq ""){
                    $link.title = $link.url
                }
                $jqna.links.Add($link)
            }
        }
        $jgen.qnas.Add($jqna)
    }
    $json.content.Add($jgen)
}

# 出力する
Set-Content qna.json ( ConvertTo-Json $json -Depth 6 ) -Encoding UTF8

FAQページ

見た目は全体像の通り、FAQはアコーディオンで開きます。

なお、こちらはBootstrapのフリーのFAQテンプレートを流用して作っており、参考URLと検索欄の他はほぼそのまま。 www.prepbootstrap.com

このページに変換機能もあったので気まぐれでBootstrap4にアップデートしています。

テンプレからの変更点1:検索機能

検索欄に入力すると、FAQが絞れます。検索というよりは絞り込みですね。 既存の環境はいじれないのでバックエンドは考えずフロントでどうにかしています。

function search() {
    // 検索条件を取得、スペースで区切られた各条件で検索する。
    var input = document.querySelector("#search-criteria");
    var conditions = input.value.split(/ | /).filter(function(elem) {
        return elem != "";
    });

    // 正規表現の作成
    var tmpExp = "";
    conditions.forEach(function(c, i, a) {
        tmpExp += ("(?=.*" + c + ")");
    });
    var exp = "^" + tmpExp;
    var regexp = new RegExp(exp, "i");

    // Q&Aの各要素を取得。条件にヒットしないものを非表示にする。
    var divTmp = document.querySelectorAll(".qna")
    divs = Array.prototype.slice.call(divTmp, 0)
    divs.forEach(function(div, index, ar) {
        div.style.display = regexp.test(div.innerHTML) ? "" : "none";
    });
}

テンプレからの変更点2:Vue.js

以下の理由からVue.jsに触れることにしました。

  • 最近流行っている技術に触れたい
  • メンテナンスを楽にしたい
    • jsonを更新するだけで反映される構造にしたい
  • とはいえシンプルなものでさっさと作りたい
    • Reactも気になっていたんですが、環境作るのが難しそうで今回は断念

Web開発の業務経験がないので、こんな理由で使っていいのかアレですが、いろいろ興味のあるものは触れてみると楽しいので。

活用したのは「FAQのjsonデータを元にDOMを作る」目的です(言葉の使い方が正しいか不安) 以前にも riotjsを使ったことがあったので、スムーズに取り入れることができました。

具体的には以下のようなコードを書きました。

<div class="container">

    <br />
    <div class="form-group">
        <label for="usr">検索</label>
        <input type="text" class="form-control" id="search-criteria" placeholder="検索" onkeyup="search()">
    </div>
    <div class="" id="accordion">
        <div id="qnaAll">
            <div v-for="qnaByGenre in content">
                <div class="faqHeader">{{ qnaByGenre.genre }}</div>
                <div class="card qna" v-for="qna in qnaByGenre.qnas">
                    <div class="card-header">
                        <h4 class=""><a class="accordion-toggle collapsed" data-toggle="collapse" data-parent="#accordion" v-bind:href="'#' + qna.id">{{ qna.q }}</a></h4>
                    </div>
                    <div v-bind:id="qna.id" class="panel-collapse collapse">
                        <div class="card-block">
                            <div class="answer">{{ qna.a }}</div>
                            <div style="display:none">検索用キーワード {{ qna.keywords }}</div>
                            <div class="links" v-if="qna.links.length > 0">
                                <hr />
                                <h5>参考リンク</h5>
                                <ul v-for="link in qna.links">
                                    <li><a v-bind:href="link.url" target="_blank"> {{ link.title }}</a></li>
                                </ul>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

SQL Serverで全DB全テーブル全カラムのメタデータを取得する

背景

テーブルレイアウトも仕様書も、弊社内製既存のアプリがことごとく情報がなく、とりあえずDBのメタデータを出力することに。とりあえずメモ。

これらを使って開発側で仕様や意味合いなどまとめていきたい。。。

環境

コード

DECLARE @DBName NVARCHAR(256)
DECLARE @varSQL NVARCHAR(512)
DECLARE @getDBName CURSOR
SET @getDBName = CURSOR FOR
SELECT name
FROM sys.databases
CREATE TABLE #TmpTable (
    DBName NVARCHAR(256),SchemaName NVARCHAR(256),TableName NVARCHAR(256),ColumnName NVARCHAR(256), 
    ValueType NVARCHAR(256),max_length NVARCHAR(256),precision NVARCHAR(256),scale NVARCHAR(256),is_nullable NVARCHAR(256)
            
)
OPEN @getDBName
FETCH NEXT
FROM @getDBName INTO @DBName
WHILE @@FETCH_STATUS = 0
BEGIN
    print @DBName;
    SET @varSQL = 'USE ' + @DBName + ';
  INSERT INTO #TmpTable
  SELECT
   ''' + @DBName + ''' AS DatabaseName
   , SCHEMA_NAME(obj.schema_id) AS SchemaName
   , obj.name AS TableName
   , col.name AS ColumnName
   , typ.name AS ValueType
   , col.max_length 
   , col.precision 
   , col.scale 
   , col.is_nullable 
  FROM
   sys.objects obj
   INNER JOIN sys.columns col
    ON obj.object_id = col.object_id
   INNER JOIN sys.types typ
    ON col.user_type_id = typ.user_type_id
  WHERE
   obj.type = ''U''
  '
    EXEC (@varSQL)
FETCH NEXT
FROM @getDBName INTO @DBName
END
CLOSE @getDBName
DEALLOCATE @getDBName
SELECT *
FROM #TmpTable ORDER BY DBName,TableName,ColumnName
DROP TABLE #TmpTable

参考

各データベースへのループ処理

https://blog.sqlauthority.com/2008/04/29/sql-server-find-table-in-every-database-of-sql-server/

メタデータ関連

docs.microsoft.com

docs.microsoft.com

docs.microsoft.com

新春MacBook(Unibody 2010)をSSDに、RAM8GBに換装した

家族が端末ほしいと言うので新しく古いMacBook Unibody 2010mid を使えるようにHDDをSSD(500GB)に変え、RAMを8GB(4GBx2)にしてみました。

用途は簡単なプログラミングやネットサーフィンくらいかな。 前提として、元あったデータも何も捨てるのでバックアップなどは一切しません。

やったこと

  • 下調べ
  • パーツ購入
  • 換装作業
  • OS再インストール

下調べ

古いMacBookSSD化、またRAMの換装はしたことがなかったので下調べ。

分解・換装方法

写真はifixit、動画はGeekanoids氏で確認しました。

HDD

jp.ifixit.com

www.youtube.com

RAM

jp.ifixit.com

www.youtube.com

使用パーツ

新年にコンビニ受け取りし、かつ全部まとめてもらいたかったのでAmazonで注文。

HDD

秋葉館のページを参照し対応商品を確認。 www.akibakan.com

近いものをAmazonでレビューを確認しつつ購入。 www.amazon.co.jp

RAM

Apple の公式より、RAMを確認。 MacBook (13-inch, Mid 2010) - 技術仕様

Amazonからこれを購入。 www.amazon.co.jp

精密ドライバー

レビューもまずまずで、かっこよかった(キッズ並観点)のでこれを購入。

www.amazon.co.jp

換装作業

動画そのまま、問題なく実行できました。 特にメモリは Geekanoidsさんの動画で挿すときに角度をつけるなど、気をつけるべきところがわかったので助かりました。 今気づいたんですが、ifixitにあるバッテリー外し忘れてましたね。

OS再インストール

OS再インストールは

付属DVDからSnowLeopardをインストール

個人情報やAppleIDについては入力しませんでした。

ソフトウェアアップデート(トラブルあり)

SnowLeopardをインストール完了し、ソフトウェアアップデートをかけたら デフォルトの壁紙だけ表示されており、マウスポインタは動かせるが、何もすすまない状態になりました。

調べたところ、以下のQ&Aで解決しました。

SnowLeopard10.6.3でソ… - Apple コミュニティ

そうしたらしばらくアップデートと再起動の繰り返しです。

El Capitanへのアップデート

App Storeからダウンロードするはずなんですが、検索しても見つからないので AppleのサイトからApp Storeを開くリンクを使って移動し、インストールしました。

support.apple.com

以上! いい感じに使えるといいなぁ。

源泉徴収票には賞与が含まれる。

久々の記事でまったくコンテンツ性のないタイトル落ちの記事です。 本文を見ている方には申し訳がありません。 ふと思い立って検索するとタイトルで人を寄せて内容を読ませるタイプの記事が多かったのでタイトルだけで終わる記事もいいのでは?と書きました。

以上!

「画像を表示」を復活させるブックマークレットを作ってみたらもっといいものが既にあった

あらすじ

該当記事

forest.watch.impress.co.jp

私の作ったもの

2018/02/20 版

javascript:(function(){
    function convertHref2Url(href){
        var startTag = "/imgres?imgurl=";
        var endTag   = "&imgrefurl=";
        return decodeURIComponent(href.substring(href.indexOf(startTag) + startTag.length, href.indexOf(endTag)));
    };
    Array.from(document.querySelectorAll("a[jsname='hSRGPd']"),  e => {
        var imageLink = document.createElement("a");
        imageLink.setAttribute("target","_blank");
        imageLink.style ="height:30px;width:80px;" +
            "background-color:rgba(130, 130, 255, 0.8);" +
            "color:rgba(255, 255, 255, 1.0);" +
            "z-index:1;" +
            "border-radius: 10px;" +
            "display: flex;" +
            "flex-direction: column;" +
            "justify-content: center;" + 
            "align-items: center;" +
            "position: absolute;" +
            "text-decoration: none;";
        imageLink.href = convertHref2Url(e.href);
        var linkTitle = document.createElement("div");
        linkTitle.style = "color:black;font-size:5px;";
        linkTitle.innerText = "View image";
        imageLink.appendChild(linkTitle);
        e.parentNode.appendChild(imageLink);
    });
})();

2018/02/21 版

面白そうだったので色々試した

  • スクロールした後、リンクのついてない画像が出て来る。 何度も実行することを考慮して重複しないようにした。
  • すべての追加される要素にstyle属性を直書きすると文字数がすごいことになる(影響不明)のでstyle要素を追加した。 style要素も重複対応済。
javascript: (function () {
    function convertHref2Url(href) {
        var startTag = "/imgres?imgurl=";
        var endTag = "&imgrefurl=";
        return decodeURIComponent(href.substring(href.indexOf(startTag) + startTag.length, href.indexOf(endTag)));
    };

    var styleId = "gas_view-image_style";
    var labelClass = "gas_view-image_label";
    var linkClass = "gas_view-image_link";
    var s = document.querySelector("#" + styleId);
    if (s === null) {
        var style = document.createElement("style");
        style.setAttribute("id", styleId);
        style.nodeType = "text/css";
        style.appendChild(document.createTextNode(`
        .${labelClass} {
            height:30px;
            width:80px;
            background-color:rgba(130, 130, 255, 0.9);
            color:rgba(255, 255, 255, 1.0);
            z-index:1;
            border-radius: 10px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            position: absolute;
            text-decoration: none;
        }
        .${linkClass}{
            color:black;
            font-size:8px;
        }
        `));
        document.head.appendChild(style);
    }

    Array.from(document.querySelectorAll("a[jsname='hSRGPd']"), e => {
        if (e.parentNode.querySelector("." + labelClass) === null) {
            var imageLink = document.createElement("a");
            imageLink.setAttribute("target", "_blank");
            imageLink.setAttribute("class", labelClass);
            imageLink.href = convertHref2Url(e.href);

            var linkTitle = document.createElement("div");
            linkTitle.innerText = "View image";
            linkTitle.setAttribute("class", linkClass);
            imageLink.appendChild(linkTitle);
            e.parentNode.appendChild(imageLink);
        }
    });
})();

あと記事の問題ですが、コードハイライトが有効になるように修正。 (javascript を js にしていた。)

動作

こんな感じに「View image」のリンクが作られます。

f:id:NotShown:20180221002250p:plain

Google 画像検索 動作結果

javascriptどころかweb系全然触れてない(言い訳)のでコードの適当さがあります。

以上!

configを修正してもリモートホストからKibanaにアクセスできない場合

Windows ファイアーウォール で node.exe を許可しろ!!!

※ kibanaのバージョンアップごとに忘れて毎回時間食うので一言書きました。

以上。

日経BP-IT PRO Activeの記事を印刷用にするブックマークレット

もくじ

  • 前置き
  • 環境
  • ソース
  • 使うとどうなるか
  • 感想

前置き

最近チーム全体が情報収集する気風になってきていて、日経関連の記事を見る機会が多くなりました。

PDF込みで記事になっていたり、印刷ビューになるものはいいのですが、それ以外はページ全体を紙に印刷して情報共有することになっています。

で、毎度開発者ツールで無駄なものを削除していたのですが、面倒なのでブックマークレットにしてみました。

ブックマークレットとはなんぞや」についてはググってください。

環境

以下の環境で動かしてます。

  • Windows7 32bit
  • Chrome バージョン: 62.0.3202.94(Official Build) (32 ビット)

ソース

本業じゃないのでおそらく美しくないコードになっています。

javascript: (function() {
    ["#bpGlobalHeader","#wrapper>header","#wrapper>footer","#wrapper>div>aside",".content>article>footer>section",".tools",".breadcrumb"].forEach(function(val, index, ar) {
        Array.from(document.querySelectorAll(val),  e => e.remove());
    });
    document.querySelector("article").style.width="auto";
})()

使うとどうなるか

実行前

f:id:NotShown:20171204104957j:plain

実行後

f:id:NotShown:20171204105004j:plain

感想