ajax通信のライブラリを使いつつ、直列に複数のPOSTを送る
まとめ
- async/awaitを使うと関数から返ってきたPromiseがresolveされるまで処理を止めることができる
- ajax通信で通信終了まで待機するには、ajax通信を行う部分をreturn new Promiseでくるみ、通信が終わったらresolveする
- ajax通信の呼び出し元はawaitしておく
基本形
※ サンプルではajax通信用にsuperagentを使用
https://visionmedia.github.io/superagent/
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <!-- superagentのimport --> <script src="https://cdnjs.cloudflare.com/ajax/libs/superagent/3.8.3/superagent.min.js"></script> </head> <body> <button onclick="postAll()">post</button> <script> //asyncを先頭につけることでawaitが使えるようになる //awaitは戻り値(Promise)がresolveされるまで処理を止めて待機する async function postAll(){ await post() await post() } function post(){ return new Promise(function(resolve, reject){ var request = window.superagent url = "post先のURL" parameter = { //postするパラメータ } request.post(url) .send(parameter) .end(function(err, res){ //通信が終わったらresolveする resolve() }) }) } </script> </body> </html>
変形: ループに適用
postAll()の中身を下記のようにする
async function postAll(){ for(var i = 0; i < 10; i++){ await post() } }
おまけ: 簡易的な並列度の調整
5こずつ、10こずつなど全部ではないものの複数のjobをまとめて投げたいときもあるので、 一度にpostする量を変数でコントロールできるようにする
※ 全部一気に投げて良いならPromise.Allなどを使ったほうが楽
async function postAll(){ //同時に処理するjobの数をコントロールする変数 concurrency = 10; //一番最後のjobも実行されることを保証したいので、少し多めにforを回す //(もっときれいな書き方がありそう) for(var i = 0; i < num_job + concurrency; i+=concurrency){ //post()が返すpromise型の変数を格納する配列 //この中にjobを追加していく。 promises = [] for(var j = 0; j < concurrency; j++){ job_id = i + j //すこし多めにforを回しているため余計なif文がある if(job_id < num_job){ promises.push(post()) } } //promisesに入っているjobが全て完了するまで待機 for(var j in promises){ await promises[j] } } }
bash on windowsにawscli入れようとしたらyaml.hがないと怒られた
やること
sudo apt-get install libyaml-dev
経緯
bash on windows環境でawscliを入れるために下記コマンドを叩いたら、yaml.hがないのでコンパイルできない的なエラーで怒られた
sudo pip3 install awscli
参考のサイトによると「libyaml-dev」を入れてからawscli入れればエラーが出ないとのことだったので、上記apt-getを叩いてからもう一度pip3 installしたらエラーが出なくなった。
感想
すごいバッドノウハウ感あるけど副作用ないんだろうか。。。?
とりあえずちゃんと動いてそうなのでしばらく触ってみる
参考
boto3 + DynamoDBでリストに要素を追加・削除
目標
dynamoDBに入っているリスト要素に対して、追加・削除を行う
環境
コード
import boto3 import decimal import json from boto3.dynamodb.conditions import Key, Attr # DB設定 dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('table') # Helper class to convert a DynamoDB item to JSON. # AWSのドキュメントにあったもの。これがないとnumber <-> floatの変換周りでエラーになる class DecimalEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, decimal.Decimal): if o % 1 > 0: return float(o) else: return int(o) return super(DecimalEncoder, self).default(o) # 本体 def lambda_handler(event, context): ##### 要素の削除 # リストのうち削除したい要素番号を定義しておく index = 0 res = table.update_item( Key={ # アップデートするレコードの条件 # 例: idカラム(プライマリキー)=1のとき 'id': 1, }, # Remove命令でリストから要素を削除する。 # 基本形: REMOVE カラム名[要素番号] # ExpressionAttributeValuesでの要素番号指定は(何故か)できないので、削除する要素番号が可変の場合は無理やり渡している UpdateExpression="REMOVE #attribute[" + str(index) + "]", ExpressionAttributeNames= { # UpadateExpressionで使っている#attributeを置き換える # リスト要素の名前、今回はtarget_listだと仮定する '#attribute': "target_list" }, # DB操作に直接影響しないので好みに合わせて ReturnValues="UPDATED_NEW" ) ##### 要素の追加 # 追加する要素には、数値・文字列・バイナリが指定可能 # boto経由だと、変数の型が考慮されるようなので注意(そのかわりN,S,Bの指定はいらない?) append_value = 0 res = table.update_item( Key={ # 例: idカラム(プライマリキー)=1のとき 'id': 1, }, # list_appendでlist同士をつなぎ、SET命令で新しいリストを代入する # 基本形: SET カラム名 = list_append(リスト1, リスト2) # list同士の結合命令なので、追加要素が一つしかなくてもリストの形で渡す # ExpressionAttributeValuesが使えるのでこちらを使ったほうがよさそう UpdateExpression="SET #attribute = list_append(#attribute, :val)", ExpressionAttributeNames= { # UpadateExpressionで使っている#attributeを置き換える # リスト要素の名前、今回はtarget_listだと仮定する '#attribute': "target_list" }, ExpressionAttributeValues={ # UpadateExpressionで使っている:valを実際の値で置き換える ':val': [append_value] }, # DB操作に直接影響しないので好みに合わせて ReturnValues="UPDATED_NEW" )
備考
Number Setに対して同じことをやろうとするときはADD命令を使うらしいが、うまく行かなかった。 botoで実現可能なのだろうか。。。
jsで簡易マインドマップエディタを作る
できたもの
できること
できないこと
実装上のポイント
- jsから動的にSVG要素を作るときは、createElementではなくcreatElementNSを使う
- SVGで自動改行するテキストボックスを作るにはforeignObject要素の子要素としてdivを配置
- その際foreignObjectはSVG要素の直下に置く。
- foreignObject配下にdivを作るときもcreateElementNSを使っておいたほうがよさそう
var svgNS = "http://www.w3.org/2000/svg"; var htmlNS = "http://www.w3.org/1999/xhtml" var foreignElement = document.createElementNS(svgNS, "foreignObject"); foreignElement.setAttribute("width", nodeWidth); foreignElement.setAttribute("height", nodeHeight); foreignElement.setAttribute("x", x); foreignElement.setAttribute("y", y); var innerElement = document.createElementNS(htmlNS, "div"); innerElement.innerText = text; document.getElementById("map").appendChild(foreignElement); foreignElement.appendChild(innerElement);
コード
<html> <head> <script type="text/javascript" src="jquery-3.2.1.min.js"></script> </head> <style> </style> <body> <div> <div style="float:left"> <textarea id="text" name="text" rows="20" cols="40"></textarea> </div> <div style="float:left;"> <svg id="map" width="700" height="500"></svg> </div> </div> <script type="text/javascript"> var svgNS = "http://www.w3.org/2000/svg"; var htmlNS = "http://www.w3.org/1999/xhtml" var inputText = ""; $("#text").keyup(function(){ if(inputText != $("#text").val()) { inputText = $("#text").val() drawMap(inputText); } }) var nodeWidth = 100; var nodeHeight = 50; var xMargin = 50; var yMargin = 20; function drawMap(text) { $("#map").empty(); console.log(text) nodeArray = parseText(text); nodeArray = decideNodePosition(nodeArray); console.log(nodeArray); for(var i = 0; i < nodeArray.length; i++) { drawSingleNode(nodeArray[i].text, nodeArray[i].x, nodeArray[i].y) var children = nodeArray[i].children; for(var j = 0; j < children.length; j++) { connectNodes(nodeArray[i], children[j]); } } } function decideNodePosition(nodeArray) { var leafCounter = 0; for(var i = 0; i < nodeArray.length; i++) { var level = nodeArray[i].level; nodeArray[i]["x"] = level*(nodeWidth + xMargin) if(nodeArray[i].children.length == 0) { nodeArray[i]["y"] = leafCounter*(nodeHeight + yMargin) leafCounter++; } } for(var i = nodeArray.length - 1; i >= 0; i--) { if(nodeArray[i].children.length > 0) { var y = 0; var middleNodeID = Math.floor(nodeArray[i].children.length/2); if(nodeArray[i].children.length % 2 == 1) { y = nodeArray[i].children[middleNodeID].y; } else { var y1 = nodeArray[i].children[middleNodeID].y; var y2 = nodeArray[i].children[middleNodeID - 1].y; y = (y1 + y2)/2; } nodeArray[i]["y"] = y; } } return nodeArray } function drawSingleNode(text, x, y) { var rectElement = document.createElementNS(svgNS, "rect"); rectElement.setAttribute("width", nodeWidth); rectElement.setAttribute("height", nodeHeight); rectElement.setAttribute("x", x); rectElement.setAttribute("y", y); rectElement.setAttribute("fill", "white"); rectElement.setAttribute("stroke", "black"); var foreignElement = document.createElementNS(svgNS, "foreignObject"); foreignElement.setAttribute("width", nodeWidth); foreignElement.setAttribute("height", nodeHeight); foreignElement.setAttribute("x", x); foreignElement.setAttribute("y", y); var innerElement = document.createElementNS(htmlNS, "div"); innerElement.innerText = text; innerElement.style["padding"] = "0 5 0 5"; document.getElementById("map").appendChild(rectElement); document.getElementById("map").appendChild(foreignElement); foreignElement.appendChild(innerElement); } function connectNodes(fromNode, toNode) { var lineElement = document.createElementNS(svgNS, "line"); lineElement.setAttribute("x1", fromNode.x + nodeWidth); lineElement.setAttribute("y1", fromNode.y + nodeHeight/2); lineElement.setAttribute("x2", toNode.x); lineElement.setAttribute("y2", toNode.y + nodeHeight/2); lineElement.setAttribute("stroke", "black"); document.getElementById("map").appendChild(lineElement); } function parseText(text) { textArray = text.split("\n") mapObject = new Object(); nodeArray = []; levelArray = []; levelArray[0] = null; for(var i = 0; i < textArray.length; i++) { var level = 0; var text = textArray[i]; for(var j = 0; j < text.length; j++) { if(text.startsWith("*")) { text = text.substring(1); level++; } else { break; } } text = text.trim(); var node = {id: i.toString(), text: text, level: level, parent: levelArray[level]} nodeArray.push(node) levelArray[level + 1] = node; } childrenMap = {}; for(var i = nodeArray.length - 1; i >= 0; i--) { if(nodeArray[i].id in childrenMap) { nodeArray[i]["children"] = childrenMap[nodeArray[i].id]; } else { nodeArray[i]["children"] = []; } if(nodeArray[i].parent != null) { if(!(nodeArray[i].parent.id in childrenMap)) { childrenMap[nodeArray[i].parent.id] = [] } childrenMap[nodeArray[i].parent.id].push(nodeArray[i]); } } return nodeArray; } </script> </body> </html>
excel vbaで画像の位置を取得する
目標
アクティブシート内の図形/画像の中心位置を座標(ピクセル単位)で取得する
やり方
状況
以下のようにシート内に図形や図が幾つか設置されており、その中心座標を取得することを考えます
コード
中心座標を直接取得することはできないので、左(上)の座標+幅(高さ)/2することで中心座標を取得します
Sub TopAndLeftSamp1() Dim Sh As Shape For Each Sh In ActiveSheet.Shapes '---アクティブシート全ての図形に対し Debug.Print Sh.Name & ":" & (Sh.Left + Sh.Width / 2) & "," & (Sh.Top + Sh.Height / 2) Next Sh End Sub
結果
Rectangle 1:118.125,87
Rectangle 2:346.125,226.5
Rectangle 3:204.375,243.75
Rectangle 4:437.625,96.75
Rectangle 5:475.875,374.25
Picture 7:597.3749,225.3749
結果は 図形名(英語) : x座標, y座標 の形式で出力されています。
図の正方形/長方形1は正方形/長方形2より右下にありますが、x, yともにRectangle2のほうがRectangle1より大きい値を示しているので問題なく取得できていそうです。
参考
xargs + wget で ファイルリストにあるurlをすべて落として名前をつける
目標
- 事前に用意したurlリストに記述されたURLをまとめてダウンロードする
- 落としたファイルの名前を元々のURLにファイル名として使えない文字が入っていると辛いので、保存先の名前を少し置き換える
- 例えば「https://www.google.co.jp/search?q=wikipedia」というurlをwgetすると、search?q=wikipediaという名前のファイルに保存しようとして、(Windowsの場合)?がファイル名に使えないので保存できない問題をなんとかする。
それぞれ単独ならオプション1つだけで実現できるのに、同時に実現しようとすると急に辛くなる
参考: 単独で動かす場合
事前に用意したurlリストに対してまとめて落とす
wget -i ファイル名 例: wget -i urlList.txt
ファイル名を設定する方法
wget -O 保存先ファイル名 URL 例: wget -O おまじないの綴り方.html http://spell-spell.hatenablog.com/entry/2017/03/25/175513
やり方
検証環境
bash on windows Linux 4.4.0-43-Microsoft #1-Microsoft Wed Dec 31 14:42:53 PST 2014 x86_64 x86_64 x86_64 GNU/Linux
準備
wget -i で使えるような普通のurlではなく、urlの一部からなる保存ファイル名を記載したリストを作る
例:自分のブログをgoogleで調べたときの結果を調べることを想定。 https://www.google.co.jp/search?q=検索クエリ というURLで検索できるので、今回は検索クエリの部分をファイル名にすることにする。 以下のようにファイル名を並べたファイルをfile_name.txtとして保存する。
おまじないの綴り方 うらがみずむ
コード
xargs -a file -I{} wget -O {}.html URL_prefix/{} 例: xargs -a file_name.txt -I{} wget -O {}.html https://www.google.co.jp/search?q={}
説明
xargs
xargsは標準入力として受け取った文字列を任意のコマンドの引数として渡すことができるコマンド。 以下の例ではecho “abc”| によって受け取った"abc"という文字列をxargsの後ろのechoに渡している
$ echo "abc" | xargs echo abc $
汎用性が極めて高いコマンドなので理解するのがやや難しいですが、ネット上の情報も多いので、他のサイトも調べてみるといいかもしれません。
aオプション
パイプ等で標準入力を受け取る代わりに、ファイルから読み取った文字列を引数として渡せるオプション。
I(大文字のアイ)オプション
直後に指定した文字列を置換用の文字列して指定できるオプション。 例えば以下の様に使用する。
$ (例)echo "abc" | xargs -I{} wget {}.html $ (置換後)wget abc.html
例はabcという文字列をxargs経由でwgetに渡した例。-I{}によりxargsで渡ってきたabcが{}という文字列と置き換わるので、結局この例はwget abc.htmlというコマンドと等価になる。
wgetとの連携
xargs -I{} により、wgetコマンドの部分に記述されている{}の部分はxargsから渡された文字列に置き換わる。 そのため、file_name.txtに"おまじないの綴り方"と"うらがみずむ"が記録されている場合、
xargs -a file_name.txt -I{} wget -O {}.html https://www.google.co.jp/search?q={}
を実行すると"おまじないの綴り方"と"うらがみずむ"が文字列としてwgetに渡され、{}が置き換わるので、
wget -O おまじないの綴り方.html https://www.google.co.jp/search?q=おまじないの綴り方 wget -O うらがみずむ.html https://www.google.co.jp/search?q=うらがみずむ
が順番に実行され、本来ならsearch?q=おまじないの綴り方.htmlというファイル名で保存されるところが、おまじないの綴り方.htmlとして保存される。
注意点
マウスの位置によって表示位置を変えるツールチップをjsとCSSで実装する
目標
- javascript + CSS でツールチップ機能を実装する
- 画面の端の方にマウスが来てもツールチップが隠れないようにする
- 画面の端の方にマウスが来てもツールチップに重ならないようにする
前フリ
考え方
コード
<html> <style> <!--ツールチップの見た目--> span#tooltip{ position: absolute; <!--座標で表示位置を直接コントロールできるようにする--> z-index: 10; <!--他の要素より前に表示されるように--> visibility: hidden; <!--最初は隠しておく--> padding: 0 5px; background-color: #FFF; } <!--ツールチップを表示させる部分の見た目。わかればなんでもいい--> div { background: #abcdef; width:300px; height:300px; } </style> <html> <style> span#tooltip{ position: absolute; z-index: 10; visibility: hidden; padding: 0 5px; background-color: #FFF; } div { background: #abcdef; width:300px; height:300px; } </style> <body> <span id="tooltip">hoge</span> <div onmousemove="move()" onmouseover="over()" onmouseout="out()"></div> <br> <br> <div onmousemove="move()" onmouseover="over()" onmouseout="out()"></div> <script type="text/javascript"> function over() { var tooltip = document.getElementById("tooltip"); tooltip.style.visibility = "visible"; } function out() { var tooltip = document.getElementById("tooltip"); tooltip.style.visibility = "hidden"; } function move(){ var tooltip = document.getElementById("tooltip") //前々回のバージョン:特にマウス位置は考慮しない //var xPos = event.pageX + 10; //var yPos = event.pageY - 10; //前回のバージョン:ツールチップが隠れてしまわないように適切な座標を選択する var xPos = Math.min(event.pageX + 10, window.pageXOffset + window.innerWidth - tooltip.getBoundingClientRect().width - 10); var yPos = Math.min(event.pageY - 20, window.pageYOffset + window.innerHeight - tooltip.getBoundingClientRect().height -20); var xPos = 0; if(event.pageX < window.innerWidth/2) //マウスカーソル位置が画面の左半分側かどうか { xPos = event.pageX + 10; //左半分にいたらカーソル位置の右側にツールチップを表示 } else { xPos = event.pageX - tooltip.getBoundingClientRect().width - 10; //右側にいたらカーソルの右側にツールチップを表示 } var yPos = 0; if(event.pageY < window.innerHeight/2) //マウスカーソル位置が画面の上半分かどうか { yPos = event.pageY + 10 //上半分にいたらカーソル位置の下にツールチップを表示 } else { yPos = event.pageY - tooltip.getBoundingClientRect().height - 10; //下半分にいたらカーソル位置の上にツールチップを表示 } tooltip.style.top = yPos + "px"; tooltip.style.left = xPos + "px"; tooltip.innerHTML = "xPos:" + xPos + "<br/>" + "yPos:" + yPos; } </script> </body> </html>
問題点
ツールチップの内容が、画面サイズに対して一定以上大きい場合、結局ツールチップが隠れたり、重なってしまったりする問題は起きうる
ただ、これ以上はツールチップの位置よりも中身や表示サイズなどを工夫したほうが良さそう