OmniAICreator/Qiita-1.07M huggingface dataset:
decodeJwtResponse

```
  function decodeJwtResponse(t) {
    let token = {};
    token.raw = t;
    token.header = JSON.parse(window.atob(t.split('.')[0]));

    var base64 = t.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
    var jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));

    token.payload = JSON.parse(jsonPayload);
    return (token)
  }
```
【SwiftUI】Textに長押しメニューを追加する

# はじめに
iOS15からTextに長押しできる機能が追加されてました。
意外と使えそうなので記録しておきます。

# サンプル
![スクリーンショット 2022-12-09 18.46.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/3a4b28c9-f43c-9540-38e9-c6e637ad1ae2.png)

# 実装
```diff_swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("テキスト")
+           .textSelection(.enabled)
    }
}
```

# おわり
他にも良さそうな機能があったら記事にします
【Rails】UNKNOWN ACTION ERROR エラーの解決方法。超初歩的なミスだった話

# エラー内容
* `UNKNOWN ACTION ERROR`　と表示された。
ええー初めてみるぞこれ。なんだ！？

# 原因
* 超初歩的なミスをしていました。
* comments_controller.rbの一番上が以下のようになっていました。そう、2行目のendが間違いです！汗

```comments_controller.rb
class CommentsController < ApplicationController
end

def create
# 以下省略
```

# 解決方法
* 正しくは以下の通り。endは一番下。アクションはその中に入れるように注意しましょう。
* controllerの一番上の誤記は盲点で意外と時間がかかってしまったものです。
 
```comments_controller.rb
class CommentsController < ApplicationController


def create
  @comment = Comment.create(comment_params)
if @comment.save
  redirect_to prototype_path(@comment.prototype) 
else
  @prototype = @comment.prototype
  @comments = @prototype.comments
  render "prototypes/show"
end
end

private
  def comment_params
    params.require(:comment).permit(:context).merge(user_id: current_user.id, prototype_id: params[:prototype_id])
  end

  end
```
未経験からのアルバイト業務で8ヶ月経った感想とこれから

# はじめに

タイトルのままですが、未経験でアルバイトを開始してから8ヶ月が経ちこれまでの期間で感じたことやこれからどうしていきたい的なことをまとめます。
※感じたことをまとめているだけなので、アドバイス的なことはあまりないです！　テキトーに読んでもらえればと思います。。。。

また自分と同じ文系の大学生で、就活前にアルバイトなりインターンへの参加を検討している方の参考になればなと思います！（正確には自分の業務はエンジニアというよりコーダーに近いですが....）

# 参考程度に

入った時の学習歴は約三ヶ月ほどで、**HTML & CSS**、**SCSS**、**JavaScript**をprogateやドットインストールで触れた後に簡易的なwebページを数個作成した程度です。またgit操作についての学習もしていました。(progateやった後に記事のこれだけは覚えろ的なやつをやってみた程度です)

つまりど素人です:santa:

# 主な業務内容

* 運用・保守（リライト、スタイル崩れの編集）
* 記事作成(wordpressを用いて)
* ABテストの実装 (Vanilla JS、scssを用いて)

# 1. 前提知識を知らなさすぎる

単純な知識不足からコミュニケーションを取るのが難しいです。

例えば、社員の方から「僕の環境使っていいよー」　と言われた時に「いや、どうやって使うんだ....」　となり質問したら「リモートに接続できるようにconfigファイルで設定しないといけない」と返ってきて「リモート？　configファイルってどこ...？」という感じで一つのことに何回も質問してしまいます。（今もしてしまいますが、初期はずっとそんな感じでした）

またデザイナーの方に「アス比合っていないので直してください！」と言われて「承知しました🙇」と返してから、Googleで”アス比とは”で検索して「画像の比率を直せばいいのか！」と分かりそこから修正開始みたいなこととかもありました。

要は、何かを依頼される・教えてもらったりした時にそれを直ぐには返せない・理解できないことが多々ありコミュニケーションを円滑に進めることができませんでした😢

今思うと入るまでに、最低限のWebの基礎知識や使用pcのショートカットキーやトラックパッド、git操作などを抑えていくのはマストだと思います。
あと、**素直に質問する**ことも大事です。
調べてもわからないものはわからないので。

# 2. 顧客目線・保守性が高いコードが書けない

仕様書通りに実装することが精一杯で、実装の途中で「仕様書には書いていないけどクリック可能範囲はシェブロンマークだけでなくそのブロック全体にしよう」とかまで気づけず、コードレビューで「クリック範囲狭くない？」みたいな感じで返ってきて再度修正作業に入ります。

またクラスの命名が分かりにくいとかコメントアウトしていないために、どういう処理がしたいのかレビュワーが分かりにくいとか....
その他にも別ファイルで使用されているものを使えばいいのに、全く同じものを新しく作ってしまうとか....

いきなり実装するのではなく、ある程度**算段をつけてから実装する**のが大切です。
また、ただ実装するだけではなく、自分がこのサイトを訪れた時に不便に感じないかや可読性が高いコードを書けるようにならないといけません。

# 3. エンジニアって結構喋ってる

入るまではエンジニアの業務はずっとコードを書いているもんだと思っていました。

しかし実際に入ってみてコードを書く時間というのは1日の業務の内で半分くらいですし、社員の方はミーティングが多いので、自分よりさらにコードを書く比率が少ないのかな？と思っています。

正確にはミーティングでどう実装していけばいいか（作戦会議みたいなもん）を話し合っているので、コードを書く時間が少ないかもしれませんがコードに向き合っている時間は多いかもしれません。

またエンジニア同士で会話するだけではなく、デザイナーやマーケティングの方達ともコミュニケーションを取らないと開発が進めれませんし案外そういった時間の方が多かったりします。

つまり結構喋るというか、普通にコミュニケーションを取るので円滑にコミュニケーションを取る訓練が大切なのかなぁと感じています....（**言いたいこと整理してから質問する**）


# 4. まとめ

以上、働き始めてから業務中に感じたことをまとめてきました。

記事内では、〜ができないみたいに暗い話が多かったかもしれませんが自分はそのことをネガティブに捉えておらず寧ろいい経験をさせてもらっていると感じています。(一緒に働く社員の方たちも温かく接してくれますし、特にメンター的な社員の方は**常に丁寧に教えてくれるので**とても感謝しています)

実際に働くことでこれからの自分のキャリアの進め方や何を学習したらよいかの解像度が上がります。
これは独学だとなかなか難しいと思うので、思い切って**アルバイトなりインターンに応募してみる**ことを強くお勧めします。

▼ Keisuke Honda のお言葉

https://twitter.com/kskgroup2017/status/1596072416043683841

また、働き始めたら嫌でも周りの方たち（自分より技術力が遥かに上な社員の方たち）と比べてしまうので、「早く追いつかなきゃ」みたいな気持ちにもなれます。（自分の場合は）←比べすぎはよくないけど
「自分は他と比べたりしない」的なスタンスでいきたい（カッコつけたい）ですが、自分は成熟した人間ではないのでそう思っちゃいます。
その気持ちが一つの頑張る理由になっているからまだいいですけど（）

# これからについて（補足）

学生のうちはとりあえず技術を磨きたいです。
フロントエンドエンジニアを目指していますので、JavaScript、TypeScript、React、Nextあたりの学習に特に力を注いでいきたいです。

▼ Keisuke Honda のお言葉

https://twitter.com/kskgroup2017/status/1591976794668019712


もし一年後もアドカレに参加しているのであれば、この記事と違い「〜ができるようになった！」が多発するような記事を書けれるようにします。

いい意味で、「この頃の自分、何もわかってないなぁ」とか思えたり、技術的なことについて書けていればれば尚よしです👌

かなり先になると思いますが、エンジニア職だけではなくITコンサルやエンジニア採用人事にも興味がありますし、未経験エンジニアや新卒エンジニアの成長を支える教育的なことにも興味があります。
そのためには今取り組んでいるアルバイト・学習を淡々とこなさなければなと思っております🫡
VACUUMを用いてDeltaテーブルの未使用データを削除する

[Remove unused data files with vacuum \| Databricks on AWS](https://docs.databricks.com/delta/vacuum.html) [2022/11/3時点]の翻訳です。

:::note warn
本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
:::

テーブルに対して`vacuum`コマンドを実行することで、保持期間よりも古くて、Deltaテーブルによって参照されていないファイルを削除することができます。`vacuum`は自動では起動されません。ファイルに対するデフォルトの保持期間は7日間です。この挙動を変更するには、[Configure data retention for time travel](https://docs.databricks.com/delta/history.html#data-retention)を参照ください。

:::note warn
**重要！**
- `vacuum`はDelta Lakeによって管理されていないディレクトリのすべてのファイルを削除し、`_`で始まるディレクトリは無視します。Deltaテーブルのディレクトリ内の構造化ストリーミングのチェックポイントのような追加のメタデータを格納している場合には、`_checkpoints`のようなディレクトリ名を使ってください。
- `vacuum`はログファイルではなくデータファイルのみを削除します。ログファイルは、チェックポイントオペレーションの後に自動かつ非同期的に削除されます。ログファイルのデフォルトの保持期間は30日であり、SQLメソッド`ALTER TABLE SET TBLPROPERTIES`で`delta.logRetentionDuration`プロパティを通じて設定することができます。[Delta table properties reference](https://docs.databricks.com/delta/table-properties.html)をご覧ください。
- `vacuum`を実行した後は、保持期間よりも古いバージョンに[タイムトラベル](https://docs.databricks.com/delta/history.html)できなくなります。
:::

:::note
**注意**
ディスクキャッシュが有効化されている場合、クラスターに`vacuum`で削除されたParquetファイルのデータが含まれる場合があります。このため、ファイルが削除された以前のテーブルバージョンのデータにクエリーできる場合があります。クラスターを再起動することで、クラスターからキャッシュデータが削除されます。[Configure the disk cache](https://docs.databricks.com/optimizations/disk-cache.html#configure-cache)をご覧ください。
:::

# VACUUMのサンプル

```sql:SQL
VACUUM eventsTable   -- vacuum files not required by versions older than the default retention period

VACUUM '/data/events' -- vacuum files in path-based table

VACUUM delta.`/data/events/`

VACUUM delta.`/data/events/` RETAIN 100 HOURS  -- vacuum files not required by versions more than 100 hours old

VACUUM eventsTable DRY RUN    -- do dry run to get the list of files to be deleted
```

Spark SQL構文の詳細については、[VACUUM](https://docs.databricks.com/sql/language-manual/delta-vacuum.html)をご覧ください。

Scala、Java、Python構文の詳細については、[Delta Lake API documentation](https://docs.databricks.com/delta/index.html#delta-api)をご覧ください。

# どの程度の頻度でVACUUMを実行すべきですか？

過度なクラウドデータストレージのコストを削減するために、すべてのテーブルに対して`VACUUM`を定期的に実行することをお勧めします。vacuumのデフォルト保持期間は7日間です。閾値を高く設定すると、より長いテーブル履歴にアクセスすることができますが、格納されるデータファイルの数が増加し、クラウドプロバイダーのストレージコストを増加させることになります。

# 低い保持期間の閾値でDeltaテーブルをVACUUMできないのはなぜですか？

:::note alert
**警告！**
テーブルに対するリーダー、ライターの同時実行によって、古いスナップショットやコミットされていないファイルが使用されている場合があるので、保持期間の最小値は7日間にすることをお勧めします。`VACUUM`がアクティブなファイルをクリーンアップすると、同時実行されているリーダーが失敗することがあり、さらに悪いケースでは、`VACUUM`がコミットされていないファイルを削除するとテーブルが破損する場合があります。最長の同時実行トランザクション期間や、テーブルに対する最新の更新に対してすべてのストリームが遅延する最長期間よりも長い期間を選択すべきです。
:::

Delta Lakeには、危険な`VACUUM`コマンドの実行を防ぐ安全チェック機能があります。指定しようとしている保持期間よりも長い時間実行されるオペレーションが存在しないことが確実である場合には、Spark設定`spark.databricks.delta.retentionDurationCheck.enabled`を`false`に設定することで、安全チェック機能をオフにすることができます。

# 監査情報

`VACUUM`のDeltaのトランザクションログへのコミットに監査ログを含めることができます。`DESCRIBE HISTORY`を用いて、監査イベントにクエリーを行うことができます。

監査情報をキャプチャするには、`spark.databricks.delta.vacuum.logging.enabled`を有効化します。複数ワークスペースの書き込みにおいてS3で提供される一貫性保証が限定的なため、AWS S3ノエテーブルにおける監査ログはデフォルトでは有効化されません。S3で有効化する際には、複数ワークスペースの書き込みがワークフローに含まれていないことを確認してください。処理に失敗するとデータ損失につながる場合があります。

### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)
ActionController::ParameterMissing (param is missing or the value is empty: calendars): のエラー解決方法

# エラー内容
* Railsでformを設置したが、フォームボタンを押しても反映（投稿）されない現象が発生。
* 真っ赤の画面でもエラー画面が出てくれれば、なにが問題か調べることができるのだが、なにも反応ないというのは、駆け出しエンジニアにとっては鬼畜のエラー対応というもの！あのエラー画面のありがたさを知った機会でした。

# 対応した手順
* createアクションに`binding.pry`を記入して`plan_params`をチェックしてみると下記のように返ってきた。「パラメータがないか、値が空です」と言われている。

```
ActionController::ParameterMissing (param is missing or the value is empty: calendars):
```

* Railsでは、フォームから入力された値はパラメータとして作成したビューのフォームを通し、ブラウザからサーバーに送られる。つまり、パラメーターとは、viewとcontrollerにデータを送るための箱のこと。そのパラメータがない（or値が空）なのが原因らしい。

* さらに　`params` 単体でみてみると、以下のように返ってきた。
```
=> <ActionController::Parameters {"authenticity_token"=>"cyFxZR0DT9RaBC/yhtOXM35GAIPz8ek+9q2ATgFUCs1xHwGrbsBaEVN45YcSCkTWnu5CBI4i8U5OsVVtpLvsyQ==", "plan"=>{"date"=>"2022-02-13", "plan"=>"a"}, "commit"=>"保存", "controller"=>"calendars", "action"=>"create"} permitted: false>
```

* パラメーターを確認する限り、しっかり情報を渡せてるようだが、permittedがfalseになっていることが分かる
* requireのキーにはモデル名がくるが、ここにうまくモデルを指定できていなかったことが発覚。
    * ちなみに、モデル名は、上記のparams中身の中でも、{}で囲まれているものが該当する。つまりplanのこと
* そして、requireの中をみてみると、そこはcalendarsになっていた。オ―マイガー！
* 本来のモデル名に修正したらparamsが正常に渡されるようになりました。
 
```calendars_controller.rb
  private

  def plan_params
    params.require(:plan).permit(:date, :plan)
  end
```

# 結論
* requireの中にはしっかり該当するモデル名を入れよう
* binding.pryを使ってエラーの内容を把握しよう。今回の場合、paramsがそもそも正常に渡せていないのか、パラメーターを正常に受け取れて（保存して）いないのかが分かります。
* エラー画面のありがたさを実感。おかしいところを勝手に教えてくれるって親切ですよね。
xdebug3.2.0でphp7がサポート対象外になったのでDockerfileを直した

# 概要
Xdebug最新版はphp7をサポート対象から外した
よってphp7環境を構築するときはXdebugは3.1.6以前を指定しなければいけなくなった
それに伴ってローカル開発環境のDockerfileを直した

# 本文

## ビルドが失敗するようになった

ローカル開発環境を再構築する必要があったので docker-compose up したところ、エラーが出てました。

```:log
 > [ 5/17] RUN pecl install xdebug && docker-php-ext-enable xdebug:
#9 5.238 pecl/xdebug requires PHP (version >= 8.0.0, version <= 8.2.99), installed version is 7.3.29
#9 5.238 No valid packages found
#9 5.238 install failed
```

xdebugはphp8以上じゃないとだめ！というエラー。
いつの間にそんなことに。

https://xdebug.org/announcements/2022-12-08
> 雑な訳：Xdebug3.2.0で、PHP7.2～7.4はサポートしなくなりました

2022/11/10のXdebug3.2.0RC2の時点からこうなっていたようです。
というわけでPHP7をサポートする最終版である3.1.6を使わなければなりません。

https://xdebug.org/announcements/2022-11-08
> 雑な訳：PHP7をサポートする最終版です。

## 対策(Docker+peclの場合)

`pecl install` はバージョン指定が可能なようです。

```dockerfile:いままで
RUN pecl install xdebug && docker-php-ext-enable xdebug
```

```dockerfile:これから
RUN pecl install xdebug-3.1.6 && docker-php-ext-enable xdebug
```

xdebugのあとにハイフン＋バージョン番号をつけてやればOK。


# 参考資料
https://www.php.net/manual/ja/install.pecl.pear.php
OpenAPIで特定のAPIを使わないように明示したい

# OpenAPIで使うな危険を示したいときのtips

## 例えばこんな時
* APIを削除・追加したいのでOpenAPIを修正しなければいけない
* OpenAPIで仕様共有をしているけど、一部のAPIを近い将来に廃止したい
* APIのパス設計を見直したので、古いパスのほうは使わないようにしてほしい
例：/user_profile/xxx -> /user/profile/xxx

## OpenAPIで使うな危険を示す方法
* `deprecated=true` に設定する（[OpenAPI Specification](https://swagger.io/specification/)）

### ポイント
* 要らなくなったソースコードをごそっと削除するのとは違い、現在使われているAPIの場合、
丸ごと定義を削除してしまうと「あれ？あのIF仕様どこいった？？」と思われてしまう
* しばらくの期間deprecatedの状態で置いておき、全体への周知が終わってから削除するべき
'LLOCTEXT_NAMESPACE': undeclared identifierエラーに困った人へ

自分へのメモも兼ねて記事に投稿しておきます。
UnrealC++を書いていて、よく分からないエラーに遭遇する機会もしばしば...
今回も、そんな簡単な内容です。


# 発生したエラー

見たこと無いエラーが発生。
![LOCTEXT_undeclared_identifierエラー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/868cf976-8195-3192-e803-80d181a8e3ba.png)


|Severity  |Code  |Description  |
|---|---|---|
|Error  |C2065  |'LLOCTEXT_NAMESPACE': undeclared identifier  |

# 解決

[フォーラム](https://forums.unrealengine.com/t/loctext-undeclared-identifier/458954)で同じ問題があり、解決しました。

問題点としては、以下のポイントがありました。

* LOCTEXT_NAMESPACEがdefineされていなかった
* WITH_EDITORで囲む必要がある

# 参考
* https://forums.unrealengine.com/t/loctext-undeclared-identifier/458954
CSSカウンターを使ってみる

[ひとりCSS Advent Calendar 2022](https://qiita.com/advent-calendar/2022/css-becolomochi) 10日目です。

CSSカウンターを使ってみます。

## CSSカウンターとは

例

- 見出し番号を自動で振れる
- ol の番号を変えられる

## 使い方

- `counter-reset` プロパティでカウンターの値を初期化する
    - 初期化する以外にデフォルト値を設定できる
- `counter-increment` でカウンターの値を増加できる
- `counter()` または `counters()` を `content` プロパティで使用すると表示できる

## 使ってみる

```html
<section>
  <h2>Headline</h2>
  <p>Pragraqh</p>
</section>

<section>
  <h2>Headline</h2>
  <p>Pragraqh</p>
</section>

<section>
  <h2>Headline</h2>
  <p>Pragraqh</p>
</section>

<section>
  <h2>Headline</h2>
  <ol>
    <li>List item</li>
    <li>List item</li>
    <li>List item</li>
  </ol>
</section>
```

```css
:root {
  counter-reset: section;
}

h2::before {
  counter-increment: section;
  content: counter(section) ": ";
}

ol {
  counter-reset: listConter;
}

li {
  counter-increment: listConter;
}

li::marker {
  content: counter(section) "." counter(listConter) " ";
}
```

https://codepen.io/becolomochi/pen/poKYMpa

## 感想

- 便利


## 参考

- https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Counter_Styles/Using_CSS_counters
AWSが公開したFinchを使ってみた

# はじめに
2022/11/23にAWSがコンテナ開発用クライアントツール「Finch」をOSSとして公開しました。
Dockerが値上げした中で、代替になりうると注目されていたので試しに使ってみようと思います！

# Finchについて
詳細については[AWSの記事](https://aws.amazon.com/jp/blogs/news/introducing-finch-an-open-source-client-for-container-development/)が参考になるので、割愛しますが、
現時点で出来ることを簡単にまとめると

- nerdctlと統合されているため、Dockerで使っていたコマンドが使える
- GUIがないのでコマンドで操作をする
- macOSのみがサポートされている
- ボリュームとネットワークのサポートがされている
- 複数のコンテナアプリケーションを実行、テストするための Compose がサポートされている

# 導入
### パッケージをダウンロード
以下コマンドで自分の環境を確認
```
> uname -a
Darwin XXXX 21.6.0 Darwin Kernel Version 21.6.0: Thu Sep 29 20:12:57 PDT 2022; root:xnu-8020.240.7~1/RELEASE_X86_64 x86_64
```

[最新のリリースパッケージ](https://github.com/runfinch/finch/releases)より、OSにあったパッケージをダウンロードし、インストールする

### 仮想環境を起動
インストール後は初期化して起動する必要があるので、下記コマンドを実行する
```
> finch vm init
INFO[0012] Initializing and starting Finch virtual machine... 
〜〜
INFO[0175] Finch virtual machine started successfully 
```

次回以降は下記コマンドで起動・停止する
#### 起動
```
finch vm start
```

#### 停止
```
finch vm stop
```

### コンテナの起動
下記コマンドで予め用意されているサンプルのコンテナを立ち上げてみます
```
> finch run --rm public.ecr.aws/finch/hello-finch
public.ecr.aws/finch/hello-finch:latest: 
〜〜
                            @@@@@@@@@@@@@@@@@@@                                 
                        @@@@@@@@@@@@    @@@@@@@@@@@                             
                      @@@@@@@                  @@@@@@@                          
                    @@@@@@                        @@@@@@                        
                  @@@@@@                            @@@@@                       
                 @@@@@                      @@@#     @@@@@@@@@                  
                @@@@@                     @@   @@@       @@@@@@@@@@             
                @@@@%                     @     @@            @@@@@@@@@@@       
                @@@@                                               @@@@@@@@     
                @@@@                                         @@@@@@@@@@@&       
                @@@@@                                  &@@@@@@@@@@@             
                 @@@@@                               @@@@@@@@                   
                  @@@@@                            @@@@@(                       
                   @@@@@@                        @@@@@@                         
                     @@@@@@@                  @@@@@@@                           
                        @@@@@@@@@@@@@@@@@@@@@@@@@@                              
                            @@@@@@@@@@@@@@@@@@


Hello from Finch!

Visit us @ github.com/runfinch
```

### Dockerで使っていたソースを動かしてみる
これまで使っていたDockerfile、docker-compose.ymlを使って起動してみると
```
> finch compose up -d
〜〜
Loaded image: docker.io/library/XXXX:latestINFO[0715] 
Creating container XXXXX   
```
問題なく動作することが確認できました！

### 最後に
まだ簡単な事しか試せていませんが、Dockerの代替として使えるようになりそうだなという印象です
今後、様々なAWSサービスと連携されることを想像すると、期待しかないので非常に楽しみです♪
Stimulus 4: レジリエンスのための設計

:::note
この記事は[Google翻訳](https://translate.google.co.jp/)の結果を編集したものです。
:::

https://stimulus.hotwired.dev/handbook/designing-for-resilience

クリップボードAPIは[現在のブラウザーで十分にサポート](https://caniuse.com/#feat=clipboard)されていますが、古いブラウザーでアプリケーションを使用している少数の人々がまだいると予想される場合があります。

また、アプリケーションへのアクセスに時々問題が発生することも想定しておく必要があります。たとえば、断続的なネットワーク接続またはCDNの可用性により、JavaScriptの一部またはすべてが読み込まれない可能性があります。

古いブラウザーのサポートは無駄だと片付けたり、ネットワークの問題を更新後に解決する一時的な不具合として片付けたりするのは魅力的です。しかし、多くの場合でこの種の問題に対して適切に回復力のある機能を構築することは自明のことです。

プログレッシブエンハンスメントとして一般に知られているこの回復力のあるアプローチは基本的な機能がHTMLとCSSで実装され、基盤となるテクノロジーがブラウザーでサポートされている場合、その基本エクスペリエンスへの段階的なアップグレードがCSSとJavaScriptで段階的にレイヤー化されるようなWebインターフェイスを提供する方法です。

# PINフィールドの漸進的な強化

ブラウザーでサポートされていない限りコピーボタンが表示されないように、PINフィールドを段階的に強化する方法を見てみましょう。そうすれば、機能しないボタンを誰かに見せることを避けることができます。

まず、CSSでコピーボタンを非表示にします。 次にStimulusコントローラーでクリップボードAPIのサポートを機能テストします。APIがサポートされている場合はコントローラー要素にクラス名を追加してボタンを表示します。

`data-controller`属性を持つ`div`要素に`data-clipboard-supported-class="clipboard--supported"`を追加することから始めます。

```html
<div data-controller="clipboard" data-clipboard-supported-class="clipboard--supported">
```

次にボタン要素に`class="clipboard-button"`を追加します。

```html
<button data-action="clipboard#copy" class="clipboard-button">Copy to Clipboard</button>
```

次に以下のスタイルを`public/main.css`に追加します。

```css
.clipboard-button {
  display: none;
}

.clipboard--supported .clipboard-button {
  display: initial;
}
```

まず、コントローラー内に`data-clipboard-supported-class`属性を静的クラスとして追加します。

```javascript
static classes = [ "supported" ]
```

これにより、HTML内の特定のCSSクラスを制御できるようになるため、コントローラーはさまざまなCSSアプローチにさらに簡単に適応できるようになります。このように追加された特定のクラスには`this.supportedClass`を介してアクセスできます。

次にクリップボードAPIがサポートされているかどうかをテストし、コントローラーの要素にクラス名を追加する`connect()`メソッドをコントローラーに追加します。

```javascript
connect() {
  if ("clipboard" in navigator) {
    this.element.classList.add(this.supportedClass);
  }
}
```

このメソッドはコントローラーのクラス本体のどこにでも配置できます。

必要に応じてブラウザーでJavaScriptを無効にし、ページを再読み込みしてコピーボタンが表示されていないことを確認します。

PINフィールドを徐々に拡張しました。コピーボタンのベースライン状態は非表示で、JavaScriptがクリップボードAPIのサポートを検出した場合にのみ表示されます。

# まとめと次のステップ

この章ではクリップボードコントローラーを緩やかに変更して、古いブラウザーや劣化したネットワーク状態に耐えられるようにしました。

次にStimulusコントローラーが状態を管理する方法について学習します。
trocco、2022年の注目機能「チーム機能」

![チーム機能.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3036503/836adf69-d0ce-cc8b-6afb-c54a3cd44074.png)
### はじめまして
troccoのカスタマーサクセスを担当しております、ikemotoです！
troccoユーザーの皆さんにおかれましては、いつもありがとうございます。
これからtroccoを使ってみたい！という皆さんにおかれましては、お気軽にご連絡ください。

今回は、今年の[troccoアドベントカレンダー](https://qiita.com/advent-calendar/2022/trocco)のために、はじめてqiitaに記事を投稿させていただきます。
何かの一助になれば幸いです。

## troccoのチーム機能
これまでtroccoのオプション機能として提供されていた「グループ機能」が、今年、「チーム機能」として大幅リニューアルされました！
troccoには、あらゆるデータソースへの接続情報やデータの転送設定などをご登録いただくため、「チーム機能」へのリニューアルで細かな権限管理ができるようになったことで、troccoを使っていただけるユーザーの幅が広がったと感じております。
実際、ユーザーの皆さんからも、権限管理ができるようになって運用の幅が広がった！というお声をいただくこともございます。

### チーム機能とは
あらゆるSaaSやクラウドプラットフォームをご利用の皆さんは、権限管理の重要性も難しさもご理解されていることと思います。
troccoのチーム機能でできる権限管理について、あらためてご紹介します。
- 「チーム」と「リソースグループ」の2つの概念で、ユーザーとリソースを管理する
- 「チーム」には、ユーザーを所属させる（1ユーザーが2つ以上のチームに所属することも可能）
- 「リソースグループ」には、各種リソース（troccoの接続情報、転送設定、データマート定義、ワークフロー定義等）を所属させる
- 「リソースグループ」に「チーム」を紐づけ、チームに持たせる「ロール」を設定する

![team.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3036503/b3365d36-9831-62fd-fab2-377ff8f78c09.png)


詳細につきましては[ヘルプページ](https://documents.trocco.io/docs/about-team)をご覧ください。

### チーム機能でできること
チーム機能では各種リソースに対してユーザーにロールを付与することができます。
つまり、
**「このユーザーは、このリソースは閲覧権限だけでいいけど、こっちのリソースには編集権限もつけたいな」**
**「リソースグループ自体の変更は限られた一部の管理者だけにしたいな」**
といった細かい権限管理が可能になります。

逆に、リソースグループでチームメンバーにリソースを共有しないと、たとえば
**「Aさんが転送設定を作ったらしいけど私（Bさん）は見れない」**
**「他のメンバーが作ったワークフローを実行したいけど、見れない」**
というような課題が出てきます。
troccoでは、リソースグループにリソースを共有しないと、作成者のみしかそのリソースを閲覧・編集・実行ができないようになっているためです。

troccoを複数名のチームでご利用いただく場合、または社内横断的に導入して部署ごとにご利用いただく場合などは、チーム機能を使ってメンバー間でリソースを共有しあい、troccoの利用状況について共通認識をもつ必要があるといえます。

## チーム機能のユースケース

#### ひとつのチームでtroccoを使う
データ分析基盤チームなど、特定のひとつの組織でtroccoを運用するお客様で多い事例です。
管理者チームとそれ以外のメンバーのチームに分け、すべてのリソースグループを皆さんが参照できるようにしつつ、リソースグループ自体の管理は管理者チームのみが行える、という状態です。
比較的小さく、誰が何の作業をしているのか皆さんが把握しているような組織での運用に適しています。
![チーム機能説明画像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3036503/11f8bdd9-9b4a-0493-86f9-9074f5c8471c.png)


#### 社内に多くのチームがあり、みんなでtroccoを使う
私たちprimeNumberをはじめ、データ利活用が進んでいるお客様で多い事例です。
trocco管理チームと、データエンジニア、データアナリスト、ビジネスサイドの皆さんもtroccoを触ることがある場合などが該当します。
具体的な設定例はパターンが多すぎるので割愛しますが、以下のような部分を考慮して、チームとリソースグループを設計していただけると良いと思います。

- リソースグループ設計
    - 秘匿性のある情報（接続情報など）については、紐づけるチームが少なくなるよう設計する
    - 1リソースあたり1リソースグループしか設定できないが、1リソースグループあたり複数のチームを紐づけることができるので、誰が使うリソースグループなのかを想定して小分けにする
- チーム設計
    - リソースグループに対して付与できるロールが4種類（管理者、編集者、運用者、閲覧者）なので、ロールごとにチームを分ける
    - チームを大きくしてしまうと、過大なロールを付与してしまう可能性があるので、なるべく同じ職種のラインでそろえて小さめに設計する
    - 1ユーザーあたり複数のチームに所属できるので、チーム管理者（各チームに必要）はtrocco管理者とそろえ、他メンバーはチームメンバーとして所属させる

一般的なサービスの権限管理と考慮ポイントは同じだと思いますが、できるだけ最小権限の原則にそって設計していったほうが後々効いてくると思っております。

## データ分析チーム以外にもチーム機能！
チーム機能は、ユーザーの皆さんの共同作業をサポートする機能であると同時に、最終的には「データの民主化」にもつながってくる機能だと感じています。
データ分析チームにはもちろんご利用いただきたい機能ですが、マーケチームやセールスチームなど、データを利活用する皆さんにも、ぜひご利用いただきたいです！

trocco利活用が、より進んでいきますように…！
[Git・Flutter] git stashの使い方 in Android Studio



こんにちわ　いせきです。

開発する上で、「`git stash`使うととても便利に開発できますよ〜〜」と教えてくれて、いざ使ってみると便利！！と思ったので共有していこうと思います。

# git stashとは

いくつかの解釈を書いてみます。どれかで理解できればいいなと思っています。


<b>・stashを一言で説明すると、「変更を一時退避する処理」であり、まだコミットはしたくない修正を一時退避するために使われます。

<b>・コミットせずに変更内容を一時退避することができるコマンドです。もちろん、退避した変更は後から再度適用することができます。

<b>・git stash コマンドは、コミットされていない変更 (ステージングされたものおよびされていないもの) を取り出し、後で使用するために保存してから、作業コピーから取り消します。



## １、Stashしたい内容を２本指でタップ
![スクリーンショット 2022-12-09 17.33.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/bf64270c-2a61-cc48-bcf5-1045f308edaf.png)

## 2、Git > Stash Changes... を選択
![スクリーンショット 2022-12-09 17.33.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/8ff33a2b-595b-b62f-7dc5-fbfb0134fe8b.png)

## 3、Messageにて、変更内容を記載して、「Create Stash」をタップ
![スクリーンショット 2022-12-09 17.34.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/476752e7-a6c5-55c3-0508-1d9581326d0f.png)


これでStashができました。コマンドでもできますが、あえてAndroid Studio内でやってみました。試してみてください。



# Stashを戻す方法

## １、Git > Uncommitted Changes > Unstash Changes... をタップ

![スクリーンショット 2022-12-09 17.43.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/3e83e52f-bd8f-f113-744f-0e66503ae0f6.png)

## ２、Stashを選択して、戻す処理を行う

Stashesから選択して、「Apply Stash」で戻すことができます。

![スクリーンショット 2022-12-09 17.44.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/cd83004a-e34a-2585-713b-0c1d3ed1f2be.png)



# 最後に

今までは、保存する内容をメモ帳に入れておいて、Rollbackしていました。こんな便利なことがあることを知らずにいました。。めっちゃ便利ですね。。たくさん使っていこうと思います！
Laravelのテスト実行を途中で止める時の豆知識

# Laravelでテスト実行を中断する時の注意

## よくやる手順
* テストを一括で実行する
* ctrl + C で実行を中断する

## その結果
* Unit/Featureテストの場合は問題なし
* Browserテストを実行している場合は、途中で止めるとDBのデータが書き換わることがある
→ **Browserテストを途中で止める場合は注意が必要**

## 結論
* しばらく気づかずに、予期せぬ挙動が起きてから、原因はなんだ？！と
慌てるはめになることがあるので、頭の片隅に覚えておくといいかもしれない。
【Djangoチュートリアル】Page not found (404)とValueError at /polls/1/vote/

# 概要
* Django [チュートリアル０４](https://docs.djangoproject.com/ja/4.1/intro/tutorial04/)実施中に以下二つのエラーが発生した。無事に解決できたので、備忘録としてここに残しておく。

## エラー１の内容　Page not found (404)
```
Page not found (404)
Request Method:	GET
Request URL:	http://127.0.0.1:8000/
```

* 詳細（スクショ）
![スクリーンショット 2022-12-09 11.17.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710373/234d5a9b-31d7-b8f0-b8db-4146888e0c83.png)

## エラー１の原因・解決方法
* URLconfに設定したのが「/polls」 又は「/admin」 だけだったので、これらでないとアクセスができませんよ、と言われている。なので、以下URLであればアクセスできる。`http://127.0.0.1:8000`がダメって言われると「なんでや！？」と慌てがちですが、冷静にurls.pyのurlpatternsを見ないとですね。
* http://127.0.0.1:8000/polls/
* http://127.0.0.1:8000/admin/

## エラー２の内容　ValueError at /polls/1/vote/
```
ValueError at /polls/1/vote/
The view polls.views.vote didn't return an HttpResponse object. It returned None instead.
```

* 詳細（スクショ）
![スクリーンショット 2022-12-09 11.18.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710373/45b12a40-de69-31f8-29d4-1410ad828aef.png)

## エラー2の原因・解決方法
* voteがHttpResponseを返していないというので、polls/view.pyのvote関数を見たら、

```polls/view.py
def vote(request, question_id):
    ... # same as above, no changes needed.
```

となっていた。あら、[チュートリアル０４](https://docs.djangoproject.com/ja/4.1/intro/tutorial04/)からそのままコピペしたから大丈夫だろうと思っていたら、コメントアウトでここは変えるなとあった。ちゃんと読まないとダメですね汗
以前のものに戻してあげたら解決しました。
import mglearn: Memory.__init__() got an unexpected keyword argument 'cachedir'

# 環境
- Windows11
- Python3.11
- Visual Studio Code

# 問題
　やったこと
```Python:Python3.11
import mglearn
```
　現れた警告
```Python:Python3.11
例外が発生しました: TypeError
Memory.__init__() got an unexpected keyword argument 'cachedir'
```
  
# 解決法
1.コマンドプロンプトでmglearnのファイルの場所を確認する。
```:cmd
python -m pip show mglearn
```
2.Githubにあるmglearnのファイルを確認する。下のリンクを参照。

https://github.com/koyachi/introduction_to_ml_with_python/tree/fix/joblib-memory-interface

3.更新差分を上書きする。
4.importできるようになる。

# 参考

https://qiita.com/ophhdn/items/4d3ecc6354d92b7ac0ba

https://teratail.com/questions/jep3vj7dxob4yy
エンジニアのための英語学習について

# 英語について
最近、英語でコミュニケーションをする会社に転職したので 始めた英語の勉強内容について記録していきます。仕事が忙しく少しサボり気味だったので禊のために書くことで当方のモチベーションアップにつながればと思っています。

## 毎日やろうと思っていたこと
### **Grammar in Use**

https://cambridge-university-press.jp/material/giu_br-2/

Grammar in Useはイギリス英語の実用文法書です。 コミュニケーションに使える自然な例文が載っています。
ちなみに全て英語で日本語の解説等はありません。

**2ユニット/日**

### **速読英単語**

https://www.zkai.co.jp/books/sokutan7/

速読英単語は大学受験に使用される単語帳？？なんですかね。
これを見つけたのは、サッカーワールドカップで活躍されている遠藤航選手が使用している動画を見て使用することを決めました。この教材自体はただの単語帳ではなく、面白い英文章を読みながら単語を覚えることができます。ページ見開きに英文と日本語があるのでストレス無く使用することができます。また、Web上で音声も聞くことができるのでシャドーイング等にも良いと思います。

https://www.zkai.co.jp/books/sokutan7/endowataru6/

**2セッション/日**


### **PATAPURA**

https://patapura.com/

パタプラは、喋るためのトレーニング教材です。 英語のフレーズを2~8語程度の塊であるチャンクとして覚えて、レッスンをエビングハウスの忘却曲線に合わせて反復する教材です。実施すべきレッスンは基本的にアプリがレコメンドしてくれますし、レッスン自体は大体10~20分程度で完了できるのでスキマ時間に最適です。

**推奨レッスン/日**

### **Journaling**

毎日何かしらのトピックに対して、日記を英語で書きます。これをする一番のメリットは、自分専用の話題に対して使用する英単語をインプットしてアウトプットできることです。

**書く/日**


## 結果

- Grammar in Use
→ 3日坊主　（この禊を書いている時に、やはり実用的な英文ばかりという記事を多く見たのでこれからがんばります　）

- 速読英単語
→ たまにサボってしまうが継続中
→ 必修編を買ったが、英会話ではもう少し簡単なフレーズで良いので中学生版を買おうか検討中

- PATAPURA
→ コンスタントに継続中
→ 関係代名詞や関係副詞が出る部分から、スピーキング力が広がっている気がする。

- Journaling
→ 1ヶ月間やったが、忙しくて最近できていない。。。
→ 効果はあったと思う。（これも禊書いてる時に振り返って思ったので、ちゃんと再開したい。）


## その他
身の回りを英語にしていく

- daily.dev
はてぶ→ 海外のはてぶ的なやつ

https://app.daily.dev/

- Language REACTOR

YoutubeやNetflixで日英字幕してくれるExtension。マジで便利。無料だし。

https://www.languagereactor.com/


- TEDを見る

↓　おすすめのスピーカー

https://www.ted.com/speakers/matt_cutts


# さいごに
禊を書いていると、全然学習が足りないなと振り返れたのでちゃんとやって継続していこうと思います。
多分英語学習は朝が良いので早起きから！！（笑）


# 以前書いたMediumの記事
https://medium.com/@ta1m1kam/%E8%8B%B1%E8%AA%9E%E3%81%AE%E5%8B%89%E5%BC%B7%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6-%E6%A4%9C%E8%A8%BC-d09d7e026b98
PHPの基本操作を分かり易く解説！

# はじめに

今回の記事では、これからPHP学習を始める方向けに基本的なPHP操作を解説していきたいと思います。
PHPはサーバーサイドを開発するためのプログラミング言語なので、難しそうなイメージがあるかとは思いますが
そこまで身構える必要はありません。使い続ければ必ず身についてきますのでご安心ください。

過去の記事で、環境構築の解説をしているので、MAMP、XAMPPのインストール方法に悩んだ際はご活用ください。

https://qiita.com/ninja0408/items/6dc13b2d57b57953e4d4

※今回の記事では、パッケージソフトは「MAMP」、テキストエディタは「VSCODE」を使って解説していきます。

# 1. testファイルを作ろう

MAMPのインストールができたら「MAMP」フォルダ内の「htdocs」の中に「test_php」という新規フォルダを作成しましょう。
フォルダを作成できたら、VSCODEを開き「test_php」フォルダを読み込ませます。
拡張子を「.php」で「index.php」などの名前でファイルを作成します。これでテストファイルの用意ができました。

# 2. ブラウザの準備

先ほど作成した「index.php」ファイルをブラウザに読ませておきます。以下のURLを開いておきましょう。
http://localhost/php_test/index.php

# 3. ブラウザに文字を表示させてみよう

PHPを記述する際は、```<?php ~ ?>```と書き、その中に命令を書くというのがPHPの書き方です。
文字列を表示するには「echo」を使います。
下記のように記述すると先ほど開いたブラウザに文字列が表示されているはずなので、確認してみましょう。

```PHP:PHP
<?php
  echo 'ninja code'; // ninja code
?> 
```

# 4. 数値の計算結果を表示させてみよう

PHPでは、数値の計算をすることも可能です。下記のように記述することでブラウザに計算結果を表示させることができます。
再度ブラウザを開いて確認してみましょう。
ここで注意点があります。数値を表示させる場合は、シングルクォーテーション（''）もしくはダブルクォーテーション（""）は不要です。
万が一囲ってしまった場合は、パソコンが文字列だと認識し式をそのまま表示します。
```PHP:PHP
<?php 
    echo 10 + 17; // 27
    echo '10 + 17'; // 10 + 17 
 ?>
```
# 5. 変数を使ってみよう

次にPHPで頻繁に使う変数についてです。
変数は、データの入れ物のような役割を持ちます。
PHPでは「$変数名 = 値;」というような書き方で変数に値を代入することができます。
以下のように変数を代入してみましょう。
```PHP:PHP
<?php 
  $ninja = 'ninja code';
  echo $ninja; // ninja code
?>
```

### 変数展開を使える

変数展開とは、文字列の中に変数を埋め込むことができるPHPの機能のことです。
実際の書き方を見ていただくとわかるように｛｝波括弧で囲うことで変数を埋め込むことができます。
また、結合演算子と呼ばれるドット（.）を使って文字列同士、数値、変数などさまざまな値を文字列として結合することもできます。

```PHP:PHP
<?php 
  $name = "忍者";
  echo "私の名前は{$name}です"; // 私の名前は忍者です
  echo "<br>"; // 改行
  echo "私の名前は".$name."です"; // 私の名前は忍者です
?>
```

# 6. HTML内にPHPを埋め込む

PHPはよくHTML内にプログラムを埋め込んで利用されています。埋め込んで利用することでWebページにアクセスした人や状況に応じて表示するテキストなどを変えることができます。

# さいごに

今回の記事は以上となります。
PHPの基本学習が落ち着いたら、どんどん手を動かして機能を実装していくフェーズに入りましょう！
無料で50問ほどの問題に挑むことができるサイトになるので、是非活用しましょう。

https://koga.ninjacode.site/

最後まで読んでいただきありがとうございました。
ActiveStorage + has_one_attached + S3でもサブディレクトリに入れたい！

# なにがしたいのか
S3にそのまま上げても使えはするんですが、大人の事情で１つのバケットしか使えない等の理由でディレクトリに入れたい時あると思います。
S3にディレクトリの概念は無いんですがそこは便宜上ディレクトリで行かせてください。

確かに`has_one_attached`でも`record.attach(key: 'images/example.png')`の形でキーは使えます。
ただ、楽をするためにRailsを使ってるんだ！ってスタンスの自分としては気に食わないわけです。

form_withでフォーム作ってfile_field使ってパラメーター渡すだけで簡単にアップロードしたくないですか？？私はしたいです。

## ActiveStorageでフォルダはサポートされていない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96621/165e83b4-4372-2431-8de7-52be2c61856a.png)
https://github.com/rails/rails/issues/32790

バッサリですね。

## じゃあどうするか
こうです
メソッドオーバーライドします。

S3以外のサービスでちゃんと動くかは試してないので分からないですが、とりあえずこれでいけます。

```rb
class MyModel < ApplicationRecord
  has_one_attached :image

  def image_blob=(blob)
    blob.key = "images/#{blob.key}"
    super(blob)
  end
end
```

# 解説
さっさと結論出してしまいましたが一応解説しておきます。
アトリビュート名は`image`を例にして読みやすく書き換えてます。

## has_one_attachedは何をしているのか？
`has_one_attached`はマクロです。
メソッド定義を読めば一目瞭然ですが、アソシエーションやメソッドを定義しています。

https://github.com/rails/rails/blob/f04074cfa134df01ead7ec8c90cf03feeb749dc1/activestorage/lib/active_storage/attached/model.rb#L50-L88

これを追って行きます。

## アタッチ時の処理
先程のマクロで定義しているここでメソッド呼んでるのでここから追って行きます。

```rb
def image=(attachable)
  attachment_changes["image"] =
    if attachable.nil?
      ActiveStorage::Attached::Changes::DeleteOne.new("image", self)
    else
      ActiveStorage::Attached::Changes::CreateOne.new("image", self, attachable)
    end
end
```

今回は作成時の処理を見たいので更に`ActiveStorage::Attached::Changes::CreateOne`を掘り下げてみます。

https://github.com/rails/rails/blob/f04074cfa134df01ead7ec8c90cf03feeb749dc1/activestorage/lib/active_storage/attached/changes/create_one.rb

おやおや...目ぼしい処理がありますね...

```rb
def save
  record.public_send("#{name}_attachment=", attachment)
  record.public_send("#{name}_blob=", blob)
end
```

これはhas_one_attachedメソッドで定義されるafter_saveコールバックで呼び出されるメソッドです。
つまり、after_saveコールバックが発火すると`ActiveStorage::Attached::Changes::CreateOne`の`save`メソッドが呼ばれ、そこから`image_blob=`メソッドが呼ばれます。

ActiveStorageはblob.keyの値が保存されるファイル名になる仕様のため、`image_blob=`メソッドをオーバライドしてkeyを書き換えてしまえばその名前でファイルが作成されるわけです。

また、S3の場合`folder/image.png`のようにスラッシュで区切るとディレクトリ的な扱いになるのでそれを利用します。
なのでそれらを利用するとこうなるかと

```rb
class MyModel < ApplicationRecord
  has_one_attached :image

  def image_blob=(blob)
    blob.key = "images/#{blob.key}"
    super(blob)
  end
end
```

参考になれば幸いです。
いくつ知ってる？Boxの隠れ便利機能5選

この記事では、Boxのウェブ版を活用している私が日々便利だな〜と感じるものの、多くの人は知らないかも？と思うような、 隠れ優良機能をご紹介します。

- 共有リンクでページ/時間指定ができる
- マイコレクションにはドラッグ&ドロップで追加できる
- マイコレクションページをBoxのトップ画面に指定できる
- キーボードショートカットはめっちゃある
- 複数共有リンクを一度に発行できる

# 共有リンクでページ/時間指定ができる
Boxの人気機能の１つでもある共有リンク機能。社内・社外に気軽にコンテンツを共有できるのが便利ですよね。
でもたまに思いませんか？「スライドが長いので、P12から見てほしいな」「この動画の2:30頃が重要なのでみてほしい…」

こちらが指定したタイミングから共有することができます！
### ページ指定の場合
共有リンクのURL＋#p=XX
XXに指定のページ番号を入れてください。
例えば、10ページ目を共有したい場合はリンクの末尾に#p=10と記載

### 時間指定の場合
共有リンクのURL+#t=XXs もしくは #t=XXm
例えば、動画の20分目から共有開始したい場合は、リンクの末尾に#t=20mと記載

これを使いこなせると、きっと共有先の方々も助かると思います！



# コレクションはドラッグ&ドロップで追加できる
Boxを使い始めると、必然的に”よく参照するコンテンツ”が出てくると思います。
こういったコンテンツにいかに素早く辿り着けるかで日々の生産性が左右されませんか？（大袈裟？）
指定のコンテンツをマイコレクションに追加するには、ファイル選択→[...]→コレクション、と進めていくのが一般的かと思います。
![スクリーンショット 2022-12-09 16.59.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2975763/2770d1d6-4973-cee2-f011-1bb0d113d9de.png)

実はそんなことをしなくても、ファイルをそのまま左側のメニュー部分にドラッグ&ドロップして追加できます。
![スクリーンショット 2022-12-09 16.57.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2975763/558db26d-c65a-7bbe-9982-de198cfb6aa9.png)


私は[あとで見る]というコレクションを作っているので、気になる資料やあとで読み込みたい資料に出会ったときは、とりあえずここにギュイーンってしてポイってしてます。（／・・）／ ⌒

# マイコレクションをBoxトップ画面にできる
Boxを開いた瞬間から、マイコレクションが表示されるようにできる設定です。
![スクリーンショット 2022-12-09 16.52.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2975763/23181d50-1757-99fa-a600-8884b7fa3761.png)
右上の[これをBoxホームページにする]を押下で設定可能です。


# キーボードショートカットはめっちゃある
Box上で[Shift]+[?]を押下すると怒涛のキーボードショートカットが現れます。
![スクリーンショット 2022-12-09 14.50.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2975763/251c07df-b780-475e-bf08-51e00561e38b.png)
上の[項目アクション▼]を押下すると全部で4ページあるのが確認できます。


### 個人的に地味によく使うキーボードショートカット
- Shift D: 選択したファイルをダウンロード
- Shift G, A:すべてのファイルページに飛ぶ（マイコレクションをBoxトップページ設定しているのでよく使うのかも。Go AllのGAと記憶しているので簡単）
- Shift V: グリッドビューとリストビューの切り替え（基本はリストビューなので、画像や写真が多いフォルダに入ると結構使う）

# 複数ファイルの共有リンク一括発行
複数のファイルの共有リンクを発行するときに、共有リンクを発行する動作を何度もするの、めんどくさい！と思ったことはないですか？
実は一発で発行できる裏技があります。
![スクリーンショット 2022-12-09 17.04.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2975763/97b9777a-cf85-95d9-d829-ff0062f161e5.png)
複数のファイルを選択して、右上[...]→[メール]を押下。
![スクリーンショット 2022-12-09 17.04.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2975763/048ed48f-3cf7-b0a3-175f-9d6383aacd6d.png)

すると各ファイルのURLが発行されるのでこれをコピーしちゃえばOKです！


ぜひ試してみてください。
【2022年版】playwrightでデバッグ実行する方法

## 経緯
playwright でブラウザ（Webアプリ）のE2Eテストを作成していました

その際に、
「あれ、うまくデバッグ実行できない・・・」
という状況におちいりました・・・

なお、
playwright を使っているということで、
Node.js、JavaScript(TypeScript)でE2Eテストを作成するときの話です

・・・

最近のWebアプリであればE2Eテストを作って毎日回したりするので
該当する人は割と多いんじゃないかなと思います

で、

playwright によるE2Eテストの実装時、

### デバッグ実行したいですよね！！

　

「ここでテストがコケるんだけど何が原因なのか調査したい」

「実際、ブラウザがどういう画面、状態なのか見たい」

・・・

こういうことあるある・・・

　

playwrightをnode_modules に追加して
コンソール上から実行しているだけではデバッグ実行はうまくできないです

エディタ（VisualStudioCode）からテストケースを選択してデバッグ実行、ブレークポイントを立ててステップ実行する方法を自分用にメモ書きしたものを載せます

　

同じようにお悩みの開発者のお役に立てば嬉しいです


# playwright のデバッグ実行の方法

#### ０．事前準備：
　・VisualStudioCode(以下、VSode) をインストールする
　・playwright を追加する
　　※playwrightを実装している人なら既にやってると思うので詳細は割愛。
　　※この時点でplaywright のテストケースは1つ以上用意されていることとします。
　　　余力があれば、playwright 自体の使い方も載せるかも。

#### １．VSCode に テストエクスプローラー機能を追加する
　Playwright Test for VSCode
　https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188038/9170b4e0-8807-0631-faa3-fb8c941da199.png)
↓
すると、VSCodeに「テストエクスプローラー」が追加されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188038/76ca82d4-ec22-34ba-e167-b937742b048b.png)

#### ２．「実行」⇒「構成の追加」からオプション引数を追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188038/71868144-88bb-76d1-b21d-812f46288379.png)
↓
「"args": ["--inspect-brk"]」 を追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188038/ac2d71b3-51ec-9173-90f1-dee8ef079a0b.png)


#### ３．テストエクスプローラーからテストケースを選択してデバッグ実行する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188038/a9aac26c-8fca-697f-f842-242f0b53d570.png)

これで、デバッグ実行できるようになりました

あとは、必要に応じて、
テストコードの左端にブレークポイントを立てればそこで止まってくれます

ヘッドレスモードについては、
デバッグ実行すると、勝手にブラウザはヘッドレスモード：OFFで起動してくれるようです

# 結果
playwrightでデバッグ実行できた

・・・

この情報が皆さまのお役に立つことをねがっております

記事がいいなと思ったら❤ボタンをポチッとお願いします

　

おわり
ubuntu 22.04のgnomeのエラーがでていた原因を調べたらメモリが壊れていた

# 要約
ubuntu 22.04でGUIログインしたら，「システムプログラムの問題が見つかりました」が表示されるようになった．
ログを眺めると，gnomeのプロセスがシグナル11でkillされていた．
gnomeを入れなおしても同様にシグナル11でkillされるので，メモリ故障を疑い，memtest86を実行した．
すると，特定のメモリだけErrorを吐くことが分かった．

# 本題
## 環境
CPU : AMD Ryzen Threadripper
RAM : Corsair Memory 16GB x4 (合計 64GB)
GPU : GeForce RTX 4090
OS  : Ubuntu 22.04

主用途は深層学習の実行である．
## 状況
遠隔ログインでサーバを使おうと毎朝ログインを試みても使えないという苦情があった．
また，Gnomeのログイン画面でも，マウスカーソルは動くがクリックが効かず，キーボードの反応もないという状態であった．
サーバを強制終了しもう一度起動すると，動作が重いもののGnomeのログイン画面からログインでき，webブラウザやterminalを使うことができた．
ログインに成功すると，「システムプログラムの問題が見つかりました」が表示され，詳細を確認するとgnomeのプロセスがシグナル11でkillされていることが分かった．

## 対応1
gnomeのプログラムが何かの影響で破損してしまったのではないかと考え，gnomeを入れなおすことにした．
```
$ sudo apt install ubuntu-desktop --reinstall
```
しかし，状況は改善せず，gnomeのプロセスがシグナル11でkillされていることが分かった．

## 対応2
linuxがシグナル11でkillするときは，不正なメモリ参照をしたときである．
gnomeが不正なメモリ参照をするようなプログラムだとは考えにくいので，メモリが破損して，おかしいメモリ参照をしているのではないかと疑った．
メモリチェックツールとして有名なmemtest86を走らせることにした．
memtest86は以下の手順で導入した．
```
$ wget https://www.memtest86.com/downloads/memtest86-usb.zip
$ unzip memtest86-usb.zip
$ /sbin/gdisk -l memtest86-usb.img
[省略]
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          524287   255.0 MiB   0700  MemTest86
   2          524288         1048575   256.0 MiB   EF00  EFI System Partition
   3         1048576         2097118   512.0 MiB   0700  DMA Test Partition
## 重要なのは EFI System PartitionのStart sector

$ sudo mkdir -p /mnt
$ sudo mount -o loop,offset=$(({EFI System PartitionのStart sector}*512)) memtest86-usb.img /mnt/ ##今回はoffset=$((524288*512))
$ cd /mnt
$ sudo mkdir /boot/efi/EFI/memtest
$ sudo cp -r EFI/BOOT/* /boot/efi/EFI/memtest
$ df -h　| grep efi
/dev/nvme0n1p1              93M  5.2M   88M   6% /boot/efi
## 重要なのは 左側のfilesystem

$ efibootmgr -v
BootCurrent: 0001
Timeout: 1 seconds
BootOrder: 0001,0002,0003,0004
Boot0001* ubuntu        HD(1,GPT,833852b7-45e0-42d9-8e19-5dbf6e9c2b7d,0x800,0x2f000)/File(\EFI\ubuntu\shimx64.efi)
Boot0002* UEFI:CD/DVD Drive     BBS(129,,0x0)
Boot0003* UEFI:Removable Device BBS(130,,0x0)
Boot0004* UEFI:Network Device   BBS(131,,0x0)
## 重要なのはBootCurrentで指定されている値（今回は0001）と，そのBoot0001のHD(1,...)にある始めの数字（今回は1）である

$ sudo efibootmgr -c --disk {/boot/efiのある左側のfilesystem} --part {Boot000xのHDの始めの値} --loader /EFI/memtest/BOOTX64.efi --label "memtest"
## 具体例は以下のようになる
$ sudo efibootmgr -c --disk /dev/nvme0n1p1 --part 1 --loader /EFI/memtest/BOOTX64.efi --label "memtest"

## ここはBoot000xの順番を見て，memtestが前から2番目になるようにする
$ sudo efibootmgr -o 1,5,2,3,4
```
以下のコマンドを打つと，再起動時にmemtestが起動するようになる．
```
$ sudo efibootmgr -n 2
```
このようにして導入したmemtest86を実行すると，Errorが頻繁に起こることを確認した．
メモリを一枚ずつ差し，一つ一つ確認すると，Errorが頻繁に起こるメモリが1枚のみあった．
これより，メモリが破損していることが確認された．

## 参考にしたサイト
https://pyopyopyo.hatenablog.com/entry/20160528/p1
https://qiita.com/foursue/items/8548c4402c0651ba5aaa
TableauのビューをPDF化しSlackで配布する

この記事は、[Ateam LifeDesign Advent Calendar 2022](https://qiita.com/advent-calendar/2022/ateam-life-design) シリーズ2の12記事目です。

## はじめに
技術的な話ではなく、なんで複雑な手段を用いてまでやっているのかの自身への備忘に近いです。

## ざっくり何をしてるか
・Excelに送りたい頻度、ビューのパス、SlackのチャンネルID、ファイル形式を記載
・その情報をRubyが読みに行きtabcmdに叩き込む
　┗ OAuth経由でログイン認証している場合には、個人用アクセストークンを発行してソースに埋めておく
・出力されたファイルが一旦Localに落ちるので、それをSlack botで展開
・これらをスケジューラで定期的に動かしてます

## なんでこんなことしてるのか
Tableauは動的なドリルダウンをしやすいですが、
そういった分析をする人間と、その結果を元にアクションを行う人間は別であることも多いと思います。
ライセンスのない人にも最低限の情報を自動で通知できるので、現時点では重宝してます。

今回は役割に応じたコストで運用できることを目的としました。
（逆を言えば、機能活用が効率的に浸透できれば、使わなくなるかも）

## おわりに
非エンジニアかつ初投稿なので、お役に立てる情報は書けてないと思いますが、
tabcmdはあまり活用法を見かけなかったので投稿します。
もっと簡単な方法などあれば教えていただけると助かります。
Azure関連の記事一覧

# はじめに

初めてAzure環境構築を行なった際の知見や作成手順をQiitaにまとめましたので、ぜひご活用下さい。


# Azureの基本知識
https://qiita.com/Bulldog_Market/items/25b46b6ccfc47fcf0e3d


# Azure環境構築（VM作成〜VPN接続）
https://qiita.com/Bulldog_Market/items/7c5b4c52bc9261d968d5


# Azure環境構築（Azure Functions作成〜VScode連携）
https://qiita.com/Bulldog_Market/items/e480511815fe31e79e34


# Azure環境構築（Key Vault作成）
https://qiita.com/Bulldog_Market/items/c5f292f61062ec52dcd2


# Azure環境構築（Key Vault作成〜関数アプリでのシークレット操作）
https://qiita.com/Bulldog_Market/items/1108b142bbf19e6a8398
Azure環境構築（Key Vault作成）

# Key Vaultの作成

下記サイトを参考にKey Vaultを作成する

https://qiita.com/kk-ishii/items/0176b1f02e77bd4fd948

Azure FunctionsとKey Vaultの連携やシークレットキー作成は下記記事で紹介します。　

https://qiita.com/Bulldog_Market/items/1108b142bbf19e6a8398


# 付録　Key Vaultの料金

https://azure.microsoft.com/ja-jp/pricing/details/key-vault/


https://azure.microsoft.com/ja-jp/pricing/calculator/?service=key-vault


https://www.rworks.jp/cloud/azure/azure-column/azure-entry/24267/
Django の外部キー on_deleteの引数 DO_NOTHING は「何もしない」のか…！？

モデルに外部キーを設定するときに、
 **on_delete=models.DO_NOTHING**
とした時の挙動について、発見があったので共有します。

### 外部キー on_delete のおさらい

モデルA がモデルBの外部キー持つ場合に、モデルBを削除しようとする時に、Aの振る舞いを決める

```mermaid
graph LR
A[model A] --   FK --> B[model B]
```


指定する挙動は、大きく次の3タイプに分かれる

###  
#### ①PROTECT

Aが存在する時、Bは削除できない

  
###
#### ②CASCADE

Aが存在する時、Bを削除可能で、Aも同時に消える

  
###
#### ③SET_NULL  /  SET_DEFAULT

Aが存在する時、Bを削除可能で、Bの外部キーの値に代わりにNull やデフォルト値を入れる

  

  
###
**`DO_NOTHING`** は、上記のように明示的に指定したくない場合に使えると思っていた。
強いて言えば、Aが存在していても、Bを削除出来る ③ に近い挙動を取ると思っていた。

  


ところが、実際の挙動は **①とほぼ同じ** で、Aが存在する時、Bを削除できなかった。

どういうことかとDBを調べてみると、次のような制約が付いている。
>   model_a_field_9cdf3fca_fk_model_b_id
  

これは、**`PROTECT`** を選んだ時に付与されるものと全く同じ。
外部キーを作る時点でこの制約がつくようで、これにより、DB的挙動は **`PROTECT`** と全く同じとなる。

  

  
### 検証
では、**`PROTECT`** と何が違っているのか。
というか、何が **`DO_NOTHING`** なのか。
(制約つけてるやん、削除できないやん)

  

実際に **`DO_NOTHING`** を設定して管理画面からAが存在する状態でBモデルを削除しようとすると、**500エラー**が表示された。

やはりBを削除はできない。

  
###
**`PROTECT`** に変えてから削除してみるとすると、500エラーにはならず、 

> Aのモデルが存在する為、削除できません

と**削除出来ない理由が表示**された。

  

なるほど！
**`PROTECT`** は削除できない理由を教えてくれる
**`DO_NOTHING`** は挙動は同じでも、500エラーをそのまま返す（=何もしない）


###
つまり、**`DO_NOTHING`** を選んでも、Aが存在する場合はBを削除できない。
  

## 結論

- **`DO_NOTHING`** は **`PROTECT`** の下位互換なので選択する理由が無い

- 削除させるか判断し辛い段階では、とりあえず **`PROTECT`**  を選択しておくのが良さそう

- 削除を許可するには、 **`SET`** 系を明示的に選ぶ必要がある
HTML・CSSで押したらへこむボタンの作り方

# へこむボタンを作ろう！
## １．立体的なボタンを作る
まず、立体的なボタンを作ります。これは、ボタンに陰をつけるだけなので簡単に作ることが出来ます。
陰はcssにbox-shadowを書いてつけます。

ex)box-shadow: 0px 8px #434242;

![button.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959525/afb65e16-bb67-1318-67c4-f7b0b4fa806a.png)

## ２．ボタンを押すと影がなくなるようにする
セレクタにactiveを用いることで要素がクリックされている間だけcssを適用することが出来ます。
なのでボタンのセレクタにactiveをつけて、そこにbox-shadowを打ち消すためにbox-shadow: none;を書きます。

ex)
    button:active {
        box-shadow: none;
    }

## ３．ボタンをへこませる
最後にクリックしている間、ボタンをへこませます。なので、2と同じようにactiveをつけたセレクタの中にプログラムを書きます。
今回はposition: relative;を使います。これはtopやleftと併用すると要素を本来の位置からずらすことが出来ます。

ex)
    button:active {
        box-shadow: none;
        position: relative;
        top: 8px;
    }

これで完成です！
![無題の動画 ‐ Clipchampで作成 (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959525/be520aef-608b-21dc-0191-ab18152c215c.gif)
obniz BLE Gatway Gen2.0で toioを操作した結果

<h1>目的</h1>
授業教材として利用できるかどうかを検証するため。

検証の期待結果：Gatewayがtoioに接続できれば、様々な応用が実現可能。

<h1>必要なもの</h1>
１．obniz BLE/Wi-Fi Gatway Gen2.0（obniz社の無料レンタルサービスで、2週間借りた）

![pdct-gw-gen2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007201/1461d48b-3350-7ddd-0a79-8d4f8128a325.jpeg)

２．toio

![image.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007201/d7b028ed-f2e8-8b9d-1274-186ea239c891.jpeg)


```BLE-gateway-otio.rb
    //put your obniz ID
    var obniz = new Obniz("OBNIZ_ID_HERE");

    //during obniz(Wi-Fi Gateway) connection
    obniz.onconnect = async function() {
 
        await obniz.ble.initWait();
        const Toio_CoreCube = Obniz.getPartsClass("toio_CoreCube");
        obniz.ble.scan.onfind = async (peripheral) => {
          if (Toio_CoreCube.isDevice(peripheral)) {  #BLEで受信したアドバタイズ情報をもとに、toio コアキューブかどうかを判定する。
            console.log("find");
            const device = new Toio_CoreCube(peripheral);
            device.ondisconnect = (reason) => { #toio コアキューブの情報を確認する
              console.log(reason)
            }

            console.log(device); 

            await device.connectWait(); #toio コアキューブに接続する
            console.log("connected"); 

            const batteryEquip = await device.getBatteryStateWait();#toio コアキューブバッテリー値を取得する
            console.log(batteryEquip); 

            const data = await device.getMotionWait();
            console.log(data);

            await device.moveAroundWait(100, 255); #toio コア キューブのモータをPosition IDと無関係に動かす

            await device.disconnectWait();
            console.log("disconnected");

          }
        };
        await obniz.ble.scan.startWait();
   
```

<h1>気づいたこと</h1>
１．デバイスを検知しても、上手く接続できないことがある。
    ⇒　対処方法：Gatewayを再起動するか、toio コア キューブを一度電源切って再起動するか

２．複数台のtoio コア キューブを接続できないみたい。

<h1>参考資料</h1>

https://obniz.com/ja/sdk/parts/toio_corecube/README.md
[Flutter]コードの見やすくする方法(ファイルツリーをスッキリさせる)


　

こんにちわ　いせきです。
今回は、生成したファイルなどを見やすくする方法をご紹介しようと思います。


# １、右上にある設定アイコンをタップします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/46e4034d-6d04-7b1a-5835-e4cf262182f1.png"width=50%>






# ２、File Nesting... をタップします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/97837f24-c172-fe94-1970-5ba6da849f4e.png"width=25%>



# ３、折りたたみたい内容を記載します。

![スクリーンショット 2022-12-09 16.24.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/9b26e95b-8174-288e-2edb-db689b436c76.png)



今回は、`freezed.dart`と`g.dart`の内容を折り返したいので以下のように記載します。

```
.freezed.dart; .g.dart; の２つを追加
```

設定できたらOKを押してもらうと以下のようなファイル構造になります。


![スクリーンショット 2022-12-09 16.00.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/bf9965fb-eced-4470-245f-8862fb810066.png)


# 最後に

僕はこのことは知っていましたが、Android Studioだとできないと思っていました。。VscodeやIntelliJだけだと思っていました。別のやり方をやろうとして、教えてもらい、Android Studioにもあるんかーいととてもびっくりしました。。。
でもできてよかったです。。。
ChatGPTにインタビュー

# ChatGPTとは
![キャプチャ2-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985768/b680a320-e383-fc52-e0ac-df7f27c157bd.png)
こちらこそよろしくお願いします。

# 使用方法
![キャプチャ3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985768/9f374eb0-ed10-ab05-93c5-8fa0d87349d1.png)
かんたん。

# できること・できないこと
![キャプチャ4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985768/1ca0838e-3bd0-7e35-8d2a-21589f284f84.png)
苦手なことはない。強気ですね。

# AIに仕事を奪われる？
![キャプチャ5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985768/c2868ac8-7a5a-5194-931b-2d3c12981620.png)
AIが言うならそうなんだろう。

# 最後に
![キャプチャ6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985768/90dcfeec-2a68-23fd-7b96-6ff5ce3263dd.png)
あれ？シンギュラリティ起きてない？

# おまけ
![キャプチャ13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985768/d648dcbd-870d-e7d8-da40-f3f7e7771b9b.png)

ええやん	&#x1F601;&#x1F44D;
PythonでDynamoDBのデータ型記述子を取り除く

# 本記事について

- DynamoDB Streams などでデータを扱う場合、データ型記述子が混ざっていて扱いづらい
- Pythonで上記の解決方法が見つけづらかったため、備忘録として残しておく

# 解決方法

boto3 にちゃんと備わっていた

```python
from boto3.dynamodb.types import TypeDeserializer

def conv_dynamodbjson_to_json(item):
    td = TypeDeserializer()
    return {k: td.deserialize(value=v) for k, v in item.items()}

converted_json = conv_dynamodbjson_to_json(dynamodb_json)
```

# 実行例

"N"、"L"、"S"などのデータ型記述子が入っていて扱いにくい

```json:sample.json
{
  "Age": { "N": "8" },
  "Colors": {
    "L": [{ "S": "White" }, { "S": "Brown" }, { "S": "Black" }]
  },
  "Name": { "S": "Fido" },
  "Vaccinations": {
    "M": {
      "Rabies": {
        "L": [
          { "S": "2009-03-17" },
          { "S": "2011-09-21" },
          { "S": "2014-07-08" }
        ]
      },
      "Distemper": { "S": "2015-10-13" }
    }
  },
  "Breed": { "S": "Beagle" },
  "AnimalType": { "S": "Dog" }
}
```

上記を読み込んで変換して出力する処理を作成 (ついでにDecimalも変換)

```python
import json
from decimal import Decimal
from boto3.dynamodb.types import TypeDeserializer


def conv_dynamodbjson_to_json(item):
    td = TypeDeserializer()
    return {k: td.deserialize(value=v) for k, v in item.items()}


def conv_decimal_to_int_or_float(obj):
    if isinstance(obj, Decimal):
        if int(obj) == obj:
            return int(obj)
        else:
            return float(obj)


with open("sample.json", "r") as sample_json:
    dynamodb_json = json.load(sample_json)

converted_json = conv_dynamodbjson_to_json(dynamodb_json)
plain_json = json.dumps(converted_json, default=conv_decimal_to_int_or_float)
print(plain_json)
```

無事、扱いやすい形になった

```json
{
  "Age": 8,
  "Colors": ["White", "Brown", "Black"],
  "Name": "Fido",
  "Vaccinations": {
    "Rabies": ["2009-03-17", "2011-09-21", "2014-07-08"],
    "Distemper": "2015-10-13"
  },
  "Breed": "Beagle",
  "AnimalType": "Dog"
}
```

## 雑感

- boto3 にも知らない機能が沢山眠っているんだろうなと思った
    - 時間があれば公式リファレンスを隅々まで読み漁ってみたい
- parquet に変換する前処理が終わった
    - DynamoDB Streams → Lambda → S3 に履歴データを蓄積して Athena で分析したい
    - データ分析サービス多すぎて迷ってしまう・・・このあたりも要勉強
- データ型記述子が役立つ場面はあるのだろうか