yamaken1343’s blog

技術ブログもどき

オカムラのコーラルを買ったので椅子の選び方の覚え書き

オフィスチェア(とデスク)を買いました

引っ越しに伴って, 椅子と机を買うことにしました. こういう大型のものはだいたい買うより買い換えるほうが面倒だしアップグレードできるものでもないので, 予算の許す限り最初から良いものを買うべき というのが家訓です.

というわけで, オカムラのコーラルとコクヨの平机を買いました. 見た目は完全に事務所ですが.

椅子と机の写真
椅子と机 事務所か?

椅子のスペックは

[チェアタイプ] 背メッシュ ハイバック
[フレームカラー] シルバー
[ボディーカラー] ホワイトボディ
[ランバーサポート] ランバーサポートなし
[アームタイプ] 可動肘
[カラー] ミディアムブルー

机は特筆するところはないです. 110cmのものにしましたが, 140cmにしとけばよかったですね. 机の広さは心の広さ.

どちらもオフィスバスターズさん(https://www.officebusters.com/)で通販しました. 実際に見に行きたかったのですがこの時勢なので. ただ, 試座できるようなら確実にしたほうが良いと感じています.
価格は椅子が27,000円, 机が7,900円, 送料が4,400円, プラス消費税で合計43,230円でした まとめて送ってもらいましたが送料が割引されてるかはわかりません.

2週間ほど座りましたが, これまで座ってきたどの椅子よりも素晴らしいとしか言いようが無いです. ただ, ランバーサポートは後付できるようなのでつけるか悩んでます.

椅子の選び方

ここからはどういうところを気にして買うかを残しておきます. コーラルがどれかは☆つけときます

素材

メッシュかクッションかといった部分ですが, すわり心地以外にも一長一短あります.

  • メッシュはフレームにホコリが溜まりやすい
  • クッションはウレタンの掃除がしづらい 飲み物こぼしたりするとよろしくない ☆

ロッキング

体重かけたら背もたれが動く機能です.

  • 背もたれのみ
  • 背もたれと座面が同様にうごく
  • 背もたれと座面が2:1でうごく(シンクロロッキング) ☆

このうち, シンクロロッキングを選ぶべきです. というかマストだと思います. 足が浮かないために, 回転軸が座面前側にあるもの(オカムラだとアンクルチルトリクライニング)だと更に良いです.

反発力の調整方法にもいくつかあります

  • まわして決める
  • レバーで決める ☆
  • 自動で決定してくれる

以前はあんまり調整しない項目だったのですが, レバー式だとかんたんに調節できるので用途に応じて変えるようになりました.

リクライニング

背もたれの角度の決定方法ですね.

まず確認すべきは

  • 前傾姿勢に対応
  • 前傾姿勢に非対応 ☆

これによって用途が限定されるので, まずはじめに選ぶべき部分です.

角度は

  • 無段階で固定
  • 段階式で固定 ☆
  • なし

さらに固定方法も2種類あって

  • 背もたれ自体が動かなくなるもの ☆
  • 倒せる角度が決まってそれ以上は動かなくなるもの

固定方法は排他ではないので両方できるものもあります.
固定方法は割と好みが分かれる部分かと思いますので, 確認すべきです. 私は倒せる角度が決まってそれ以上は動かなくなるもののほうが好みだったのですが確認不足でした.(ロッキングの強さで代用しています)

背もたれ

高さを決める

  • ローバック (背中なかごろまで)
  • ハイバック (肩まで)
  • ヘッドレストつき (頭まで)

基本的にハイバックが良いかと. 部屋との調和や圧迫感を感じたりするようならローバック. 予算に余裕があるならヘッドレストつき.

これこそ実際に試すべきです. 私は試さなかったのでフレームがあたって違和感がありました(慣れました).

肘掛け

わりと好みが分かれる部分かと

  • 固定
  • 可動 ☆
  • なし

個人的には固定肘はちょうどよいところになければないほうが良いかと感じます.
可動肘は高さ調節はマストで, ほかはあれば良いといった印象.
お腹を机につけて作業する人はなしのほうが良い.

見落としがちですが重要な部分として

  • 背もたれと肘掛けが連動する ☆
  • 背もたれと肘掛けが連動しない

こちら非常に重要で, ロッキングの快適さに直結するので気にしてほしい部分ですね. 書いてないことも多いですが, 背もたれとくっついてるフレームに肘掛けのフレームがついてるかついてないかが目安になります. 動画みたら一発です.

その他

  • キャスター: かんたんに付け替えできるので破損とか素材とかあまり気にしなくてよし
  • ハンガー: 家用ならいらなくない?
  • ランバーサポート: 試してから決めるべき
  • 座面の奥行き調整: ないならないで問題はないが, あしがむくみやすい人は考慮したほうがよさそう
  • 座面の角度調整: 使ったことないからわからない
  • 背もたれの奥行き調整: 使ったことないからわからない
  • 前すべり防止機能: シンクロロッキングならまあまあ防止できる印象
  • 座面の上下調整: ないやつを買うな

まとめ

椅子を決めるときには, まず前傾姿勢に対応する必要があるかを決め, その後こんな感じで必要機能を洗い出すと選べない椅子が出てくるので絞りやすいと思います.

  • 必須
    • シンクロロッキング
    • 可動肘
    • 肘と背の連動
    • ハイバック
  • できればほしい
    • リクライニング上限を固定
    • 調整がかんたん

あとは好みと予算かと.

高いもののほうが調整項目が多く, かんたんで, デザインがかっこよくなりますが, 全体的な機能としては中級ラインとあんまり変わりません.

私の場合は予算と在庫の関係でコーラルしかなかったので実際に見に行くこともなく楽な買い物でしたが, やっぱり実際に試座して決めたほうが良かったなと若干後悔してないこともないです.

研究室内で業績をアーカイブするOSSをつくった

まえがき

出した論文とか発表した学会とかの管理ってどうやってるんでしょうね.

以前所属していた研究室ではとくに管理していないようでした. 現在所属している研究室ではWeb管理システムをつくって管理しています.

このシステムが稼働から10年ぐらい経っているらしく, 最新のHWで動作しないので置き換えが必要となりました. というわけで新しいシステムをつくりました. せっかくなので公開します.

成果物

システム本体 github.com

本番用Docker-compose github.com

要件

  • 内部の人間しか使わないので制限も最低限. ログインとかいらない. 誰でも登録とか更新とかできるように
  • バックアップ等の管理が楽だと良い
  • 移行も楽にやりたい

  • 機能は旧システムと同様でOK

とのことだったので, 研究で触っている人の多いPythonフレームワークであるDjangoを使用する
また, Docker慣れている人も多いのでDockerを使う
セオリー通りにDjango+Gunicorn+NginxでやりたかったのでDocker-composeで構築
あとはバックアップが楽なSQLiteを組み合わせ, 可能な限り1ディレクトリで完結するように心がける

旧システムはPHPでどこにソースがあるかもわからなかったので適当に構築 最初はAPIサーバにしようと思ったけどJavascriptで画面作るのが面倒になって

機能とか

f:id:yamaken1343:20200310144637p:plain
トップページ
f:id:yamaken1343:20200310144639p:plain
業績詳細
レコードにはこのような項目が存在する. 主著者と業績区分あたりは外部キー.
著者や雑誌名も外部キー化したかったけど過去の業績との兼ね合いから旧システムとほぼ同様の構造のまま
f:id:yamaken1343:20200310144636p:plain
主著者一覧
旧システムがレコードの外部キーに主著者を指定しており, ユーザも主に主著者で検索をかけていたので一覧画面が存在する
f:id:yamaken1343:20200310144651p:plain
検索画面
レコードの各項目で検索できる
f:id:yamaken1343:20200310144649p:plain
検索結果
表形式で表示. 表示はDataTablesを使用. ほかに参考文献風やbibtexで表示可能. PDFの一括ダウンロードも可能.
f:id:yamaken1343:20200310144623p:plain
bibtex出力
f:id:yamaken1343:20200310144653p:plain
登録画面
前述の通り誰でも新しいレコードが登録できる. 全角カンマとか全角スペースとかは半角に変換される.

つくってるときに詰まったとことか

POST時のファイル関連で詰まった覚えがある 結局RESTflamework入れてなんとかしたんだっけな

PDFの一括ダウンロードも結構面倒だった HTMLに

<button class="btn btn-secondary"
            onclick="location.href='{% url 'gyoseki:download' %}?list={% for i in recode_list %}{{ i.file|yesno:"media/," }}{{ i.file }}{{ i.file|yesno:"!!!PARSER!!!," }}{% endfor %}'">
        PDF一括ダウンロード
    </button>

と無茶苦茶なGETリクエストを書いて

def download(request):
    li = request.GET.get('list')
    li = li.split('!!!PARSER!!!')
    response = HttpResponse(content_type='application/zip')
    response['Content-Disposition'] = 'attachment; filename=matome.zip'
    zf = zipfile.ZipFile(response, 'w')
    for l in li:
        if l:
            zf.write(l)
    return response

ただzip化するだけなんだけども, 無茶苦茶なGETリクエストやでかいZIPファイルを通すために

    client_max_body_size 4G;
    large_client_header_buffers 4 128k;

みたいにNginxの設定をいじる必要があったりとすんなりいかなかった

Djangoのstatic周りとか設定ファイルを本番と開発で変更するとかは改めて書かないがすんなりいかなかった

あとはまあそれなりといった感じでしょうかね
Djangoの知識なしで勉強しながらつくってだいたい0.5人月くらいでした

オンラインジャッジシステムのSharif-Judgeをdocker-composeで構築した

といっても借り物の改良

授業で使うオンラインジャッジシステムが壊れたそうなのでリプレース用に構築.
できるだけ手をかけないでやろうと思ったけどやっぱり近道はよくない.

成果物

github.com

元ネタ

さくっと構築しようと思ってDocker上で構築できそうな

qiita.com

この記事および

github.com

を元に最初構築したが, 問題が発生

  • docker-compose down したらまるまるデータが消える
  • どう頑張ってもRuntime Errorが発生する

このままだとつかいものにならないのでなんとか改善する

データが消える問題の解決

Postgres 側

元ネタのDockerfileを見ると, PostgreSQL/var/lib/postgresql/dataがVolumeとしてマウントされていることがわかる. 確かに名前なしVolumeはdown時に消えるので名前ありのVolumeにすることで解決できそう.

volumes:
      - dsjpgdata:/var/lib/postgresql/data

に変更することで解決 ...しませんでした.

Sharif Jadge側

どうやら問題そのものや提出したコードはDBに格納されるわけではないらしく, 普通に消えました. 巧妙なことにDB側のデータは生きているのでひと目ではわからない (過去の提出したコードを参照した際に発覚) のが厳しい.

がんばって格納される箇所を探してそこをマウントする
結果的に,

  volumes:
    - /data/assignments:/data/assignments

とすることで消えなくなりました.

Runtime Errorの解決

コンパイルは通ってそうだったのでとりあえず実行に関係のありそうな設定を弾く

よくわからんがSandboxing と Shield のチェックを外したら動くようになったからとりあえずヨシ
(PHP5だしUbuntu14だしでもうセキュリティなんて知らない)

まとめ

よくわからないものをよくわからないまま使うのはやめましょう

github.com

opencv python で特定の色を抽出する

特定の色を持つ画素を抽出するだけでなんかえらい時間かかったのでメモ

結論

# アルファチャンネルがない場合
(img == (B, G, R)).all(axis=2)
# あるばあい
(img == (B, G, R, A)).all(axis=2)

でtrueかfalseをもつ配列が得られる

使いかたの例

白を黒に置き換え

img[(img == (255, 255, 255)).all(axis=2)] = (0,0,0)

白い画素にアルファチャンネルを付与

rgba = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
rgba[..., 3] = np.where((img == (255, 255, 255)).all(axis=2), 0, 255)

2次元カルーセルをつくった

まえがき

画像群を上下左右方向に動かす必要性に駆られたが, いい感じのライブラリがなかった

つくった

https://github.com/yamaken1343/carousel-2d/tree/master

動作デモは https://yamaken1343.github.io/carousel-2d/

javascriptなんもわからん

TODO

  • スマホのフリック対応
  • 端っこにいったらコントローラーを消す

ASUS WS X299 SAGE に32GBのメモリを認識させる

まえがき

32GB × 4 枚で128GBだ!
をやろうとしたのにメモリが認識しなくて詰まったのでメモ

結論

BIOSをアップデートする

機材

ASUS WS X299 SAGE www.asus.com

G-skill F4-2666C18Q-128GVK www.gskill.com

やったこと

だいたい差し込みが甘いせいなのでとりあえず1枚さして起動 -> だめ

メモリスロット故障を疑い別のメモリを刺す ー> 起動

対応メモリの表(https://www.asus.com/jp/Motherboards/WS-X299-SAGE/HelpDesk_QVL/)を見る -> ちらほら32GBのメモリがある

ここでBIOSを0502から最新の2002にアップデートする

1枚で起動 ー> 成功

4枚で起動 ー> 成功

今後の課題

128GB以上認識するのか試したい

GrowiのAPIを叩きたい

研究じゃなくてこういうことだけ無限にやりたい

まえがき

研究室でGrowiというWikiを使ってます.
github.com 自動でSlackの特定のチャンネルの投稿内容をWikiアーカイブすることになったので, その調査をします.

一応ドキュメントは存在しますが, docs.growi.org 手が回っていないようなのでソースを見ます.

API捜索

おそらくここにあるものがすべてかと思われます.*1
https://github.com/weseek/growi/blob/master/src/server/routes/index.js API名からあたりをつけます.

今回はページの更新をしたいので
https://github.com/weseek/growi/blob/master/src/server/routes/page.js
を見れば良さそうです.

仕様調査

ページの更新がしたいので, 旧ページのデータをもってくる Getpage API とページの更新を行う UpdatePage API を調査します.

Getpage API

https://github.com/weseek/growi/blob/master/src/server/routes/page.js

  • method: GET
  • endpoint: /_api/pages.get
> @api {get} /pages.get Get page data
> @apiName GetPage
> @apiGroup Page
>
> @apiParam {String} page_id
> @apiParam {String} path
> @apiParam {String} revision_id

とりあえずpathを指定しておけば良さそう

認証済みのブラウザからなら

https://demo.growi.org/_api/pages.get?path=/

認証してないブラウザ等からはAPI KEYをパラメータに渡してやれば良さそうです.

https://demo.growi.org/_api/pages.get?path=/&access_token=[ユーザー設定から確認できるAPI KEY]

ただし, API KEYに"+"等のURLに使えない値が含まれる場合はエンコードが必須です.
pythonのrequestsは勝手にやってくれました.

認証済みのブラウザからならトークンなしでも素直に通るので, もっと良いやり方があるかもしれません.

ちなみに叩くと以下のようなJSONが返ってきます.

{
  "page": {
    "status": "published",
    "grant": 1,
    "grantedUsers": [],
    "liker": [],
    "seenUsers": [
      "592f8f117f616a0019f77e74",
      "592f853a7f616a0019f77e6e",
      "592f9dd47f616a0019f77e7c"
    ],
    "commentCount": 0,
    "createdAt": "2019-03-20T21:57:34.092Z",
    "updatedAt": "2019-05-09T07:42:46.324Z",
    "_id": "5c9403b8ef06c40058a243e8",
    "path": "/",
    "creator": {
      "isGravatarEnabled": false,
      "isEmailPublished": true,
      "lang": "ja",
      "status": 2,
      "admin": true,
      "_id": "592f8f117f616a0019f77e74",
      "email": "admin1@crowi-plus.org",
      "username": "admin1",
      "name": "Admin 1",
      "createdAt": "2017-06-01T03:50:41.524Z"
    },
    "lastUpdateUser": {
      "isGravatarEnabled": false,
      "isEmailPublished": true,
      "lang": "ja",
      "status": 2,
      "admin": false,
      "_id": "592f9dd47f616a0019f77e7c",
      "email": "guest1@crowi-plus.org",
      "username": "guest1",
      "name": "Guest 1",
      "createdAt": "2017-06-01T04:53:40.461Z",
      "image": null
    },
    "redirectTo": null,
    "grantedGroup": null,
    "__v": 58,
    "revision": {
      "format": "markdown",
      "_id": "5cd3d9f6ef06c40058a245f7",
      "createdAt": "2019-05-09T07:42:46.323Z",
      "path": "/",
      "body": "# Overview\n\n ..."
      "author": {
        "isGravatarEnabled": false,
        "isEmailPublished": true,
        "lang": "ja",
        "status": 2,
        "admin": false,
        "_id": "592f9dd47f616a0019f77e7c",
        "email": "guest1@crowi-plus.org",
        "username": "guest1",
        "name": "Guest 1",
        "createdAt": "2017-06-01T04:53:40.461Z",
        "image": null
      },
      "hasDiffToPrev": true,
      "__v": 0
    },
    "hasDraftOnHackmd": false,
    "pageIdOnHackmd": "lcFmaxSxRXel2uwnGIWgIQ",
    "revisionHackmdSynced": "5cd3d9f6ef06c40058a245f7",
    "id": "5c9403b8ef06c40058a243e8"
  },
  "ok": true
}

UpdatePage API

  • method: POST
  • endpoint: /_api/pages.update
> @api {post} /pages.update Update page
> @apiName UpdatePage
> @apiGroup Page
>
> @apiParam {String} body
> @apiParam {String} page_id
> @apiParam {String} revision_id
> @apiParam {String} grant
>
> In the case of the page exists:
> - If revision_id is specified => update the page,
> - If revision_id is not specified => force update by the new contents.

全部さっきの Getpage API で取得できます.
revison_idを指定しないと新しい内容で強制的に上書きするって書いてますが, ないと通りませんでした.
grantは1が全体公開っぽい. ないとnullが指定されます. 青枠が表示される以外は全体公開と同様?

ページの更新に失敗するとokカラムにfalseの入ったjsonが返ってきます.
成功すると先程のgetで取得可能なjsonが返ってきます.

次回は実際に使ってみます.

*1:node.jsわからない