OmniAICreator/Qiita-1.07M huggingface dataset:
【SQLServer】パフォーマンスを意識したSQL

前に調べた記事の内容を見返しながら、再度「自分の中で押さえておきたいポイント」を整理してみた。
※せっかくまとめたけど、しばらくSQLServerは使わなそう...残念。

# インデックスの種類
## そもそもインデックスって？
テーブルやビューからの行取得を高速に行うためのもの。
SQLServerの場合は「B-Tree構造」で作成され、指定したキーで昇順に並び替えられたデータの範囲によって枝分かれしている。

## クラスター化インデックス
リーフレベルに、「実際のデータそのもの」が格納されている。
クラスター化インデックスが作成されると、非クラスター化インデックスのリーフレベルには
「ポインタ(RID)」ではなく「クラスター化インデックスの値」が格納される。
そのため、実際のデータを探すことなく(RID Lookup)することなく、インデックスをSeekするのみで検索が可能になる。
また、クラスター化インデックスは「テーブルに1つのみ作成可能」で、デフォルトでは主キーの値で作成される。

## 非クラスター化インデックス
リーフレベルに「実際のデータへのポインタ(RID)」が格納されており、インデックスの走査後にポインタを元にデータを取得する。
この実データへのアクセスを「ページジャンプ」という。
また、クラスタ化インデックスがテーブルに作成されている場合は、「クラスター化インデックスのキー」が格納されているため、クラスター化インデックスからデータを取り出す処理となる(Key Lookup)。

## 複合インデックス(カバリングインデックス)
複数カラムをインデックスキーに指定したもの。
複合インデックスでもリーフノードからの「ページジャンプ」を行って、実データへのアクセスを行う。
しかし利用したいデータが「複合インデックスのキー」に含まれている場合、実データ領域へのアクセスをすることなくデータを取得することができる。
これを活かして、「取得するデータをインデックスに全て含めてしまおう」というのが「カバリングインデックス」。
ただしすべてのノードに複数カラムのデータを保持するため、インデックスのサイズが大きくなってしまう。

## 付加列インデックス
すべてのノードに複数カラムのデータを保持するのではなく、リーフノードにのみカラムを追加する(列を付加する)ことで、
ページジャンプを防ぎながらインデックスサイズを押さえることもできる。
「WHERE句に指定するカラムをキーに指定し、取得するカラムを付加しておく」といった使い方をする。

#データへのアクセス
## Index Seek, Clusterd Index Seek
**Bツリーを走査**したあと、一致するデータを見つけるためにリーフノードを検索する。

## Index Scan, Clusterd Index Scan
インデックスのツリー構造を使った走査を行わず、リーフノードを順番に検索する。
Index Seekよりも多くのページにアクセスすることになるため、**非効率な検索方法**といえる。

## Key Lookup(Clusterd)
非クラスタ化インデックスのリーフノードに格納された「クラスター化インデックスのキー」を基に、クラスター化インデックスから1行を取り出す。

## RID Lookup(Heap)
非クラスタ化インデックスのリーフノードに格納された「RID(行識別子)」を基に、実テーブルから1行を取り出す。

# なぜIndex Scanになってしまうのか
## WHERE句の左辺に関数を使用している
せっかく値が入っているのに、関数を使ってしまったらインデックスの値で探せなくなってしまう。

## インデックスが張ってある列の一部しか条件指定していない、または順番が間違っている
インデックスは「指定したキーでデータが並んでいる」ため、そのキーの一部しか条件に指定しなかった場合、正しくインデックスをSeekせずにScanしてしまう。

# パフォーマンス
## Index ScanからIndex Seekに
↑に書いたような点を意識してSQLを見直す。
正しくインデックスが利用されるようなSQLにすることで、Index Seekにする。

## Key LookupやRID Lookupを無くす
クエリのチューニングをするためには、「クエリによって行われる読み取り数を減らす」ことが重要。



# 参考URL
- [SQLServerのインデックスについてざっくりとまとめてみた](https://qiita.com/kz_morita/items/41291516ff3ee2650554)
- [SQLパフォーマンスtips(SQLServer)](https://qiita.com/kazuho39/items/0d3e617661670311ea05)
- [SQLのインデックスとそのチューニングについてのオンラインブック](https://use-the-index-luke.com/ja)
- [SQL Server のインデックス](https://techinfoofmicrosofttech.osscons.jp/index.php?SQL%20Server%20%E3%81%AE%E3%82%A4%E3%83%B3%E3%83%87%E3%83%83%E3%82%AF%E3%82%B9)
- [実行プランを読む – 活用編 (その 1) – クエリチューニング](https://blogs.msdn.microsoft.com/jpsql/2011/11/14/1-2/)
- [◆インデックスの構造と内部動作１](http://mtgsqlserver.blogspot.com/2013/03/blog-post_30.html)
Docker-composeでappとDBの接続する際の注意点

Docker composeでappとDBを接続するときは、appのソースコード内で指定するDBのホスト名をDBのコンテナを作成するときのサービス名にする。

例えばdocker-compose.ymlをこのようにしたら

```docker-compose.yml
nodeapp:
  image: node:8.9.4
  container_name: node-dev
  tty: true
  volumes:
    - ./node/src:/src
  ports:
    - "8080:8000"
  links:
    - postgresql

postgresql:
  image: postgres
  environment:
    POSTGRES_USER: postgres
    POSTGRES_PASSWORD: postgres
    POSTGRES_DB: dbname
  ports:
    - "5432:5432"
  volumes:
    - postgresql.volume:/var/lib/postgresql/data
  container_name: postgres-db
```

host名は`postgresql`となるので、
DBに接続する際は、以下のソースコードの４行目のようにpostgresqlをホスト名とする。

```post.js
'use strict';
const Sequelize = require('sequelize');
const sequelize = new Sequelize(
    'postgres://postgres:postgres@postgresql/dbname',
    {logging: false });
const Post = sequelize.define('Post', {
    id: {
        type: Sequelize.INTEGER,
        autoIncrement: true,
        primaryKey: true
    },
    content: {
        type: Sequelize.TEXT
    },
    postedBy: {
        type: Sequelize.STRING
    },
    trackingCookie: {
        type: Sequelize.STRING
    }
}, {
    freezeTableName: true,
    timestamps: true
});

Post.sync();
module.exports = Post;
```

### 参考
- [1ファイル・3コマンドで実現！Dockerでチーム全員の開発環境の構築を始めてみませんか？](https://nulab-inc.com/ja/blog/typetalk/docker-tutorial-local-environment-team/)
【初心者向け】Go言語のポインタの操作の概要

仕事でGolangを使うことになりそうなので、腰を据えて学ぶことにしました。
今まで、Rubyで開発していたのでポインタの操作は久しぶりだったので少しGolangを触って学んだことを整理しました。
同じような背景で、Golangを学び始めた方がポインタについての記憶を思い出す一助になればと記事にしました。
何か間違いなどがあれば気軽にご指摘くださいませ。

### ポインタとは？
ポインタとはアドレスを格納した変数のこと。
利用される場面は値渡しではなく参照渡しをして、その値自体を関数内で変更する際。

```go
package main

import (
  "fmt"
)

type Employee struct {
  ID     int
  Name   string
  Salary int
}

func main() {
  var employee Employee = Employee{
    ID: 1,
    Name: "John",
    Salary: 300,
  }
  fmt.Println(employee.Salary) // => 300
  // 構造体をそのまま渡すと値がコピーされて渡される（値渡しされる）ので更新結果が反映されない
  promete(&employee)
  fmt.Println(employee.Salary) // => 1300
}

func promote(pointer *Employee) {
  pointer.Salary += 1000
}
```

### ポインタの基本操作
**変数に対する操作**
`&値`: 値のアドレスを持つポインタ
`*ポインタ`: ポインタに格納されるアドレス上の値

**型定義**
`*型`: その型の値を指すポインタ

```go
employee := Employee{ID: 2}

// employeeを格納するポインタ
fmt.Println(&employee) // => &{2  0}

// employeeを格納するアドレスの指す値（つまりemployee）
fmt.Println(*&employee) // => {2  0}

// employeeを格納するアドレスを格納する変数（ポインタ）
var pointer *Employee = &employee

// ポインタが指すアドレス上の変数
fmt.Println(*pointer) // => {2  0}

// ポインタは変数なのでそのアドレスも取得できる
fmt.Println(&pointer) // 0xc42000c030
```

### Tips
1. 構造体のポインタからそのフィールドに直接アクセスできる
2. ポインタの指す値にアクセスする時は、優先順位に気をつける

```go
func promote(pointer *Employee) {
  // ⑴ 構造体のポインタからそのフィールドに直接アクセスできる
  pointer.salary += 1000
  
  // ⑵ ポインタから値へのアクセス（*）よりフォールドへのアクセスの方が優先度が高いので注意
  fmt.Println((*pointer).salary) // => 1300
  
  // empoloyee.salaryの値（int型）をポインタとして値を取得しようとしてエラーになる
  fmt.Println(*pointer.salary) // => invalid indirect of pointer.Salary (type int)
}
```
夏休みの工作におススメ！自動水やり機（Obniz＋電動灯油ポンプ）の製作

#背景
帰省等で数日家を留守にする際にベランダの植木が枯れるの防ぐため、自動水やり機をObnizで構築しました。

#処理フロー
①毎朝6時にスクリプトを自動起動
②水を流す（obniz→灯油ポンプに5秒電力供給）
→画面から任意のタイミングで起動もしたい

#完成イメージ
<img src="https://qiita-image-store.s3.amazonaws.com/0/181708/78370fc3-a9c6-846a-dcae-2bfd749ca0a6.gif" width=30%>


#材料
1.obniz
2.電線
3.[灯油ポンプ 自動停止型 TP-20R](http://amzn.asia/eqGh8FR)
   -電池カバーを外せばスイッチ部分も見えるため、加工がしやすくおススメです

#構築手順
##1.ポンプとObnizを繋げる
![石油ポンプ.png](https://qiita-image-store.s3.amazonaws.com/0/181708/e434042c-19d1-53ae-cef2-de2dd50cce9f.png)

##2.PCからポンプを起動できることを確認する
[LED (Light)](https://obniz.io/sdk/parts/LED/README.md?iframe=false)
を参考に
-起動時
-ONボタンを押したときに
5秒だけポンプが動くことを確認

LEDを制御するコードを丸写しなので、下記の「led」の部分をポンプと読み替えて下さい

```html:WaterPump.html
<!-- HTML Example -->
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
  <script src="https://unpkg.com/obniz@latest/obniz.js"></script>
</head>
<body>

<div id="obniz-debug"></div>
<h1>LED Switch</h1>
<button id="on">ON</button>
<button id="off">OFF</button>

<script>
  var obniz = new Obniz("OBNIZ_ID_HERE");
  obniz.onconnect = async function () {
    var led = obniz.wired("LED", {anode:0, cathode:1});
    
      led.on();
      await obniz.wait(5000);
      led.off();
    
    
    $("#on").on("click", async function(){
      led.on();
      await obniz.wait(5000);
      led.off();
      
    });
    $("#off").on("click",function(){
      led.off();
    });
  };

  obniz.onclose = async function(){
    $("#on").off("click");
    $("#off").off("click"); 
  };
</script>​
</body>
</html>
```

##3.タイマー起動
-下記を利用して定時起動
>[参考：サーバーレス イベント](https://obniz.io/doc/about_event)

■設定画面のスクリーンショット
![obnizイベント.jpg](https://qiita-image-store.s3.amazonaws.com/0/181708/ae44c613-3190-9a1d-0f1f-cef23263fe77.jpeg)

■つまずいた点
・OBNIZ_ID_HERE、の部分にIDをコードに入れておく必要あり
`var obniz = new Obniz("OBNIZ_ID_HERE");`
・5秒Waitする方法
`await obniz.wait(5000);`

#感想
・Obnizは構想から実装までが早くて便利
・外からポンプを操作できるもの楽しい
・室内から操作するのも便利（暑い日は窓を開けたくないので・・・(笑)）

#今後の構想
・アサガオは1日2回水をあげた方が良いらしいので、イベントを朝夕2回起動するように変更
・土壌湿度センサーをつけて乾燥度をモニタリング
(解決)Xamarin  Prism.Forms でViewModelがインスタンス化されない

Prism.Forms でViewModelがインスタンス化されない問題にはまったのでメモ

個人的にはxaml系のアプリは画面遷移の仕組みが重要と考えており、
下記URLにて紹介されていたPrismの新しい画面遷移の機能を試していました。
https://blog.okazuki.jp/entry/2018/07/17/114318

Prism.Formsがpreview版だったので、
Xamarinのバージョンなどがあっていないせいか
ビルドが通らずいろいろ試行錯誤した結果画面遷移はするようになったものの
ViewModelがインスタンス化されなくなってしまいました。

# 開発環境
|  | Version |
| --- | --- |
|OS|Windows 10 バージョン 1804
|Microsoft Visual Studio Community 2017| 15.7.5
|Microsoft .NET FrameworkVersion| 4.7.03056
|Xamarin|4.10.10.2 (35a01d8dc)


# 開発環境(NuGet Manager)
| Package | Version |
| --- | --- |
|Prism.Unity.Forms|7.1.0.172-pre
|NETStandard.Library|2.0.3
|Xamarin.Forms|3.1.0.561732-pre4

# 問題
Android Simulaterにて実機デバッグ中にViewModel側でブレイクポイントを張っても止まらない。
xaml側でのBindingも全く動作しないため、ViewModelがインスタンス化されていなさそう。

# 原因
Prism.Formsでは、View と ViewModel の命名規則が一致していれば、
自動でViewModelのインスタンスが生成される(紐づけも一緒に行ってくれるみたい)

原因としては、この命名規則が守れていなかった。
しょうもない原因ですが、
ViewModelのファイルを格納するフォルダを手動で作成したので
ViewModelとなっていたことが原因でした。

誤 : ViewModel
正 : ViewModels

```csharp

namespace プロジェクト名.ViewModels // ← namespaceも一致させないといけない
{
	public class SliderSampleViewModel : ViewModelBase
	{

    }
}
```

View側のnamespaceも Views でないといけない。

```csharp

namespace プロジェクト名.Views
{
    public partial class SliderSample : ContentPage
    {
        public SliderSample()
        {
            InitializeComponent();
        }
    }
}
```

# 対策
まとめると以下のように命名規則をそろえる必要があります。

## namespace
  -ViewModels
  -Views

## ViewとViewModelの関連
例) SampleView ⇔ SampleViewModel


# 参考
下記のページがきっかけでインスタンス化されないことに気づけました。ありがとうございました。
http://www.nuits.jp/entry/2017/02/22/120046
Uncaught (in promise)の発生条件と抑制方法

同期にせよ非同期にせよ、普通にエラーをthrowしてcatchしないでおくとブラウザの開発者ツールのコンソールにエラーとして表示されます。

```js
setTimeout(function() {throw new Error('async')}, 1000);
throw new Error('sync');

// Uncaught Error: sync
// Uncaught Error: async　１秒後
```

Promiseのエラーは実際はrejectなので、本質的にはErrorがthrowされた状態ではありません。が、親切なコンソールではエラーハンドルを忘れていた場合（promiseがrejectされるまでに、一回もcatchハンドラーが設定されていなかった場合）に以下のように表示されます。

**親切なコンソール（FirefoxやChrome）**

```js
Promise.reject(new Error('promise!!!'));

// Uncaught (in promise) Error: promise!!!
```

これは開発中にうっかりcatchし忘れるのを防ぐのに大変便利ですので、基本的に**抑制すべきではありません。**

---

しかし、１パターンだけわかっていて抑制したい場合があります。それは「今このpromiseはrejectするが、catchハンドラーがsetされるのはもっと後」という場合です。

```js
var promise = Promise.reject(new Error('promise!!!'));

setTimeout(function() {promise.catch(/* 適切なエラーハンドル */);}, 1000);

// Uncaught (in promise) Error: promise!!!
// 後々エラーハンドルするんだけど、コンソールにはUncaught Errorが出てしまう！
```

これは「promiseがrejectされるまでに、一回もcatchハンドラーが設定されていなかった場合」に表示されるのですから、以下のように即座に一回promiseをcatchしてしまえば抑制できます。

```js
var promise = Promise.reject(new Error('promise!!!'));

// これで抑制できる！！（後で見る人にとっては意味不明なのでコメントでこの記事のURLを貼っておこう）
promise.catch(function(e) {});

setTimeout(function() {promise.catch(/* 適切なエラーハンドル */);}, 1000);

// なにもでない
```

なお、似てるようですが下記の書き方は大変まずいです。

```js
var promise = Promise.reject(new Error('promise!!!')).catch(function(e) {});

setTimeout(function() {promise.catch(/* 適切なエラーハンドル…でcatchできない！！！ */);}, 1000);

// なにもでない
```

これだと変数`promise`には「エラーをすべて握りつぶした後のpromise」が登録されてしまいます。後続のエラーハンドルが全く機能しなくなってしまうので、絶対にやめましょう。
OctoberCMSトラブルシューティング：管理画面で"too many redirects"でページが表示されない

OctoberCMS管理画面で"too many redirects"でページが表示されない現象でハマりかけたので共有。

## TLTR
結論から言うと、`config/cms.php`の下記設定を`false`にすることで解決できるかも。

```php
    /*
    |--------------------------------------------------------------------------
    | Back-end force HTTPS security
    |--------------------------------------------------------------------------
    |
    | Use this setting to force a secure protocol when accessing any back-end
    | pages, including the authentication pages. If set to null, this setting
    | is enabled when debug mode (app.debug) is disabled.
    |
    */

    'backendForceSecure' => false,
```

## 詳細
下記のいずれかの場合、`/backend/*`へのアクセスが自動的に`https`にリダイレクトされる

* `backendForceSecure`が`true`
* `backendForceSecure`が`null`かつ`app.debug`が`false`

親切な機能ではあるが、Webサーバの設定やロードバランサーで`https`へリダイレクトかけていたりしていると不都合が起こるようだ。

なので、別途`https`の対応をしている場合は`backendForceSecure => false`とする。
ローカルからAWSのEC2にファイルをコピー

scpコマンドはファイルやフォルダをSSHで暗号化しながら転送してくれるコマンド。

```console
$ cd ./.ssh/keys
$ scp -i [秘密鍵] [転送するファイルのパス] [EC2ユーザー名]@[パブリックDNS]:[コピー先のパス]
```

(例)

```console
$ cd ./.ssh/keys
$ scp -i expample.pem test.php [EC2ユーザー名]@[パブリックDNS]:~/
```

(参考)
http://kawatama.net/web/1298
DOMの移動・挿入

wordpressの繰り返しカスタムフィールドに入れられたHTMLタグを
PHP側で任意のHTMLタグを挟んだ状態にreplaceして一括出力しようとしていたところ仕様上実現できず苦戦していたのですが、
jQuery側で実装すればよかったようで・・・
知らないまま頑なにムリだと思ってた自分を殴る気持ちで:skull:
初歩的ですがDOMの移動や挿入操作についてまとめました。
（HTMLの組み方がガチャガチャなのですがご容赦ください）
>
##出てくるメソッド
*・prepend*
*・append*
*・before*
*・after*
*・insertBefore*

##HTML側
こんな感じのHTMLがあり、この要素をわちゃわちゃやって・・・
![スクリーンショット 2018-07-22 22.07.21.png](https://qiita-image-store.s3.amazonaws.com/0/133085/78f910aa-ade7-4fca-47ca-3b3a8e7ca0b5.png)




一旦このようにしたいと思います。
![スクリーンショット 2018-07-22 22.07.09.png](https://qiita-image-store.s3.amazonaws.com/0/133085/82f26949-d371-722d-a420-0c1e4dce02f8.png)





##jQuery
```javascript

$function(){
//beforeは指定要素の前に挿入
$('#song').before('<div class="jpop">J-POP</div>');
//prependは指定要素の子要素の先頭に挿入
$('#song').prepend('<h1>天体観測</h1>');
//appendは指定要素の子要素の末尾に挿入
$('#song').prepend('<p class="a_melo">午前二時フミキリで</p>');
//afterは指定要素の後に挿入
$('#song').before('<div class="jazz">JAZZ</div>');
});
```

>補足：閉じタグに関しては自動補完されます。
参考サイト様：[jQueryでタグが自動補完される | cly7796.net](http://cly7796.net/wp/javascript/tag-is-automatically-complemented-with-jquery/)

>このメソッドたちはあくまで要素を挿入・移動するもので、
beforeで`<div class="jpop">`,afterで`</div>`を挿入としたいときは
`$('h1').wrap('<div class="jpop"></div>');`
のようにwrapを使ってあげると必要な要素が全部中に入れられると思います。
wrapAllですべてのh1を中に入れるみたいなこともできるようです。


さらに移動したい要素を移動させます。

```javascript

$function(){
//insertBeforeで要素を後ろに移動させる
$('.c_melo').insertBefore('.a_melo');
)};

```
先ほどのメソッドたちとは要素の指定が逆になり、

```javascript
$function(){
//insertBeforeで要素を後ろに移動させる
$('移動させたい要素').insertBefore('ターゲットになる要素');
)};

```
となります。



**完成！**
![スクリーンショット 2018-07-22 22.06.39.png](https://qiita-image-store.s3.amazonaws.com/0/133085/50baa014-f092-acf7-746d-563ca464fb97.png)


##まとめ
*・prepend*
*・append*
*・before*
*・after*
*・insertBefore*
で要素を移動したり挿入できたりできました
囲いたいなど別目的がある場合は別のメソッドを使用する必要があります（wrapなど）
macOS High Sierra(10.13)とVisual Studio For Macではまったこと

この文章はMac版のVisual Studio(以下はVSと略す)をMacにインストールした際に起きた問題と解決に至った経緯についてまとめます。

もちろんあくまで一事例なので、一概にはいえませんが結論を言いますと、現時点最新のVisual Studio Mac（7.5.3）は出荷時プレーインストールされたHigh Sierra上でただしく動作しない可能性があります、Sierraからのアップグレードを踏んでいれば解決できることを確認しました。

先月Macを新調したので、VSをインストールしました。インストール自体が上手く行きましたが、早速目玉のXamarinFormsでHelloWorldを作ったところ、.NetStandardとの参照ができていないか、まだ1行も書いていないのに一面ビルドエラーです。参照設定弄ったり、VS再インストールなど手を尽くしましたが、一向に解決できず、最終手段としてクリーンな状態で一からやり直そうと思って、OSを再インストールすることにしました。

OSのインストールはまたそれはそれで一苦労ですが、趣旨ずれるのでここでは割愛します。

さてOSを一新しいざ再チャレンジです。それなりに経験積んでいるから万全の態勢で臨んでいたつもりですが、結果的に前述と同じビルドエラーに陥りました。この時点でまだOSとVSしかいませんので、関係者を絞れたことが唯一の救いですね。
そういえば、周りのMac(High Sierra)はそういった事象が起きていないし、世の中を見てもやはり同じ事例は皆無でした。ここであることに思いつく、新調したMacは出荷時にHigh Sierraがプレーインストールされていて、周りのHigh Sierraは古いOSからのアップデートです。

これが原因とは断言できないけど、一応試す価値はあると思って作戦を立てました。まずはOSを一個前のSierraにし、そこからHigh Sierraにアップデートできる状況を作ります。VSはSierraの状態でインストールします。そこでHigh Sierraへアップデートし、最新のXcode9.4(iOSシミュレーターで必須)取得。なんとこれで念願のXamarinFormsのHelloWorld
ようやく対面することができました。

順番は私の場合でうまく行きましたが、ストレージを何回も消去する危険な作業です。皆さんはうまくいく保証はできませんので、くれぐれも自己責任でお願いします。
railsでformを使わずにhttpリクエストを送る方法

##httpリクエストを送るには
`Net::HTTP::Post`(渡したいデータを入れる)を引数として、`Net::HTTP`(Post,Get先の情報を入れる)の`request`メソッドを発火させる
##コード例
```ruby:test.rb

class Test

  require 'net/http'
  require 'net/https'
  require 'uri'

  def test
    #PostまたはGet先のURL
    uri = URI("https://example.com")
    #Net::HTTPのインスタンスを生成
    http = Net::HTTP.new(uri.host, uri.port)
    
    #ssl(https)を利用する場合はtrueに
    http.use_ssl = true
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE

    req = Net::HTTP::Post.new(uri.path)

    #リクエストヘッダ
    req['ヘッダフィールド'] = '内容'

    #送りたいデータを格納
    req.set_form_data({'first' => 'test',
     'second' => 'test2'
     })

    #レスポンスデータの受け取り
    result = http.request(req)
    
    #デバッグ
    puts result
  end
end
```

###補足
間違っている箇所や不足点などがあった場合、お手数ですがコメントにてご指摘お願いします。
Raspberry Pi用の激安温度センサーモジュールをlm-sensorsから使う

eBayで電子部品を検索していたら、下記のようなRaspberry Piに直接差せる温度センサーモジュールが1ドル程度で売られているのを発見しました[^1]。

![lm75a.jpg](https://qiita-image-store.s3.amazonaws.com/0/46856/51b39de8-3dd4-af89-f7bc-d057322e0565.jpeg)

激安が大好きな私は大喜びで買ったのですが、世間的な知名度はあまり高くないような気がします。本稿ではこのセンサーモジュールの使い方について紹介します。

[^1]: 「LM75 Raspberry」で検索すればすぐ見つかるはずです

## これは何

上記のモジュールは、温度センサーNXP LM75AとI2Cアドレス設定用のプルアップ抵抗を実装したもので、Raspberry Piに直接差せるピン配置になっているのが特徴的です。

|1  |2  |3  |4  |5  |
|---|---|---|---|---|
|VDD|SDA|SCL|未接続|GND|

Raspberry Piのピン配置に合わせるために4番目のピンが結線されていないのがアツいですね。

実際に差すと下記の写真のようになります。

![1341fd20-97f5-bac0-093f-e7d8abc6648a.jpeg](https://qiita-image-store.s3.amazonaws.com/0/46856/141d7437-bd89-4612-d5bb-20f3bcdfc2d5.jpeg)


もちろんセンサーを単体で買ってきてブレッドボードで接続しても温度は取れますが、このように本体に直接差せると取り回しが断然楽なのでお勧めです。

ただし、外気温を測りたいという場合にはお勧めしません。というのも、上の写真を見ればわかるように温度センサーとSoC（≒CPU）の距離が近すぎて、SoCの発熱の影響をもろに受けてしまうのです。Raspberry Piが熱くなりすぎていないか検出する目的で使うのが良さそうです。

LM75Aの仕様は下記の通りです（[データシート](https://www.nxp.com/docs/en/data-sheet/LM75A.pdf)から抜粋）。

- 電源電圧: 2.8Vから 5.5V
- 動作温度範囲:-55°C から +125°C
- 通信方式: I2C
- 温度分解能:0.125℃

## 準備

```
$ sudo raspi-config
(I2Cを有効化）
$ sudo apt install i2c-tools
```

## 使い方

このモジュールはRaspberry Piの1,3,5,7,9番ピンに接続して使うようです[^2]。

[^2]: eBayの販売ページには説明が一切書いていないので推測で使うしかないんですよね…。

本モジュールのI2Cアドレスは0x48です。これは `i2c-detect` でも確認できます。

```
$ i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
```

また、 `i2cget` コマンドで生の測定値を取り出すこともできます。

```
$ i2cget -y 1 0x48 0x00 w
0xe029
```

ただ、今回は自力で頑張りたいわけではないので、hwmonドライバの管理下に置いて利用しましょう。 `/boot/config.txt` の末尾に下記のように書いてリブートします。


```:/boot/config.txt
dtoverlay=i2c-sensor,lm75,addr=0x48
```

再起動後、`dmesg` で温度センサーが認識されているかどうか確認します。

```
$ dmesg | grep hwmon
[    3.961488] lm75 1-0048: hwmon0: sensor 'lm75'
```

## lm-sensorsで確認

センサーがhwmon管理になるとlm-sensorsから利用できます。

```
$ sudo apt-get update
$ sudo apt-get install lm-sensors
$ sensors
lm75-i2c-1-48
Adapter: bcm2835 I2C adapter
temp1:        +41.8°C  (high = +80.0°C, hyst = +75.0°C)
```

バッチリ温度が取れました。あとはCPU負荷をかけて温度変化を確認したり、値をグラフ化するなどして楽しみましょう。

## 参考URL

- https://www.raspberrypi.org/forums/viewtopic.php?t=177236
- https://github.com/raspberrypi/linux/tree/rpi-4.14.y/arch/arm/boot/dts/overlays
【しずおかアプリ部】活動ログ　第52回

![しずおかアプリ部2.jpg](https://qiita-image-store.s3.amazonaws.com/0/88105/ee800648-fa25-1829-20c3-bad9c2055d35.jpeg)

##■公式サイト##
[http://monolizm.com/shizuoka-app](http://monolizm.com/shizuoka-app)
「Slack」も利用しています。 shizuapp で検索し、ご参加ください。

##◾️参加申し込み##
しずおかアプリ部への参加申し込みは[こちら](https://shizuoka-app.connpass.com/)から

##■タイトル##
第５２回


##■開催日##
２０１８年７月２１日（土）　１６：００～１８：００


##■参加者##
１４名


##■活動内容（概要）##
・部員さんプレゼン： C#と時の部屋～メソッドとデバッグ～
・部員さんプレゼン： C#と時の部屋～VR180についてと作り方～
・菅原プレゼン：「Unity始めるよ 〜これは便利！Unity小ネタ集〜」

![IMG_0218.jpg](https://qiita-image-store.s3.amazonaws.com/0/88105/8876786e-25e1-c34a-353b-ddd13cef6a5c.jpeg)



##■活動内容（詳細）##

【実演・プレゼン】


**・部員さんプレゼン： C#と時の部屋～メソッドとデバッグ～**
　C#での開発について解説。
　　C#は開発環境が強力であることも魅力の一つ。
　　VisualStudioの強力なデバッグ機能を使うことで、開発速度アップ間違いなし。
　　環境構築の仕方を実践解説した。
　　![csharp52.jpg](https://qiita-image-store.s3.amazonaws.com/0/88105/ef7ceb0a-6cae-545f-ce27-f1e81b603c8b.jpeg)


　　その他、Uniryプロジェクトを使って初心者向けにプログラムの基礎的な内容も紹介。
　　![csharp52.jpg](https://qiita-image-store.s3.amazonaws.com/0/88105/c47b62c0-6530-377e-244b-aa8964082835.jpeg)


　[資料](http://monolizm.com/sab/pdf/第52回_プレゼン資料(CSharpと時の部屋「1.型と演算」).pdf)　
 　[プロジェクト一式](http://monolizm.com/sab/src/CSharpWorld.zip)　
<hr>

**・部員さんプレゼン： VR180についてと作り方**
　「VR180」について紹介。
　　■「VR180」とは？
　　前方180度分の風景を見ることが出来るVRフォーマットで、
　　主にスマホのYoutubeアプリに対応している。

　　■「VR180」のメリット
　　360度に比べて、見せる必要のない画像を見せなくすることで、見せたい画像に視線を集中させることが出来る

　　■「VR180」動画の作り方
　　専用カメラを使うほか、Theta2台でも可能。
　　Theta2台を使ったVR180動画の作り方を解説。

　　![vr52.jpg](https://qiita-image-store.s3.amazonaws.com/0/88105/41fba09a-4f1e-d1c7-50ca-1f47f5c9ecc8.jpeg)


　 [資料](http://monolizm.com/sab/pdf/第52回_プレゼン資料(VR180についてと作り方).pdf)

<hr>

**・菅原プレゼン：「Unity始めるよ 〜これは便利！Unity小ネタ集〜」**

　知っているようで知らない便利なテクニックを紹介しました。

　・ショートカットキー
　・シーンビューでのカメラ操作
　・シーンビューでのオブジェクト操作
　・インスペクタビューでの小技
　・ヒエラルキービューでの小技
　・プロジェクトビューでの小技

　![suga52.jpg](https://qiita-image-store.s3.amazonaws.com/0/88105/5709d29a-1710-5b9b-34a7-4e903350d088.jpeg)


　これを覚えることで、Unity上での編集速度がアップすること間違いなし！
　学習コストは低いので是非覚えてください！


　 [資料](http://monolizm.com/sab/pdf/第52回_プレゼン資料(これは便利！Unity小ネタ集) .pdf)　
<hr>
【SQL】インデックスの基本知識まとめ

#インデックスとは何か
- DBで作成できる索引情報のこと。
- インデックスの存在する列に対して検索が行われた場合、DBMSはインデックスの使用を試みる為、高速になることが多い。

## どのような列に対してインデックスを作成すべきか
### 一般的にインデックスの効果が高い列
- WHERE句でよく使われる列
- ORDER BY句でよく使われる列
- JOINの結合条件によく使われる列

## 基準となる概念
- カーディナリティ
- 選択率

### カーディナリティとは
- 値のばらつき具合を示す概念
- もっとも高いのはプライマリーキーなど一意キーの列

### 選択率とは
- 選択率 = 絞り込んだ件数 / 総データ件数
- 具体的な閾値は5%~10%前後
- 5%未満で絞り込める条件であればインデックスを貼る価値がある(かも)

## 選択率の高いケースの対処法
### インデックスオンリースキャン
- 必要な列をインデックスするだけで充足する場合にテーブルへのアクセスをスキップする技術
- 特定の複数列をカバーするインデックス(カバリングインデックス)を作成
- ひとつのインデックスに含められる列数には限りがある
- 更新時にテーブルのオーバーヘッドを増やす

###データマート(サマリテーブル)を作成
- データ鮮度(どのタイミングで同期するか)の運用上の難しさや、テーブルサイズ(オリジナルとのサイズとあまり変わらない場合)によっては効果が薄い、など副作用も考慮する必要がある

##インデックス作成のデメリット
- 索引情報保存のため、ディスク容量を消費する
- テーブルのデータ変更があるとインデックスの書き換えも必要となり、INSERT,UPDATE,DELETE文のオーバーヘッドが増す。


### その他
参考図書 : [SQL実践入門──高速でわかりやすいクエリの書き方](http://amzn.asia/bjYs5bj)
参考図書 : [スッキリわかるSQL入門](http://amzn.asia/d/diFEu7A)
AtomPubを使ってPythonでライブドアブログに画像投稿する

#はじめに
API経由でライブドアブログに画像を投稿する際、結構苦戦してしまったので備忘録を残しておきます。

#リソースURI

ライブドアブログでは、以下のリソースURIが用意されています。
（詳しくは http://help.blogpark.jp/archives/52288925.html を参照）

- http://MEMBER_PROVIDER.blogcms.jp/atom/BLOG_PROVIDER/BLOG_NAME/article (記事のコレクションURI)
- http://MEMBER_PROVIDER.blogcms.jp/atom/BLOG_PROVIDER/BLOG_NAME/article/ARTICLE_ID (記事のメンバURI)
- http://MEMBER_PROVIDER.blogcms.jp/atom/BLOG_PROVIDER/BLOG_NAME/category (カテゴリ文書URI)
- http://MEMBER_PROVIDER.blogcms.jp/atom/BLOG_PROVIDER/BLOG_NAME/image (画像のコレクションURI)
- http://MEMBER_PROVIDER.blogcms.jp/atom/BLOG_PROVIDER/BLOG_NAME/image/IMAGE_ID (画像のメンバURI)

画像をポストする際は、画像のコレクションURIをエンドポイントに指定します。

#使用したライブラリ

```python
#WSSE認証のために使用 
import datetime
import hashlib
import random
import base64

#HTTPリクエストのために使用
import urllib3
```

#コード

```python
def create_wsse(USER_ID,API_KEY):
    created = datetime.datetime.now().isoformat() + "Z"
    b_nonce = hashlib.sha1(str(random.random()).encode()).digest()
    b_digest = hashlib.sha1(b_nonce + created.encode() + API_KEY.encode()).digest()
    s = 'UsernameToken Username="{0}", PasswordDigest="{1}", Nonce="{2}", Created="{3}"'
    return s.format(USER_ID, base64.b64encode(b_digest).decode(), base64.b64encode(b_nonce).decode(), created)

def post_image(img_path,USER_ID,END_POINT,API_KEY):
    http = urllib3.PoolManager()
    with open(img_path, 'rb') as fp:
        binary_data = fp.read()
        r = http.request(
            'POST',
            END_POINT,
            body=binary_data,
            headers = {
                'X-WSSE': create_wsse(USER_ID,API_KEY),
                'Content-Type' : u'image/jpeg'
            })
        return r
```

WSSE認証の部分は http://thirotan.blog.jp/archives/1068967144.html を参考にさせて頂きました。
このpost_imageに画像のパス、ライブドアブログのユーザーID、エンドポイント、APIキーを入れれば画像をポストできます。

```python
USER_ID = 'YOUR_USER_ID'
API_KEY = 'YOUR_API_KEY'
ENDPOINT_IMAGE = 'http://livedoor.blogcms.jp/atom/blog/{user_id}/image'.format(user_id=USER_ID)

req = post_image('path/to/img',USER_ID,ENDPOINT_IMAGE,API_KEY)
print(req.data.decode("utf-8"))
```
Pythonについて vol10.関数

今回は関数(function)です。繰り返し同じ処理をしたいときや、ソースを見やすくするために使います。
極論を言うと関数なんてなくてもプログラムは動くんですが、プログラムに芸術を求める人はやっぱり関数を使います。（嘘

簡単な例です。

```py
def hello():
    print('hello')

hello()
```

->helloと表示されます。

もうちょっと関数の恩恵を受けられるように、関数を使ってみんなだいすきフィボナッチ数列を表現します。

```py
def fin1(a,b):    # 引数を2個受け取って足し算結果を返す関数
    return a+b

x=[0, 1]    # フィボナッチ数列の初期値は0と1
for i in range(2,10):    # とりあえず10個出してみる。
    x.append(fin1(x[i-2],x[i-1]))    # 先ほどの関数を使って数列を作っていく

print(x)    # 結果表示
->[0, 1, 1, 2, 3, 5, 8, 13, 21, 34] # ちゃんと出ました。
```

もうちょっと複雑な感じで書くと、

```py
def fin2(n): # 引数分だけフィボナッチ数を返す関数
    x=[0,1]
    for i in range(2,n):
        x.append(x[i-2]+x[i-1])
    return x

print(fin2(10))
->[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
```
という感じになります。

これらのプログラムは、

```py
n=10
x=[0,1]
for i in range(2,n):
    x.append(x[i-2]+x[i-1])

print(x)
```
と、関数を使わなくても全然書けちゃいます。

でもfin1であれば足し算の結果を、fin2であればフィボナッチ数そのままを返して、
プログラムのメインは短くできるので、なんか見た目が良いですよね。

ご参考　[フィボナッチ数(wikipedia)](https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A3%E3%83%9C%E3%83%8A%E3%83%83%E3%83%81%E6%95%B0)

フィボナッチ数の話を書き過ぎちゃったんですが、関数はAWS CLIを使う時にちょっと便利です。
例えば、DMSタスクを起動するコマンドを実行する時に、StartReplicationTaskTypeが'start-replication'固定だとしたら、

```py
def start_rep_task(dms_task_arm):
    dms_client.start_replication_task(
        ReplicationTaskArn=dms_task_arm,
        StartReplicationTaskType='start-replication'
    )

### 中略 ###

for dms_task_arm in dms_task_list:    #dms_task_listにタスクのARMが入っている。
    start_rep_task(dms_task_arm)
```

となりなんとなくスッキリしてますよね？もし関数を使わないでやると、

```py
for dms_task_arm in dms_task_list:
    dms_client.start_replication_task(
        ReplicationTaskArn=dms_task_arm,
        StartReplicationTaskType='start-replication'
    )
```

となり、なんか見辛いですよね。そんなことないか。。。
vim エディタを開発環境用にカスタマイズしよう♪ ( CentOS7 )

# vim エディタを開発環境用にカスタマイズするよ♪ ( CentOS7 )
- **Step ① : vim 用 syntax color file のダウンロード**
 - Dark Visual Studio colorscheme for Vim
 
```console:command
wget https://github.com/termNinja/vim-vstudio-theme/archive/master.zip
unzip master.zip
cd vim-vstudio-theme-master/
ls -R
.:
README.md  vstudioDark.vim
```
- **Step ② : vstudioDark.vim を, vim 設定フォルダに格納する**

```console:command
cp -vi --preserve=all ./vstudioDark.vim /usr/share/vim/vim74/colors/
ls /usr/share/vim/vim74/colors/vstudioDark.vim
----------------------------------------------
/usr/share/vim/vim74/colors/vstudioDark.vim
----------------------------------------------
```
- **Step ➂ : ホームディレクトリに, vim 設定ファイルを作成する**
 - 開発環境用にカスタマイズする設定を書きます
 - 構文の色に先ほどダウンロードした, vstudioDark を使うように設定します

```console:command
cd ~
touch .vimrc
vi ./.vimrc
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
" タブ幅をスペース 4 つ分にする
set tabstop=4
" タブを半角スペースで挿入する
set expandtab
" vim が自動で生成する ( 読み込み時など ) スペース 4 つ分にする
set shiftwidth=4
" 改行時などに自動インデントを行うようにする
set smartindent
" 空白文字の可視化
set list
"可視化した空白文字の詳細な表示設定
set listchars=eol:↓,tab:>\ ,extends:<
" 0 で始まる数値を, 8 進数として扱わないようにする
set nrformats-=octal
" ------------------------------------------------------------------------------
" 全角スペース・行末のスペース・タブの可視化
if has("syntax")
    " 構文ごとに文字色を変化させる
    syntax on

    " PODバグ対策
    syn sync fromstart

    function! ActivateInvisibleIndicator()
        " 下の行の"　"は全角スペース
        syntax match InvisibleJISX0208Space "　" display containedin=ALL
        highlight InvisibleJISX0208Space term=underline ctermbg=Blue guibg=darkgray gui=underline
        "syntax match InvisibleTrailedSpace "[ \t]\+$" display containedin=ALL
        "highlight InvisibleTrailedSpace term=underline ctermbg=Red guibg=NONE gui=undercurl guisp=darkorange
        "syntax match InvisibleTab "\t" display containedin=ALL
        "highlight InvisibleTab term=underline ctermbg=white gui=undercurl guisp=darkslategray
    endfunction

    augroup invisible
        autocmd! invisible
        autocmd BufNew,BufRead * call ActivateInvisibleIndicator()
    augroup END
endif
"-------------------------------------------------------------------------------
" colorscheme 項目は, autocmd こうもくより下側に記述されていなければならない
" エラーがでてしまう
colorscheme vstudioDark
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

vi インサートモードを抜け, wq で書き込んで終了します。
```
###### ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
上記に書かれている, この中が記述する設定です。
###### ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
- **Step ④ : 動作確認**
 -  vim ./.vimrc で以下に示す画像の様になれば ok
 - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 - ![2018y07m22d_190811812.png](https://qiita-image-store.s3.amazonaws.com/0/261898/a14303fa-27d6-b782-0794-666e155ee834.png)
 - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 - 今回設定したカスタマイズ vim 設定を他のユーザでも引き継ぎたい場合は下記のような手順で行えば良い。
 - ユーザ名などは, 適宜, 各自の環境に置き換えて行って下さい。

```console:command
cp ./.vimrc ../home/lpicstudy
chown -R lpicstudy. ../home/lpicstudy
```
###### ( ここでは ) コピー元は, 先ほど作成した, .vimrc のフォルダを指定しています。
論文まとめ：Medical Image Synthesis with Context-Aware Generative Adversarial Networks

#はじめに  

ちょっと古いがGANsを用いたCT画像の生成タスクに関する先駆けとして、以下の論文をまとめてみる。
[1] D. Nie, et. al. "Medical Image Synthesis with Context-Aware Generative Adversarial Networks"

arXivはこちら
https://arxiv.org/abs/1612.05362

# 一言で言うと  
GANsを階層的に繋げたモデルでMRI画像からCT画像を生成させた

<img width="302" alt="Medical_Image_syn_img02.png" src="https://qiita-image-store.s3.amazonaws.com/0/116706/a0e26cc8-c1e9-e77b-f87c-0e6589909c81.png">
[1]のFigure 1より
左のMRIから右のCT画像を生成する

# アーキテクチャ  
##１層のモデル
１層のGANs部分は以下のようなモデル。
<img width="661" alt="Medical_Image_syn_img03.png" src="https://qiita-image-store.s3.amazonaws.com/0/116706/314f408d-bc24-3c6d-79fd-644a37b0af54.png">

左から見ていくと、GeneratorにMRIを入れ、CTを生成させる。それをreal CTと比較しlossを計算する。

一方で生成したCT画像、および real CT画像をDiscriminatorに入れ、realかgeneratedかを判定させる。

ベーシックなGANsの形。

## 多層のモデル  
この論文ではACM(Auto-Context model)と呼ぶ多層な形を取り入れる。以下の図

<img width="509" alt="Medical_Image_syn_img04.png" src="https://qiita-image-store.s3.amazonaws.com/0/116706/189f6021-046d-d17a-5ec3-5836cf77ba9d.png">

のようにgeneratorを多層にする。

# 目的関数
## Discriminatorのloss
DiscriminatorのlossはノーマルなGANごとく以下のadversarialなもののみ。

```math
L_D = L_{bce}(D(x), 1)+L_{bce}(D(G(x)),0)
```

$L_{bce}$ はbinary cross entropy。

## Generatorのloss
generatorはadversarialなlossにreconstruction error等を加えたもの。

```math
L_G = \lambda_1 L_{bce}(D(G(x)), 1) + \lambda_2 \| Y - G(x)\|^2_2 + \lambda_3 L_{gdl}(X,Y)
```

最後の項 $L_{gdl}$ はgradient difference lossで

```math
L_{gdl} = | |\nabla Y_x | - |\nabla \hat{Y}_x | |^2 + | |\nabla Y_y | - |\nabla \hat{Y}_y | |^2 + | |\nabla Y_z | - |\nabla \hat{Y}_z | |^2
```

と勾配の差をとる。

<font color="red">かきかけ</font>
Clova Extensions Kitでカスタムスキルの実機動作確認する

Clova Extensions Kitを使ってカスタムスキルを作成してから実機確認する際に、
何度やってもスキルが起動しないってことがあるかと思います。
そんな時は、Clova Extensions Kitにある発話履歴から発話した内容を確認してみましょう。

# 発話履歴確認
テストにある［発話履歴］をクリックします。ONにして発話履歴機能を有効にします。
この状態でClovaデバイスに向かって発話するとリアルタイムで発話内容が表示されます。
これで「○○を開いて」と言った場合に正しく認識されているか確認できます。

<img width="1046" alt="スクリーンショット_2018-07-22_11_01_26.png" src="https://qiita-image-store.s3.amazonaws.com/0/154355/3b0c0df6-7672-79a9-ca01-75f3b7ccd327.png">
Laravel 開発環境の構築メモ（GitHub + Sourcetree + Tera Term + Eclipse+ VirtualBox + Vagrant + Laravel Homestead）

# Laravel 開発環境の構築

####1.「GitHub」のアカウントとリポジトリを作成する。
https://qiita.com/rshibasa/items/f62db870ed573ca4dced

####2.「Sourcetree」をインストールする。
http://proengineer.internous.co.jp/content/columnfeature/6983

####3.「Sourcetree」に「GitHub」のリポジトリをクローンする。
https://ics.media/entry/15195/2

####4.「Tera Term」をインストールをする。
http://www.j-oosk.com/teraterm/install/42/
※必須ではない。

####5.「Eclipse（エクリプス）」をインストールする。
https://techacademy.jp/magazine/1620
※本体はここからダウンロード「http://mergedoc.osdn.jp/」

####6.「Sourcetree」でクローンした際に作成されたローカルリポジトリを「Eclipse」にてインポートする。

####7.「VirtualBox」をインストールする。
https://eng-entrance.com/virtualbox-install

####8.「Vagrant」をインストールする。
https://weblabo.oscasierra.net/install-vagrant-onto-windows/

####9.「Laravel Homestead」をインストールする。
https://qiita.com/tosite0345/items/2e5143ee2b7bb31f2ffb
※「Homestead.yaml」ファイル内の「sites」の「to:」に注意、「index.php」のパスを正しく記述できていないと、IPアドレスでアクセスした際に「No input file specified.」と画面に表示される。

以上です。
proxy環境下でcurlコマンドをローカルホスト宛に発行する時にproxyを見ないようにする

# ローカルに立てたサーバに対してcurlでHTTP POST

proxy環境下でHTTP POSTを普通にしている分には大丈夫なのに、ローカルに対して実行すると
エラーが発生してしまいます。
proxy環境下で使っている端末では、proxyの設定をしているので、こうなってしまうのですが、できればproxy関係の設定は変えたくないです。

# そんな時に役立つ --noproxyオプション 

proxyを通したくない場合、curlコマンドのオプションで--noproxy [通したくないドメイン：自分の場合はlocalhost]をいれるとproxyを介さずにローカルのサーバに対してアクセスしてくれます。

```sh
curl -XPOST -F file=@/Users/tottu22/photo.jpg http://localhost:5000/reply --noproxy localhost
```

# さいごに
色々とproxy環境下で端末を使っていると問題が発生します。そしてピンポイントの悩みが検索しても出てこなかったりするので、メモとして残します。
[macOS初心者]Finderのアイコン表示時にファイルを複数選択できない。

Macを使っていてわからないことをメモ程度に書いてゆく。
わかった時はわかったことも書いてゆく。
基本はWindowsユーザなので，なんでできないのか？それともやり方がちがうのか？すらわからないことが多い。
ゆっくり書いてゆくし，ゆっくり解決してゆく。
__ちょっと多くなりそうなので記事を別にして整理してゆくことにします。__

# Finderのアイコン表示時にファイルを複数選択できない。

下記の記事の通り，リスト表示においてはShift + クリックで複数選択が可能である。

http://c-through.blogto.jp/archives/42221693.html

しかし，アイコン表示の時にはShift + クリックでの複数選択ができない。（Windowsは可能）
下記のような単純な矩形ではない場合に，矩形選択ではダメなのでどうすればよいのか？を考えている。

<img width="607" alt="矩形.png" src="https://qiita-image-store.s3.amazonaws.com/0/120396/1ad6ba32-8096-7e5c-dc23-91db5530b8c8.png">

解決法1：矩形選択を何度か継ぎ足す（Shift選択は諦める）
解決法2：大きく矩形選択をしてから，Cmd+クリックでいらないところを消す。（コメントより）

「ところでfinderはアイコン表示の時、どの位置にアイコンが置かれているかをファイルに記録しているので、連ねる選択？のような選択方法に弱いのかも」というご意見をいただいた。
なるほど，そうなのか。まあこういう思想的な部分がわかっているかどうかが重要ですね。

macOS 10.13も，ファインダーのアイコン表示でもシフト＋クリックでの選択／選択解除できるので，なにか設定がおかしくなっているのかも，というご意見をいただきました。設定の直し方などがわかりましたら教えていただけるとありがたいです。


# キーボード関係

PageUp = Fn + 上
PageDown = Fn + 下

https://qiita.com/ryota-murakami/items/bf656c7c6af16f2a1a48

よく考えるとHHKと同じだったけど，Fnキーが右下にしかないので気づかなかった。
zeit/now-cli を使ってpublicなhttps環境へNode.jsアプリケーションを高速デプロイしてドメインまで取得する(タダで)

この blog は下記へ移行しました。しかるべき期間が過ぎたらこちらの記事は削除しようと思います。
https://lidqqq.dev/blog/11722f5e3225f7806f50
初めてGitLabでコード管理してみた

お金をかけずにprivateリポジトリを管理したかったので、[GitLab](https://about.gitlab.com/)を使ってみました。ローカルで作成していたソースコードをpushするところまでやってみます。

# プロジェクトを作る

まずは[GitLab](https://about.gitlab.com/)でアカウントとプロジェクトを作ります。

[GitLab](https://about.gitlab.com/)のトップページの右上の「Sign In/Register」をクリックします。
![1.Home.png](https://qiita-image-store.s3.amazonaws.com/0/113146/a08d65b2-e03a-252b-a858-049cc01b4012.png)

Registerタブをクリックし、今回はGoogleアカウントでSignInします。アカウントはすぐに作れます。
![2.Register.png](https://qiita-image-store.s3.amazonaws.com/0/113146/14f4b6b2-0291-06b9-9542-97f9700d2fee.png)

空のプロジェクトを作ります。
![3.CreateProject.png](https://qiita-image-store.s3.amazonaws.com/0/113146/c9ebfa78-6090-16f2-d8cd-a5c6ba19bb55.png)

てきとうなプロジェクト名をつけて、Privateに設定します。
![4.NewProject.png](https://qiita-image-store.s3.amazonaws.com/0/113146/386ce2b3-e0cc-1401-d061-859445e64254.png)

これで空のプロジェクトが完成です。

# pushする

次に、ローカル環境からpushするために、パスワードを設定します。右上からSettingsをクリックします。
![5.Setting.png](https://qiita-image-store.s3.amazonaws.com/0/113146/5cdd118d-09ad-0019-d616-62d6d7cc1a8e.png)

画面左側から :lock: のアイコンを選び、パスワードを設定します。

![6.UserSetting.png](https://qiita-image-store.s3.amazonaws.com/0/113146/938fb826-2a35-fc68-6f75-9903e5e08f83.png)

![7.Password.png](https://qiita-image-store.s3.amazonaws.com/0/113146/d548b33b-9b4a-a944-51ec-f1d7367f2919.png)


次はローカル作業です。
ターミナルで次の操作を行います。

```
$ git add .
$ git commit -m "Initial commit"
$ git push -u origin master
```
Usernameは、GitLabへのログインに使うメールアドレス。
Passwordは、先ほど設定したGitLabへのログインパスワードです。


以上でGitLabへソースコードをpushできました。快適なGitLab生活を！
Gradle Plugin 3.2でデータバインディングがエラーになった

Android Studio 3.2 Beta4を使い、Gradle Pluginを3.2.0-beta04にしたらデータバインディングがエラーになったのでメモ。
Stable版で修正される可能性があります。

## 今までの書き方
当該のレイアウトXMLのデータバインディングの書き方は以下だった。
**************.entity.Userは、BaseObservableを継承している。

```xml
<variable
    name="nav_header_user"
    type="**************.entity.User" />

<TextView
    android:id="@+id/nav_user_name"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@{nav_header_user.nickname}" />
```

bindingに対して、Kotlinで設定していた。

```kotlin
val binding = DataBindingUtil.bind<NavHeaderMainBinding>(view)
binding!!.navHeaderUser = user
```

## 現象
3.2.0だと、navHeaderUserという名前ではなく、nav_header_userという名前で生成される。
レイアウトXML内のdataのvariableに書いたnameと、生成コードで名前が一致するようになった。

じゃあその名前に変更で直るかというと、今度は生成コード内でまた別のエラーになる。

```text

error: cannot find symbol
        this.mNavHeaderUser = NavHeaderUser;
            ^
  symbol: variable mNavHeaderUser

error: cannot find symbol
        **************.entity.User navHeaderUser = mNavHeaderUser;
                                                    ^
  symbol:   variable mNavHeaderUser
  location: class NavHeaderMainBindingImpl
```

これはデータバインディングで生成されるコードで、ViewDataBindingを継承した \*\*\*\*\*\*Binding に存在しないシンボルを\*\*\*\*\*\*BindingImpl が使用しているようだ。


## 3.2.0での直し方
存在しないシンボルに合わせ、mNavHeaderUserにリネームすると直る。

```xml
<variable
    name="mNavHeaderUser"
    type="**************.entity.User" />

<TextView
    android:id="@+id/nav_user_name"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@{mNavHeaderUser.nickname}" />
```

```kotlin
val binding = DataBindingUtil.bind<NavHeaderMainBinding>(view)
binding!!.mNavHeaderUser = user
````

これでコンパイルが通り、アプリも起動できた。
[Android] 画面上のタップされた位置（座標）を取得する

# 概要
静電気を用いたスタンプをアプリ上で押してもらい、タッチされた座標を照合することでスタンプの整合性を確認する場合など、Android端末の画面上でユーザがタップした位置の座標を取得したい場合があります。その場合、ActivityクラスのonTouchEventまたはdispatchTouchEventメソッドをオーバーライドすることで、タップされた位置の座標を複数取得することができます。

# 用例
```java:Activityクラス
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        this.onTouchEvent(event);

        return super.dispatchTouchEvent(event);
    }

    private void onTouchEvent(MotionEvent motionEvent) {
        // タップされた位置を取得する（指を離さずに動かした場合等は除外）
        if (event.getActionMasked() == MotionEvent.ACTION_DOWN || 
            event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
            StringBuilder builder = new StringBuilder();
            this.coordinates = new ArrayList<>();
            int count = event.getPointerCount();

            // 複数箇所がタップされた場合に対応
            for (int i = 0; i < count; i++) {
                int x = (int) event.getX(i);
                int y = (int) event.getY(i);
                Logger.d("# X: " + x + ", y: " + y + ", PointerID: " + event.getPointerId(i));
                builder.append("(" + x + "," + y + "),");

                Coordinate coordinate = new Coordinate(x, y);
                this.coordinates.add(coordinate);
            }

            Logger.d("# 座標: " + builder.toString());
        }
    }

```

```java:Coordinateクラス
public class Coordinate {
    private int x;
    private int y;

    public Coordinate(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }
}
```
自動ログインしてみるbyRequest&BeautifulSoup

ちとテストでつかう機会があったので備忘。
自動でログインとかできちゃうのはやっぱ感動。
soupのメソッド？っていうのかfind、selectを使いこなしたいもんだ。

```python
import requests
from bs4 import BeautifulSoup

def check_if_login(USER, PASS):
    #セッションをつくる
    session = requests.session()

    
   # ログイン postが必要な項目
    login_info = {
        "signin_id":USER,
        "signin_pass":PASS,
        "signin_submit":"サインイン",
    }
    url_login = "http://XXXXXXXXXXXX/index.html?process=signin"
    #セッションに{URL}と{ログインに必要な項目}をぶち込み
    res = session.post(url_login, data=login_info)
    res.raise_for_status()
    
    #帰ってきたデータをbeautifulsoupに取り込んでハンドリングしやすくする
    soup = BeautifulSoup(res.text,"lxml")
        
    signout_display = soup.find(attrs={'data-url': '/index.html?form_display=signout'}) 
    name_display = soup.find('form',action="index.html?apps=request").find('li', class_="name" ).find('input').get('value')
    address_display  = soup.find('form',action="index.html?apps=request").find('li', class_="adress" ).find('input').get('value')


    print(signout_display, name_display, address_display)

#main
USER = "fefe"
PASS = "loveisover"
check_if_login(USER, PASS)
```

参考URL
https://python.civic-apps.com/beautifulsoup4-selector/
[短文ネタ] for (i=0; (buf[i] = "/proc/self/fd/"[i]); i++);

```C
unsigned i, j;
for (i=0; (buf[i] = "/proc/self/fd/"[i]); i++);
// 以下略
```

muslとかいうライブラリのソースを読んでいたら、こんなコードを見た。
ギョッとするが、おそらく、以下のコードと等価。

```C
unsigned i, j;
strcpy(buf, "/proc/self/fd/");
i = strlen(buf);
```

Cやばい。こわい。
PHP で全角1文字を半角2文字としてカウントしたい（等幅フォント向け）

## TL;DR

CLI アプリの表示など**全角文字に合わせて半角の '`-`' で下線を引きたい**が、文字列の長さが合わない。

> `mb_strwidth` を使う

```php
<?php

$string = 'テスト'; //expect 6 chars length such as '------'
$width  = mb_strwidth($string);
$ul     = str_repeat('-', $width);

echo $string, PHP_EOL;
echo $ul, PHP_EOL;
```
↓
<img width="74" alt="スクリーンショット 2018-07-22 15.35.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/78776/cb5a4e17-dfa3-4708-de03-b212e76ab308.png">

### 注意点

**「全角＝マルチバイト」というわけではない**ため、**全ての全角文字が2文字としてカウントされるわけではない**ことに注意してください。

特に記号を扱う場合は注意が必要です。「✅」（`bin2hex=e29c85`）といった記号や「😀」（`bin2hex=f09f9880`）といった絵文字などは「`mb_strwidth=1`」となりますが、（Mac など）OS や使うフォントによって表示は全角になります。

CUI のシェル画面でテキスト表示した際に描画が崩れる（全角文字に隣の半角が埋もれる）のをみたことがあると思いますが、これが原因です。

また、当然ですが、そもそも等幅フォントを使っていない場合はガタガタになります。

- 「[`mb_strwidth()` は一部の文字が なんで？ という結果になるのが残念なところです](https://qiita.com/k1LoW/items/8a460b61b8ee1560a06c#comment-77c1c6373a7696cb1152)」 @ Qiita


```php
<?php

$string = 'テスト';

echo 'mb_strwidth:', PHP_EOL;
echo $string, PHP_EOL;
echo str_repeat('-', mb_strwidth($string)), PHP_EOL, PHP_EOL;

echo 'strlen:', PHP_EOL;
echo $string, PHP_EOL;
echo str_repeat('-', strlen($string)), PHP_EOL, PHP_EOL;

echo 'mb_strlen:', PHP_EOL;
echo $string, PHP_EOL;
echo str_repeat('-', mb_strlen($string)), PHP_EOL, PHP_EOL;


echo '全角記号で1文字になりうる文字', PHP_EOL;

echoWidth('※');
echoWidth('★');
echoWidth('△');
echoWidth('■');
echoWidth('①');
echoWidth('✅');

echo 'PHP version: ', phpversion(), PHP_EOL, PHP_EOL;

function echoWidth($string)
{
    echo $string, ': ', var_dump(mb_strwidth($string, 'utf-8'));
}

```
- [オンラインで検証する](https://paiza.io/projects/_OkY1gJBo5AjgLiXsrhxPw) @ paiza.IO

### 出力結果

以下は macOS(10.13.5) + MSゴシックで表示した場合の例

<img width="430" alt="スクリーンショット 2018-07-22 15.32.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/78776/ee373105-1fbb-1c7d-8a51-450aca25513e.png">

## 参考文献

- [「全角40文字制限」とはなにか？](https://qiita.com/k1LoW/items/8a460b61b8ee1560a06c) @ Qiita
Pythonについて vol09.try/except

今回は例外処理（エラーハンドリング）です。JAVAで言うところのtry-catchですね。
例えばboto3を使って、AWS CLIを実行して何かエラーがあるとそこでスクリプトは停止してしまいます。
エラーが発生したら、その対処をして、後続に進めたい時にtry-exceptを使います。

```py
try:
    aws.cli.hogehoge
except Exception as err:
    print('error is ### {0} ###'.format(err))
```

短い文ですが、単純にaws.cli.hogehogeを打ったときと出力が異なります。

### try文を使う。
ちゃんとエラーハンドリングされる。

```py
>>> try:
...     aws.cli.hogehoge
... except Exception as err:
...     print('error is ### {0} ###'.format(err))
...
error is ### name 'aws' is not defined ###
```

### 単純に実行。

Tracebackが表示される。

```py
>>> aws.cli.hogehoge
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'aws' is not defined
```
storyboardのsegueで前の画面に戻る方法

画面Aから画面Bにsegueで移動し、segueで前の画面に戻りたいものとする。

１．画面Aに関連付けているクラスに以下のコードを記述する
（中身はなくてOK,名前もなんでもOK）
	
```:swift
@IBAction func back(segue: UIStoryboardSegue) {
}
```

３．画面Bに関連付ける　注,これは動画です、しばらくしたら動きます
![hp4xo-b5645.gif](https://qiita-image-store.s3.amazonaws.com/0/59336/dabc1b82-4850-dc67-41df-0330037f8eb5.gif)

４．完成！
Pythonについて vol08.Boto3

今回のテーマはAWS SDK for PythonであるBoto3です。
この連載の根幹となるテーマです。（あと数回ありますが、それらはおまけです。）

Boto3にはマニュアルというかリファレンスがありますので、詳細はそちらをご覧ください。
https://boto3.readthedocs.io/en/latest/

インストールは、`pip install boto3`で完了です。
使う時は、`import boto3`でインポートしておきます。

Boto3を使う時は認証が必要です。
通常のアクセスキーを使うのであれば、~/.aws/の下に、configかcredentialsにアクセスキー、シークレットキーを書いておけばOKです。

```shell:~/.aws/config　or　~/.aws/credentials
[default]
aws_access_key_id=foo
aws_secret_access_key=bar
```

configとcredentialsで書き方が微妙に違うのでご注意ください。(defaultは上記の通り同じです）
ご参考：https://boto3.readthedocs.io/en/latest/guide/configuration.html

Assume RoleをBoto3で使うこともできます。が、便利な使い方はSTSが使えるIAMロールが付与されているEC2インスタンスで実行する必要があります。(Appendix1に書いておきます。)
なので、自分の場合はSTSが実行できるサーバまでSSHでSTSを実行して、認証情報を取得するクラス(get_sts_assumerole.py:Gsa)を作ってそれを使っています。

取ってきた認証情報は以下のように使います。

```py
import boto3
from get_sts_assumerole import Gsa

# STS設定
creds=Gsa().get_sts_assumerole('mig')
session = boto3.Session(
    aws_access_key_id=creds[0],
    aws_secret_access_key=creds[1],
    aws_session_token=creds[2],
)
```

上記で作ったsessionを使ってコマンドを打っていきます。
先のboto3のリファレンスに各サービス毎のページがあります。例えば、
EC2：https://boto3.readthedocs.io/en/latest/reference/services/ec2.html
RDS：https://boto3.readthedocs.io/en/latest/reference/services/rds.html
などです。
目的のサービスのページを見ると、そのサービスで利用できるメソッドが確認できます。

例えば、EC2のインスタンス情報を表示するdescribe_instancesメソッドを利用して、

```py
ec2 = session.client('ec2') # ec2クライアントを作成
response = ec2.describe_instances()
```
とすると、instanceの情報がresponseに入ります。

responseにはJSON形式っぽい何かで情報が入っているので、 # 前回の記事参照
目的のものだけを抜き出すために、

```py
for res in response['Reservations']
    print(res['Instances'][0]['InstanceId'])
```

とやったり（標準出力に出力）

```py
with open('result.json', 'w') as f:
    json.dump(response, f, default=json_serial, indent=4)
```

とやったり（ファイル書き出し）します。※json_serialについては前回の記事参照

Boto3というか、AWS CLIには一回で出せる出力量が決まっているものがあります。
そういう時はNextTokenを使って、複数回取得します。

例えば、先ほどのdescribe_instancesであれば、

```py
response = ec2.describe_instances(
    MaxResults=100,
    NextToken=''
)
```

と実行します。続き(101個目以降)がある場合はresponse['NextToken']に続きを取得するためのtokenが入ります。
これを使って以下のようなwhile分でループをまわして、レスポンスを取得します。

```py
ec2 = session.client('ec2')
token = ''

while token is not None:
    response = ec2.describe_instances(
        MaxResults=10,
        NextToken=token
    )
    for res in response['Reservations']:
        print(res['Instances'][0]['InstanceId'])

    if 'NextToken' in response:
        token = response['NextToken']
    else:
        token = None
```

MaxResultsとNextTokenはサービスによって呼び方が変わるみたいです。
RDSだとMaxRecordsとMarkerのようです。

# Appendix
Boto3から直接STSを実行して認証情報を取得する時は、以下のようにconfig/credentialsを書いておけば良いみたいです。

```shell:~/.aws/credentials
[development]
aws_access_key_id=foo
aws_access_key_id=bar
# 原文ママ。2行目は aws_secret_access_keyだと思われる。
```

```shell:~/.aws/config
[profile crossaccount]
role_arn=arn:aws:iam:...
source_profile=development
```

https://boto3.readthedocs.io/en/latest/guide/configuration.html#assume-role-provider
GETメソッドでリクエストボディを指定してはいけない(Swift)

## はじめに

[この記事](https://qiita.com/uhooi/items/68939999c2c31e5f5557) で「http://」のAPIを実行できるようにしたのですが、まだ以下のエラーが発生して落ちます。

エラーを転記します。

http://zipcloud.ibsnet.co.jp

```log:zipcloud.ibsnet.co.jp
The data couldn’t be read because it isn’t in the correct format.

domain: "NSCocoaErrorDomain" - code: 3840

"NSDebugDescription" : "Invalid value around character 0."
```

https://postcode-jp.appspot.com

```log:postcode-jp.appspot.com

HTTP load failed (error code: 303 [4:-2201])

finished with error - code: 303
```

このエラーでいくらググっても解決できなかったので苦戦しました。
やっと解決できたのでその方法を紹介します。

## 環境

- OS：macOS High Sierra 10.13.1
- Xcode：9.2
- Swift：4.0.3

## 原因

タイトルで原因を言っていますが、 `URLRequest.httpBody` に値を入れているためでした。
基本的にGETメソッドではリクエストボディを指定してはいけなく、APIによってはエラーが発生します。

空を指定してもエラーが発生しました。

```swift
// bad
var request = URLRequest(url: url)
let requestBody = [:]
request.httpBody = try! JSONSerialization.data(withJSONObject: requestBody, options: [])
```

## 解決策

`URLRequest.httpBody` に値を入れずにGETメソッドを実行するとエラーが発生しなくなります。

リクエスト作成メソッドを汎用的にする場合、引数が `nil` でないときのみリクエストボディに値を代入するようにするといいです。

```swift:HTTPClient.swift
    static private func createRequest(requestBody: [String: Any]?, url: URL, method: HTTPMethod, contentType: ContentType) -> URLRequest {
        var request = URLRequest(url: url)
        request.httpMethod = method.rawValue
        request.setValue(contentType.rawValue, forHTTPHeaderField:HeaderField.contentType.rawValue)
        
        // リクエストボディをディクショナリからJSONへシリアライズする
        if let _requestBody = requestBody {
            do {
                request.httpBody = try JSONSerialization.data(withJSONObject: _requestBody, options: [])
            } catch let error {
                print(error.localizedDescription)
                fatalError()
            }
        }
        
        return request
    }
```

GETメソッドでリクエストを作成するときは常にリクエストボディを `nil` で渡します。

```swift:HTTPClient.swift
    static public func get(urlString: String, queriesDictionary: [String: String]?) -> Any {
        let url = createUrl(urlString: urlString, queriesDictionary: queriesDictionary)
        return runHTTPMethod(requestBody: nil, url: url, method: .get, contentType: .json)
    }
```

## 結論

GETメソッドは用法・用量を守って正しく使い、リクエストボディを指定しないようにしましょう。
リクエストボディを送信したい場合、PUTメソッドなどを使います。
よく見る簡単な css animation tips[初心者向け]

```html
<html>
 <head>
  <meta charset="utf-8"/>
  <link rel="stylesheet" type="text/css" href="./assets/css/pc.css">
 </head>
 <body>
  <p class="label"><span>ラベルが入ります。</span></p>
 </body>
</html>
```

```../assets/css/pc.css
span {
 position: relative;
 display: inline-block;
}

span:before {
 content: '';
 position: absolute;
 background-color: black;
 animation: revealLTR 1s ease;
 height: 100%;
}

@keyframes revealLTR {
 0%{
  width: 0;
  left: 0;
 }
 65%{
  width: 100%;
  left: 0;
 }
 100% {
  width: 0;
  left: 100%;
 }
}
```

### 結果

![anim.gif](https://qiita-image-store.s3.amazonaws.com/0/9026/a96880bb-5628-6db4-2123-65abd4704ee1.gif)