OmniAICreator/Qiita-1.07M huggingface dataset:
CCC2023 参加レポート

今年の7月から10月にかけて行われた CivicTech Challenge Cup 2023 に参加させて頂いて感じたことや、制作したアプリについてお話しできればと思います。

# コンテストに参加してみた

私たちは、Flutter大学というオンラインコミュニティ内でチームを結成し、「RiverPotter」というチーム名で **Civictech Challenge Cup 2023** という学生向けのコンテストに参加させて頂きました。チーム名は、Flutterで有名なパッケージのRiverpodを元に決めました。結果としては、ファイナリストに選出していただくことができ、中川政七商店様からの企業賞をいただくことが出来ました。

https://ccc.code4japan.org/

## こんなアプリを作ってみた
皆さん、こういった経験ありませんか？

**バスの遅延による影響**

- バスの遅延は、次の乗り換えができなくなるという問題を引き起こします。これは、特にタイトなスケジュールを持つ人々にとっては大きなストレスとなります。
- また、バスがすでに出発してしまったのか、それともまだ来ていないのかを判断するのが難しく、待つ時間が不確定になるという問題もあります。

**バス停の見つけられなさ**

- バス停を探すのは、特に初めて訪れる場所では困難です。どんなに周囲を見渡しても、バス停の看板が見つからないことがあります。これは、旅行者や新しい場所に移住した人々にとっては大きな問題となります。

以上の問題を解決するために私たちはバスの現在地がリアルタイムでわかるアプリ**どこバス** (デモアプリ)を開発しております。

## 使用技術

- Flutter
    - iOS、 Android、 Web などに対応したクロスプラットフォームのアプリ開発フレームワーク
- Supabase
    - バックエンドの構築に使用
    - データベース、認証、ストレージ、リアルタイム機能を提供


## 開発中の機能紹介

主な機能として、3つ紹介しております。

### 1. バスの現在地情報

リアルタイムでバスの現在地を把握する機能を開発しています。この機能を実現するためには、バス内に位置情報を発信するデバイスを設置する必要があります。現段階では、開発者のスマートフォンの位置情報を数秒ごとに取得する実装を行っています。これにより、将来的にバスにデバイスを設置する際に、円滑に導入できるようになることを期待しています。

### 2. バス停の位置情報

バス停の位置情報は、[GTFS-JP](https://tshimada291.sakura.ne.jp/transport/gtfs-list.html) を用いています。

:::note info
GTFS とは General Transit Feed Specification の略です。世界標準の公共交通データフォーマットです。

GTFS-JP は日本版になります。
:::

https://tshimada291.sakura.ne.jp/transport/gtfs-list.html

GTFS-JP から取得したデータは Supabase のデータベースに保存され、Flutter アプリケーションでそのデータをマップ上に表示します。


### 3. 通知機能の実装

私たちは基本的な通知機能を実装していますが、それだけでなく、バスの現在地情報とバス停の位置情報を活用した新たな通知機能も開発しています。具体的には、バスがバス停に到着する時間を予測し、その情報を通知する機能です。これにより、ユーザーは待ち時間を最小限に抑えることができます。

これらの機能は、公共交通機関を利用するすべての人々にとって、より便利で快適な移動体験を提供することを目指しています。私たちは、これらの機能が公共交通の利便性を向上させ、より多くの人々が公共交通機関を利用するきっかけになることを期待しています。

# 今後の挑戦について

これまで紹介してきた開発に加え、以下のイベントにも取り組んでいきます。

### 台湾渡航

2024年の3月1日から3月3日まで台湾で行われるイベントに参加し、このプロジェクトについて発表を行います。発表は英語で行われるため、残り三ヶ月の間で英語力を向上させることを目指します。そのためには、毎日の英語学習を習慣化していこうと考えております。

### 高専起業家サミット

チームメンバーの一人が高専生で、参加基準を満たしているので**高専起業家サミット**に参加しました。このイベントは、起業を視野に入れている学生たちにとって、貴重な情報交換の場となります。また、他の起業家たちから学ぶことで、新たな視点やアイデアを得ることができます。

https://startup.gekkan-kosen.com/

以上が私たちのチームが主に直面している挑戦です。これらの経験を通じて、私たちはチームとして成長し、プロジェクトを成功に導くことを目指しています。

最後までご覧いただきありがとうございました。これからも私たちの活動を応援していただけますと幸いです。どうぞよろしくお願いいたします。

##  お世話になってるコミュニティ
https://flutteruniv.com/

## 執筆者
https://twitter.com/FlutterSatoru

https://twitter.com/fencer0268
yyyymmddhhmmssfff+3桁ランダム数字

業務で一意な一時ファイルを作成するために、
「yymmddhhmmssfff+3桁のランダム数字」を生成するコードをjavascriptで作成したのでここに残す。

※タイムスタンプのケツ3桁(fff): ミリ秒

※厳密には完全に同時刻に実行され、生成された3桁のランダム数字も一致する可能性が0ではないので、一意とは言えない。

```
// クラスのインスタンス化
const currentDate = new Date();
// 年
const year = currentDate.getFullYear();
// 月
const month = currentDate.getMonth() + 1;
// 日
const day = currentDate.getDate();
// 曜日のリスト
const dayOfWeek = ["日", "月", "火", "水", "木", "金", "土"];
// 曜日の番号を取得
const weekNumber = currentDate.getDay();
// 時間
const hour = currentDate.getHours();
// 分
const min = currentDate.getMinutes();
// 秒
const sec = currentDate.getSeconds();
// ミリ秒
const msec = currentDate.getMilliseconds();

//yyyyMMddHHmmssfff文字列を返却
const yyyyMMddHHmmssfff = () => {
  const date =
    year +
    String(month).padStart(2, "0") +
    String(day).padStart(2, "0") +
    String(hour).padStart(2, "0") +
    String(min).padStart(2, "0") +
    String(sec).padStart(2, "0") +
    String(msec).padStart(3, "0");
  console.log(date);
  return date;
};

//000~999変数を返却
const threeDigitRandom = () => {
    const random = Math.floor(Math.random() * 1000).toString().padStart(3, '0');
    console.log(random);
    return random
}
const timeStamp = yyyyMMddHHmmssfff();
const random = threeDigitRandom();

//yyyyMMddHHmmssfff+3桁ランダム値
const timeStampAndRandom = timeStamp+random;
console.log("yyyymmddhhmmssfff+★★★: " + timeStampAndRandom);
```
VisualStudioCodeでMarkdownを書く環境を整える

# はじめに
Markdownで資料作成だ！ということでVSCodeでMarkdownを書くためにまずは環境を整えたいと思います。
Markdownで資料作成するにあたりやりたいことは以下の5つです。

1. とりあえず書ける環境を作りたい
1. 完成した資料をPDFで書き出したい
1. 完成資料を完成時の見た目でプレビューしながら資料作成したい
1. Mermaidで図なんかも書きたい
1. 正しいMarkdown記法で書きたい

上記の項目別にやるべきこと(適当な拡張機能の追加)を分けて書いていこうと思うので、参考になりそうな部分お読みいただければと思います。

# 環境構築するPC等の環境
+ Mac OS 13.0
+ Visual Studio Code 1.84.2

VScodeは既にPCに入っているものとして以下環境構築行っていきたいと思います。

# 1. とりあえず書ける環境を作りたい
実を言うと最低限書くだけならばVSCodeの基本機能で十分かもしれません。

+ 章立てして文章書く
+ 図表を挿入する
+ 書きながら文章をプレビューする

ようなことだけでOKであれば、VSCodeの基本機能で十分できてしまいます。
公式のドキュメントにできることは書いてあるので、一読してみると良いかと思います。

https://code.visualstudio.com/docs/languages/markdown

その上で入れておくととても便利なのがMarkdown All in One です

https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one

リンクのインストールボタンまたはVSCodeの拡張機能よりインストールできます。

+ 文章に目次を入れたい
+ オートコンプリート・ショートカットなど使ってスラスラ書きたい
+ 箇条書きいい感じに整形して欲しい

など痒いところに手が届く機能がつまっています。
個人的にあって嬉しいのは、画像を挿入したいときにファイル名のオートコンプリートが働く瞬間です。

# 2. 完成した資料をPDFで書き出したい
PDF書き出しにはMarkdown PDFという拡張機能です。
こちらもリンクのインストールボタンまたはVSCodeの拡張機能よりインストールできます。

https://marketplace.visualstudio.com/items?itemName=yzane.markdown-pdf

PDFだけでなく、HTMLやpng,jpegでの書き出しにも対応しています。
Mermaidなどで図のみ作成して画像として書き出しておいて別のところで使う、なんてこともできるのかなと思います。

書き出しは以下の2つの方法でできます。
+ 右クリックして開くメニューから形式を選択して書き出し
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594517/2dbf22f9-c681-f790-3b44-4bca004ae4c2.png" width=60%>

+ `F1`または`cmd+shift+P`で開くコマンドパレットに`markdown ~~`と入力して出てくるコマンドを選択して書き出し
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594517/5e56ed57-6e79-90c3-7d55-607067f64e9d.png" width=60%>

また今回ドキュメントをよく読んでみて初めて知ったのですが、Markdownを保存するたびにPDFヘ自動変換するという設定も行うことができるみたいです。
ドキュメントのUsege>Auto Convertの項目を参照ください。

# 3. 完成資料を完成時の見た目でプレビューしながら資料作成したい
こちら地味に困る時があるのですが、デフォルトのプレビュー機能では書き出される書面そのものをプレビューしてくれているわけではありません。例えば、VSCodeをダークモードで使用しているとプレビューで表示される書面の背景色もその時設定されている背景色と同様の黒となります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594517/69a55836-538a-7f9f-a8e5-fa009d9e1657.png" width=80%>

完成資料そのものを眺めながら体裁整えたりしたいんだー！という場合にはMarkdown Preview Enhancedという拡張機能がおすすめです。

https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced

拡張機能追加後のプレビュー画面は以下

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594517/791d219c-796e-2b2a-b2b8-920bf8ec5c25.png" width=70%>

ヘッダの余白なんかも含めて、出力される通りにプレビューしてくれます。
この拡張機能、入れるとできるようになることが多いので私自身まだ全然使いこなせていません。
できることが増える一方で、前までの感じでVSCode動いてないな？と戸惑う瞬間もあったりするので一長一短あるかなとは思います。
入れてみて戸惑ったことは後日記事に~~できたらなと思います。~~ してみました↓↓

https://qiita.com/kiii142/items/e2f10a72e4780ca5f543


# 4. Mermaidで図なんかも書きたい
VSCodeでMermaid書きたい場合は拡張機能Markdown Preview Mermaid Supportをインストールします

https://marketplace.visualstudio.com/items?itemName=bierner.markdown-mermaid

設定からMermaidのテーマの変更なども簡単にできるので大変助かります。

# 5. 正しいMarkdown記法で書きたい
拡張機能markdownlintを入れます

https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint

正直正しい書き方わからなくてもなんとなくそれっぽく書けちゃうのがMarkdownだと思うのですが、他人と共有したりする場合はできるだけ良いとされている書き方で書いておいた方がいいのではないかと思っています。

# おわりに
さしあたっての技術文書作成に導入した拡張機能一式でした。
他にもこんなの入れとくといいよ！みたいなおすすめあれば教えてください！
『ASP.NET MVC 5 実践プログラミング』でlocalDBを使用する方法


## 解決したいこと
添付のエラーの解決。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629897/ff9887b0-0362-2ffc-fc78-f81d57a59094.png)

## 対象者
この記事は下記のような人を対象にしています。
『ASP.NET MVC 5 実践プログラミング』を読み始めた初学者、特に「Visual Studio Installer」を用いて環境構築した人

## 結論
Web.configの「connectionStrings」を修正。

```C#:Web.config
<connectionStrings>
    <add name="MvcBasicContext" connectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=MvcBasicContext;Integrated Security=True;Connect Timeout=30;Encrypt=False;" providerName="System.Data.SqlClient"/>
</connectionStrings>
```

:::note info
私の環境は上記で解決しましたが、解決するまでの経緯を記載します。
:::

## 実行環境
Visual Studio 2022
SQL Server

:::note info
書籍のサンプルを使用しています。
:::

## 下準備
### 接続文字列の確認
1.「表示」→「SQL Server オブジェクト エクスプローラー」をクリック。
2.SQL Serverよりlocaldbをクリック→「プロパティ」の「接続文字列」を控える。


## 接続
### 接続文字列の修正
1.接続文字列の「Catalog=master;」を「Catalog=MvcBasicContext;」に修正
2.「connectionString」のData Sourceを控えた「接続文字列」に置き換える。
```C#:Web.config
<connectionStrings>
    <add name="MvcBasicContext" connectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=master;Integrated Security=True;Connect Timeout=30;Encrypt=False;Trust Server Certificate=False;Application Intent=ReadWrite;Multi Subnet Failover=False" providerName="System.Data.SqlClient" />
</connectionStrings>
```
3.List.cshtmlをデバッグ。サンプル通りの画面が表示されたら成功。もし『キーワードがサポートされていません』と表示されたら、接続文字列の該当部分を削除して再実行。

### 補足
「App_Data」配下にmdfファイルが作成されず、「プロパティ」の「既存のデータベースの場所」に生成されます。
「App_Data」配下に作成する方法分かる方いらっしゃいましたら教えていただけると幸いです。

## 参考書籍
山田祥寛,ASP.NET MVC 5 実践プログラム,秀和システム,2016,pp58-59

## おわりに
SQL Serverへの接続先を変更しました。
もし誤りがあればご指摘いただけると幸いです。
aria属性の用途

日頃の業務の中であまり使ってこなかったARIA属性について、詳しく理解しきれていなかったので今後の業務で活かせるように改めて用途を調べてみました。

ARIA属性はウェブアクセシビリティを向上させ、コンテンツの状態や性質を表すためために使用される属性です。
視覚的には表示されないですが、アクセシビリティ支援技術を使用するユーザーに情報を的確に提供する材料となり、アクセシビリティが高まるのです。
aria-*属性の値の形式には種類があり、用途によって使い分けます。
以下はその一部の例です。

## aria-label属性

```ruby:index.html
<button aria-label="閉じる">×</button></select>
```
上記の例では、「閉じる」ボタンに aria-label 属性がついています。ボタンに関する情報が視覚的には表示されませんがスクリーンリーダー等を使用するユーザーに正確な情報を提供します。

## aria-hidden属性

```ruby:index.html
<div aria-hidden="true">非表示のコンテンツ</div></select>
```
aria-hidden 属性は要素がスクリーンリーダー等に対して非表示であることを示し、不要な情報を読み上げるのを防ぎます。

## aria-live属性:
```ruby:index.html
<div aria-live="assertive">新しいメッセージが届きました</div>
```
aria-live 属性は、動的な変更がある場合にスクリーンリーダーにその変更を通知するために使用されます。assertive 値は重要な変更があった場合に即座に通知することを示します。



以上は一部の例ですが、「情報アクセシビリティ」について対応を求められた時に実装できるよう、今後の業務の中で間違った使い方をしないように意識的に使っていこうと思います。
その中で間違いや気づきがあれば、随時更新します！
Oracleエクスポートを中断する

Oracleのエクスポートをして中断したい場合は以下記事を参照して中断する
https://www.bnote.net/oracle/stop_data_pump.html

顧客先でEXPDPエクスポートバッチを実行したがHDD容量が少なく止める必要があった。焦ってバッチを終わらせてしまい、検索した結果上記のサイトに出会った。また同じことがあった時のために備忘録として残すことにしました。
【AWS】IPv6 を使うと ECR のための VPC エンドポイントは 3 つ必要

ECR が IPv6 対応されたら 0 個で済みます。

# ecr.dkr, ecr.api
Fargate のプラットフォームバージョンによっては ecr.dkr だけで良い場合があります。

|Plaform Version  |ecr.dkr  |ecr.api  |
|---|---|---|
|1.3.0  | **必要**  |不要  |
|1.4.0  | **必要**  | **必要**  |

# s3
ECR が GetDownloadUrlForLayer で S3 経由のイメージ取得をしようとするため必要になります。

https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/APIReference/API_GetDownloadUrlForLayer.html

ECR が IPv6 対応していないため、S3 のデュアルスタックエンドポイントにアクセスしてくれず VPC エンドポイントが必要になります。

# おわり
こいつのせいで Fargate の IPv6 移行が進まないから
【UEFN】ワールド制作時の効率化を図ってみる＃１

## 初めに
最近ワールドを効率よく大量生産できないか考えて最近たどり着いた答えの一つがワールドのアウトライナーのフォルダ構成だと気が付きました。

## 問題だった点
- UEFNが用意していたデバイスをたくさん使用していたこと
- Verseデバイスの設計が悪くたくさんデバイスを指定していたこと
- ワールド上にVerseデバイスを適当な位置に配置していたこと<-
- 同一フォルダで全てのデバイスやプロップを管理していたこと

## 解決していく
### UEFNが用意していたデバイスをたくさん使用していた件
問題点としては利用するデバイスが増えてどこでバグが起きていたか分かりにくくなってしまっていることにある。
**これに関してはVerseでデバイスを作ることでより最小限のデバイスだけでゲームの機能が制作できるようになった。**
### Verseデバイスの設計が悪くたくさんデバイスを指定していた件
先ほどの問題点の次にきくる関門になりました。Verseの設計が悪いと`@editable`でしていするデバイスやオブジェクトが増えて複数回同じオブジェクトを指定することになる時がありました。。
**これに関してはさまざまな設計パターンを学習する必要がありそうでした。僕自身はもともとUnityエンジニアでそこで学習してきた設計をVerseに落とし込んで実装することで解決してきました。(自己満感ｗ)**
### ワールド上にVerseデバイスを適当な位置に配置していた件
以下の画像を見ててくださいw

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/ccc35fef-d125-cff9-3b8c-7b64f0a8892e.png)

これは似た機能を持つためctrl+Dで複製した結
果になります。
ここで起きるのは指定する時に別のVerseデバイスで同じものを指定してしまう危険性が増えることです。
以下のように結びつけるデバイスの隣に配置することで間違えずに指定することができます。ペンのマークを押すとカーソル先のオブジェクトを選択できるのでより早くより正確にしてすることができるようになりそうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/69e82c39-0ed4-938a-91c8-28235b88559c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/cce47ad4-21df-b44f-e23f-83b66f7a2e42.png)

### 同一フォルダで全てのデバイスやプロップを管理していた件
これに関しても上記の件と同様に指定する時に指定する時に間違いが起きないことや、バグが起きた時にいち早く発見しやすくなったり、さらには同じようなデバイス構成のものがあった時に複製がしやすくなることや、視認性が良くなるなど多くのメリットがあります。
現状最高の構成はこれになりました。
#### 同一階層に配置したダメな画像
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/f39f5637-64b1-b882-b95d-51d67260ec83.png)

#### 自分なりに整理した最高の配置
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/26266107-3a82-b994-036d-bb24b8284d94.png)
- カスタムVerceデバイス
- UEFNが用意したデバイス
- 紐づいているオブジェクトまとめフォルダ


#### 余裕があればデバイスの名前も工夫するとなおよし！！

## まとめ
Verseがかけなくてもより良く整理することで改善しやすくなったりバグを発見しやすくなることがあります！

最終的には自分がわかれば良いのでどこまで整理するのかたくさんワールドを作って試行錯誤をしてみるのも良いかもですね！！

## 余談
この度UEFN/Verseに関するオープンコミュニティサーバーを建ち上げました。ちょっとでも興味があれば奮ってご参加くださいませ。

https://discord.gg/cK4daQyD
AWS SQS デッドレターキューのメッセージを一括削除

## デッドレターキューのメッセージを全削除するシェルスクリプトを作ってみた。

デッドレターキューに限ったことではないのですが、やりたかったこととしては、デッドレターキューにあるメッセージを全削除することだったので、このタイトルです。
（通常のキューのメッセージを全削除することはそうそうないかなと思って）

### やりたかったこと（背景）

SQSを使ったアプリケーションの開発中に、テストで登録したSQSのメッセージがデッドレターキューに溜まってしまった場合に、まとめて消してしまいたい時がある。

AWSマネジメントコンソールで手動でポチポチ削除していたのだが、数が多いとそれなりに面倒。

メッセージの中身をデバッグ用などに取っておきたい場合もあるので、削除する前にJSONファイルに出力しておきたい。

どのファイルに所望のメッセージが格納されているかわかるように、識別可能なキー項目をメッセージから取得してファイル名に付与しておきたい。

### AWS CLI を使って一括で削除

Python で boto3 も良いかと思ったが、先ずは、AWS CLI だけで作ってみる。

#### 動作確認環境

```
$ bash --version
GNU bash, version 5.2.15(1)-release (x86_64-pc-msys)
$ aws --version
aws-cli/2.5.2 Python/3.9.11 Windows/10 exe/AMD64 prompt/off
```
#### 免責事項

* スクリプトの実行は自己責任でお願いします。本スクリプトを実行することにより生じるいかなる問題に関しましても、筆者は一切責任を負いません。

```shell
#!/bin/bash

# 
# 指定のデッドレターキュー内の全メッセージを削除
# 
#   処理内容 
#     キュー内のメッセージを取得し、メッセージ内容のキー項目などをファイル名に付与してJSONファイルにメッセージ内容を出力し、メッセージを削除する
# 
#   前提条件
#     jq が実行できること（PATHが通っていること）
# 

set -e

# キュー名
QUEUE_NAME="デッドレターキューの名前"

# キューメッセージ内容JSONファイル出力先ディレクトリ
OUTPUT_DIR="dead_queue"

mkdir -p ${OUTPUT_DIR}

# キュー名でキューのURLを取得（AWSアカウントとリージョン固定なら、わざわざ取得せず、URLを直書きしても良いと思います）
QUEUE_URL=$(aws sqs list-queues --queue-name-prefix ${QUEUE_NAME} --query "QueueUrls[0]" --output text)
# QUEUE_URL="https://sqs.{リージョン}.amazonaws.com/{AWSアカウントID}/${QUEUE_NAME}"
echo "[DEBUG] QUEUE_URL=${QUEUE_URL}" >&2

del_cnt=0

while true
do
    message_json=$(aws sqs receive-message --queue-url "${QUEUE_URL}")
    if [[ -z "${message_json}" ]]; then
        # メッセージを取得できなければ終了
        break
    fi

    # メッセージ削除用に、 ReceiptHandle を取得
    receipt_handle=$(echo "${message_json}" | jq -r '.Messages[].ReceiptHandle')

    # メッセージ内容を取得
    msg_body_json=$(echo "${message_json}" | jq -r '.Messages[].Body')
    # メッセージの中のキー項目の値をファイル名に使用する（下記の例では、id )
    file_base_name=$(echo "${msg_body_json}" | jq -r '.id')
    # メッセージ内容をJSONファイルへ出力
    echo "${msg_body_json}" | jq '.' > ${OUTPUT_DIR}/${file_base_name}.json

    # メッセージ削除
    aws sqs delete-message --queue-url "${QUEUE_URL}" --receipt-handle "${receipt_handle}"
    
    del_cnt=$((++del_cnt))

    sleep 1
done

echo "${del_cnt}件のメッセージを削除しました。"

```

ちなみに、 `Messages` 配列内の要素は一つであることが前提で記載しています。

なお、AWS CLI で使える JMESPath ではなく、jq を使っているのは好みの問題というのもありますが、削除する前にメッセージ内容をJSONファイルに出力するために、`ReceiptHandle` を取得してから、`Body` を取得しているので、jq を使っています。
単に、`ReceiptHandle` を取得して削除するだけなら、--query オプション(JMESPath) を利用した方がシンプルです。

#### あとがき（言い訳）

もう少し省略した書き方ができますが、自分がシェルスクリプト初心者なんで、初心者にとってわかりやすいかなという気持ちで敢えて冗長な書き方もしています。
【8日目】OSS連携してみたい！(6)PythonでAutowareのmsgをサブスクライブ

この記事は筆者オンリーの[Advent Calendar 2023](https://qiita.com/advent-calendar/2023/aichallenge_racing_for_mas)8日目の記事です。  

# 前回までのあらすじ
OSS連携を試みるため、PythonでROS2のパブリッシャーとサブスクリプションを用いてHelloWorldしてみました。
今回はC++同様、Autowareのmsgをサブスクライブして、サブスクライブしたらログを表示するプログラムを作りたいと思います。

# ROS2でモジュール作成
[ROSのチュートリアル](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Creating-Your-First-ROS2-Package.html)の内容を例によって参照します。

# Autowareのカスタマイズ
## パッケージの作成
例によって[ROSのチュートリアル](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html)を見ながら、簡単なサブスクリプションを作ります。

[公式サイト](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html)の通り、ROS2のパッケージを作成します。

```
$ cd ~/aichallenge2023-racing/docker/aichallenge/aichallenge_ws/src/aichallenge_submit/
$ ros2 pkg create --build-type ament_python py_path_subscriber
```

[ROSチュートリアル](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html)に従って、package.xmlとCMakeLists.txtを変更します。

## ソースコード
ソースコードはこんな感じ。
Autowareのmsgのimportの仕方はまだよくわかってないです。
```py_pathwithlaneid_sub_function.py
import rclpy
from rclpy.node import Node
from autoware_auto_planning_msgs.msg import PathWithLaneId


from std_msgs.msg import String


class MinimalSubscriber(Node):
    def __init__(self):
        super().__init__("minimal_subscriber")
        self.subscription = self.create_subscription(
            PathWithLaneId,
            "behavior_planning/path_with_lane_id",
            self.listener_callback,
            10,
        )
        self.subscription  # prevent unused variable warning

    def listener_callback(self, msg):
        self.get_logger().info("I heard PathWithLaneId")


def main(args=None):
    rclpy.init(args=args)

    minimal_subscriber = MinimalSubscriber()

    rclpy.spin(minimal_subscriber)

    # Destroy the node explicitly
    # (optional - otherwise it will be done automatically
    # when the garbage collector destroys the node object)
    minimal_subscriber.destroy_node()
    rclpy.shutdown()


if __name__ == "__main__":
    main()

```

## パッケージのビルド
次に、Dockerを起動してコードをビルドします。
```
$ cd /aichallenge/
$ pip install setuptools==58.2.0
$ bash clean_build.sh 
```

以下のようにpy_path_subscriberのビルドができれば成功です。

## パッケージの実行
参考記事同様、launchファイルから実行します。本大会ではAutoware-Microを使用しており、autoware_micro_awsim.launch.xmlを変更します。

```autoware_micro_awsim.launch.xml
<!-- lane_driving -->
〜割愛〜
<group>
    <node pkg="cpp_pathwithlaneid_pubsub" exec="cpp_pathwithlaneid_pubsub"/>
</group> <!-- Simple Pub Sub -->
</group> <!-- lane_driving -->
```
実行してみます。
```
$ bash run_autoware.sh
$ bash run_awsim.sh
```
![Screenshot from 2023-12-03 22-53-49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3154772/8dd3179f-a0de-5c7e-7713-3e41a934958c.png)
PythonでもAutowareのmsgをサブスクライブできることを確認しました。

# 今後の予定
1.Autowareのメッセージをサブスクライブできるようにする。
2.Autowareのメッセージをパブリッシュし、既存のAutowareがサブスクライブするようにする。
3.OSS連携もできるように変更する。
という流れで行きたいと思います。
Goにおけるポインタ操作

# ポインタを学ぶ
3日目はポインタについてです。
Go言語においてポインタは非常に重要な概念です。

```go
package main

import "fmt"

func main() {
	i, j := 42, 2701

	p := &i         // point to i
    fmt.Println(p)  // 0x1400009c018
	fmt.Println(*p) // read i through the pointer
	*p = 21         // set i through the pointer
	fmt.Println(i)  // see the new value of i

	p = &j         // point to j
	*p = *p / 37   // divide j through the pointer
	fmt.Println(j) // see the new value of j
}
```

実行する前に挙動を考えてみることにします。

変数pに変数iの番地を代入しています。
```go
p := &i
```

pを直接出力するとメモリアドレスが入っていることがわかります。pはポインタ変数と呼びます。

次にポインタ変数のアドレスに格納されている値を参照するために、変数の前に`*`を付けて呼び出します。

```go
fmt.Println(*p)
```
変数pには変数iのアドレスが格納されています。つまり、ここでは変数の値42が出力されます。

```go
*p = 21
```
変数iの値を再代入しています。
直後では
```go
fmt.Println(i)
```
21が出力されます。


```go
p = &j
*p = *p / 37
fmt.Println(j) 
```
変数pに変数jのアドレスを代入し、変数pのアドレスの値である2701を37で除算し、変数jの値として再代入しています。
よって変数jの値は`2701/37=73`となります。

最期にプログラムを実行して確認してみましょう。

```bash
$ go run app/day3/main.go
0x1400000e0b8
42
21
73
```
よさそうです。
【Rails】form_withのf.check_box未チェック時にfalseパラメータを送らないで！

## やりたいこと
商品一覧画面で以下のような商品群の中から「在庫切れ」「価格変動あり」
それぞれもしくは両方の条件に一致するものをチェックボックスで絞り込みたい。

```
商品１　在庫なし
商品２　在庫なし　価格変動あり
商品３　　　　　　価格変動あり
etc...
```

## やったこと
form_withで以下のように実装

```search.slim
  / 在庫切れチェック
  = f.check_box :out_of_stock_alert_eq,
    class: 'form-check-input'
  = f.label :purchase_operation_out_of_stock_alert_eq, '在庫なし'
  
  / 価格変動チェック
  = f.check_box :changed_price_alert_eq,
    class: 'form-check-input'
  = f.label :purchase_operation_changed_price_alert_eq, '価格変動あり'
```

この内容で検索したところ商品1-3すべてヒットせず。
コントローラに飛んできたパラメーターを確認したところ以下のようになっていた。
```
[2] pry(#<AlertsController>)> params[:q][:out_of_stock_alert_eq]
=> "0"
[3] pry(#<AlertsController>)> params[:q][:changed_price_alert_eq]
=> "0"
```
調べたところform_withを使用した場合、Railsさんが親切に以下のようにhiddenのHTMLを生成してくれているらしい。
```
<input name="q[out_of_stock_alert_eq]" type="hidden" value="0">
<input class="form-check-input" type="checkbox" value="1" name="q[out_of_stock_alert_eq]" id="q_out_of_stock_alert_eq">
```

そのため、フォームのチェックボックスが未チェックだと"0"がパラメーターとして送られてしまい、
商品1-3はすべて条件に合致せず検索にヒットしなくなってしまう。


ちなみにこれはform_withを使用した場合でありcheck_box_tagではhiddenは自動生成されない。



## 解決策
引数に```include_hidden: false```を指定してあげればOK。

```search.slim
  / 在庫切れチェック
  = f.check_box :out_of_stock_alert_eq,
    include_hidden: false,
    class: 'form-check-input'
  = f.label :purchase_operation_out_of_stock_alert_eq, '在庫なし'
  
  / 価格変動チェック
  = f.check_box :changed_price_alert_eq,
    include_hidden: false,
    class: 'form-check-input'
  = f.label :purchase_operation_changed_price_alert_eq, '価格変動あり'
```

参考記事：
https://qiita.com/munky69rock/items/febd65ba35dc183052f0
【Go言語】シャドーイング

## シャドーイングとは
異なるスコープで同じ名前の変数が使用される場合に発生することがある
具体的には、異なるスコープで`:=`演算子を使って再宣言された変数はスコープ外の値に影響を及ぼさない

意図せずに異なる変数にアクセスしてしまう可能性があるので、注意が必要になる

### サンプル

```go
package main

import "fmt"

func main() {

	num := 10
	if true {
		num := 20        // シャドーイングが起きる
		fmt.Println(num) // 20
	}
	fmt.Println(num) // 10

	if true {
		num = 30         // シャドーイングが起きない
		fmt.Println(num) // 30
	}
	fmt.Println(num) // 30
}

```

## シャドーイングを避ける方法

1. `:=`の代わりに`=`を使う
1. shadowを利用する
使用方法は以下の記事が参考になる
* https://engineer-want-to-grow.com/go-shadow/
* https://qiita.com/sa9sha9/items/7cf08d0a3e2f47443807


## 参考

https://yourbasic.org/golang/gotcha-shadowing-variables/

https://www.oreilly.co.jp/books/9784873119694/
【初心者】３ヶ月の開発を通して学んだ３つの後悔

# 描いてる人の特徴
1. 某プログラミングスクール卒の27歳。受託企業
2. ポートフォリオはRailsで一部react使用。
3. 最初の１ヶ月はRailsのみの案件、そして最近までRails+Reactで開発。

# 入社して３ヶ月経ちました
入社が９月で研修という研修をせずそのまま現場にぶち込まれました。周りはみんな優しい方々だったのでめちゃくちゃ聞き回りました。そのおかげで自分が担当した実装部分は無事に完了し、リリースまで持っていけました。

特にこの３ヶ月でBEとFEを触らせてもらったことは本当に幸運でしかも基礎的なCRUDの部分だったので基礎の基礎をやらせてもらって本当に感謝しています。しかし、この３ヶ月でどれだけ自分のレベルが低いか（多分レベル１未満）実感したのでこれからはもっと勉強しないとなって感じました。

そこで３ヶ月の反省というかこれ実務前にやっとけばよかったってことを過去の入社前に遊び尽くしていた自分への断罪として、これからの自分の勉強方針がぶれないように残しておきたいと思います。

# １.クラスとインスタンスの違いについて
これは最初の現場でマジでキツく言われたことです。RailsのBEの実装をしていた時に以下のようなコードを書いていました

```
def self.hoge
```

特にコントローラで以上のようなクラスメソッドを連発して作ってました。当時はファットコントローラはいけないからserviceクラスを作ってコントローラの処理分けをしようと考えて作ってたのですが、コントローラのインスタンスの渡し方がわからず、作成したserviceクラスにクラスメソッドを無理やり書いてコントローラ側でそのクラスを直接呼ぶみたいなことをしていました。

しかしそれでは記述量も多くなり、何より使い回しが聞きづらいものになってしまうってことに気づきました。
self.hogeで定義したものを再利用したいときはまた別の定義でself.hoge書かないといけないのは本当にやりづらいって思いながら実装していたので案の定注意されました。

ぶっちゃけまだクラスとインスタンスの違いはわかってるけど実際に使い分けられるかというとすごい怪しいのでこれは次からの実装でも気をつけたいと強く思いました。

# 2.JavaScriptの学習不足
何故か２ヶ月目からReactのFEも実装することになりました。これが一番きつかったかもしれません。結局Reactもfunctionみたいなの使うし、buttonやinputもJSで学習するっちゃ学習するのでそのキャッチアップから始める必要があったので本当に勉強しとけばよかったと思います。引数の渡し方やconst等、Railsだけをやり続けても書き方がまるで違うのでそういったキャッチアップも進めて行かなければと思いました。

JSと書き方が似てる（参考になる程度だけど）のでFE触りたいって少しでも思ってるなら早めにJSに慣れておけばよかったと感じています。

# 3. Dockerの知識、実践経験
これはマストです。buildとupの違い、コンテナ、イメージ、ボリューム、キャッシュ、下手したらRailsの知識より大切かもしれません。特によくあったのが何故かFEかBEのどっちかが起動しなくなるトラブルでした。原因はサーバーの通信ミスっぽいんですが、コンテナのログでエラーが走ってたのでクローンし直してことなきをえました。
しかし、dockerは開発環境で必ず通る道なので覚えといて間違い無いです。てか自分もこれ最優先に勉強します。

# 終わりに
怒涛の３ヶ月でしたが理解できる範囲やできることが増えたのは確かです。やはりどんなに書籍や情報をインプットしても数個実装すれば５０％くらいは慣れで実装できるのでそういう意味で自分で色々作ることをメインでこれからも取り組んでいきたいと思います。
あとは技術もそうだがやはり人との関わり方だけは常に腰低くを心がけていきたいと思います。

コミュ力あればなんとかなるって実感したのは秘密です。
過去の解決しなかった問題を話したら、よりそうの問題解決フレームワークを教えてもらった話

この記事は、[よりそうアドベントカレンダー](https://qiita.com/advent-calendar/2023/yoriso)の12月4日の記事となります。



皆さん初めまして、よりそうでバックエンドやフロントエンドの開発を行っているjof-fumiです。
2023年1月より参画しております。


さて、いきなりですが、よりそうではエンジニア間のコミュニケーション活性化を目的として、さまざまなイベントが企画されております。
LT、NASAゲーム、雑談会や、果てはAmong Usまで、いろいろな施策が開催されています。


あるとき、エンジニア全員がLTをするという企画がありましたので、私もLTで話すことになりました。
その際、せっかくなら、過去の解決しなかった問題点を共有して、歴戦のエンジニアの皆さんの解決方法を教えてもらえたらなと思った訳です。


（そこで私が何を話したかの詳細はここでは割愛。またいつか、別の機会に。。。）

LTの後、やはり、問題があれば解決に動いてしまうのがエンジニアの性と言いましょうか、部長が解決に向けた1つの問題解決のフレームワークをを提示してくれました。


# ハイフェッツの教えは全ての問題を解決する

そう言われた私は、驚きました。銀の弾丸はここにあったのかと。。

どうやら、よりそうでは、マネジメントレイヤーは全員、ロナルド・ハイフェッツが著書の中で言及した「技術的課題」「適応課題」のアイデアを用いた問題解決フレームワークを学び、活用できるようにするための研修があるそうです。

私の問題点に関しても、そのフレームワークを通じて解決策を出せるかもしれないので、試しにやってみようということになりました。


:::note info
 「技術的課題」と「適応課題」の詳細な説明はハイフェッツ氏の著書を読んでもらえたらと思いますので詳細な説明は割愛しますが、簡単に言うと「技術的課題」は技術で解決できる課題で、「適応課題」は考え方や関係性による解決が必要な課題（＝適応が必要な課題）です。
:::



# よりそうで行われている問題解決のフレームワーク

よりそうでは以下のように行われています。
（一部省略しています。全貌が気になる方はぜひ、よりそうへ！！）

参加者：4~5名
### 役割
- ファシリテーター：1名
- 相談者：1名
- 参加者：3~4名


### やり方

1. 相談者が問題点を「現実」「理想」という切り口で記載します。
1. 相談者が参加者に状況を説明します。
1. 参加者は相談者に質問し、問題のリアルイシュー（真因）を特定します。

----------------------------------------------------------------

ここまで聞いて、「あぁ、複数人でやるタイプのなぜなぜ分析か」と思った方もいらっしゃるのではないかと思います。私も思いました。
確かにそれで解決案は出てきそうですが、「全ての問題を解決する」とは大きく出たな～と。。
しかし、このフレームワークの面白いところはここからです。

----------------------------------------------------------------

#### リアルイシューの特定方法
1. 参加者は、相談者に質問して、「理想」に対する「現実」の問題点を洗い出します。
1. 参加者は問題点をリアルイシュー（真因）とサブイシュー（真因以外の問題点）に分類し、関係性を整理します。<font color="Red">なお、ここから相談者は発言できません。</font>
1. 参加者は整理したイシューを「技術的課題」か「適応課題」か判別します。
1. 参加者はイシュー関係性や、「技術的課題」「適応課題」という属性を考慮した解決策を検討し、相談者に提案します。


いかがでしょうか。
このフレームワークでは、<font color="Red"><b>相談者が解決策を考えないのです！！</b></font>
これにはびっくりしました。
というのも、自分はエンジニアになる前は、「個人で考えて考えて考え抜く」みたいな姿勢が美徳な職種でしたので、
解決策検討フェーズで自分が黙っているということに当惑してしまいました。

<b>当事者が黙っていて、本当に妥当な解決策が導かれるものでしょうか？？？</b>


## やってみた結果

自分はもちろん相談者として参加したのですが、リアルイシュー特定の際の質疑応答以降は発言せずに見ていただけでしたが、<font color="Red">自分が思っている以上に、核心に迫る分析が出来ていました。</font>

相談内容がエンジニアならば割とよくある問題だったので、わりと容易に分析できたということもあると思いますが、自分単独の分析では届かなかったところまで分析されてく様子を見ているのは、とても面白かったです。

（質問された内容だけで深い分析までたどり着く様子を見ていると、「これだけの情報でここに辿り着くとは。。。」と言う、漫画の主人公の成長を見ているような体験ができました。）

また、「技術的課題」「適応課題」と分けて考えることで、イシューの内「技術的課題」を技術で解決さえしてしまえば、残る「適応課題」を受け入れるための心の余裕が生まれるため、この分析手法は非常に良いと思いました。


### 副次的な効果
このフレームワークは、自分以外の問題点を真剣に考えるという側面があるため、問題が解決できるというほか、<font color="Red">チームワークを高める</font>という副次的な効果があります。

というか、むしろこちらのほうが重要なのではないかと思います。
参加者は相談者のことを真剣に考えますし、相談者は参加者が自分のことを真剣に考えてくれているのを目の当たりにするので、信頼関係が構築しやすいと感じました。

### まとめ
以上、よりそうで行われている問題解決のアプローチの一部をご紹介いたしました。このほかにも、よりそうでは問題点を話すと、すぐに解決しようとフィットワーク軽く動き出すメンバーが多く、そういう文化が醸成されつつあると感じます。

みなさんの組織でも同じように、問題解決の文化の成の第一歩として、ハイフェッツの著書を読んで、問題を解決しまくりましょう！！
時計で遊ぶ。(今時231203)(ハードウェアシリアル出力)


時計で遊ぶ。(今時231203)(ハードウェアシリアル出力)


x 過去ログを見よ
x 4PINをOBでGPIO


目的
DS1307のテスト

1 NOT
2 VDD
3 GND
4 ハードウェアシリアル

8 SWD CLK
7 SWD DATA
6 SDA
5 SCL


![o_cop833.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/bc6e97db-64e9-7fdf-ae29-5cbd65476770.jpeg)



```c++



//I2C_5851_031_R1


//インクルド
#include <Arduino.h>
#include <Wire.h>
#include <HardwareSerial.h>


//定義
// デバイスアドレス(スレーブ)
#define ADDR    0x68    // 2進数 1101000


//初期化
void setup()
{

  delay(3000); //not delete

  //シリアルの初期化
  Serial.setTx(PA2_ALT1);
  Serial.setHalfDuplex();
  Serial.begin(9600);

  //i2cの初期化
  Wire.setSDA(PA12);
  Wire.setSCL(PA11);
  Wire.begin(); //pa12 pa11

} //setup


//メインループ
void loop()
{

  // レジスタのアドレスを先頭にする
  Wire.beginTransmission(ADDR);
  Wire.write(0x00);
  Wire.endTransmission();
  delay(1);

  // I2Cスレーブに8byteのレジスタデータを要求する
  Wire.requestFrom(ADDR, 8);

  // 8byteのデータを取得する
  char data_read[16] = {0x88, 0x88, 0x88}; //データバッファー
  int ii = 0;
  while (Wire.available())  {   // 要求より短いデータが来る可能性あり
    data_read[ii++] = Wire.read();       // 1バイトを受信
  }//while
  delay(1);

  //dbug
  //data_read[2] = 0x01; data_read[1] = 0x23; data_read[0] = 0x45;

  //表示変換する
  char cn1[16];  //桁
  cn1[0] = '0' + (data_read[2] >> 4);  //0 時 上位
  cn1[1] = '0' + (data_read[2] & 0xf); //1 時 下位
  cn1[2] = ':';
  cn1[3] = '0' + (data_read[1] >> 4);  //2 分 上位
  cn1[4] = '0' + (data_read[1] & 0xf); //3 分 下位
  cn1[5] = ':';
  cn1[6] = '0' + (data_read[0] >> 4);  //4 秒 上位
  cn1[7] = '0' + (data_read[0] & 0xf); //5 秒 下位
  cn1[8] = 0;

  Serial.println(cn1);

  delay(1000); //1s

} //loop




```
現役JKの１か月間プログラミング生活（初投稿）

# お詫び
タイトルにある「現役JK」とは「情報系が好きな高校生」のことであり世間一般での女子高生ではありません。当方はあくまで高校生です。
# お詫びその２
~~悲しいことにqiita-cliの設定でグダグダしたり、スプラトゥーンでビッグランとかやっていた影響で本来投稿する予定だった12月1日に投稿することができませんでした。また、それによって2日分アドカレに投稿できない日付が生まれてしまいました。その分はどこかほかのところに書きます。~~

アドカレの一日目に枠をとれたのでこちらに移動しました。また、ほかのどこかに書く予定だった2記事についても同じくアドベントカレンダーに投稿します。


# 企画説明
はじめまして「えか太郎」と申します。前述したように職業は高校生です。高校入学と同時にパソコンを買いプログラミングを始めました。しかし
* 参考書を買っても読んでない
* ゲームばかりして学んだことを忘れた
* そもそも何もアウトプットできていない
などといったこの二年間で何も得ることができませんでした。
したがって、毎日アウトプットを行うことでより学びを得たいです。
決してアドカレを25日間投稿したらもらえるグッズが欲しいとかそういうことはないです。
# 現状について
### この二年間で学んだこと
* javascript　→　ブラウザで動作する計算機を0から一人で作れる程度の能力
* Splatoon　→　サーモンランで野良カンストできるくらい。対人戦？そんなの知らない
* node.js　→　参考書を買いましたが半分も終わってないので分からないことしか分かりません（無知の知）
* ネットワーク　→　マスタリングTCP/IPを読んだのでそれなりに。
* 自作パソコン　→　10台近く組んできた。
### 感想
なにも得ることが...できませんでした

私もこれを書いていて悲しくなってきました。全然プログラミングできてないじゃない

というわけで本企画です。毎日投稿します。一か月間の「JK生活」です


# JK生活の記事一覧（順次更新予定）

* 0日目（本記事）
* 1日目　

https://qiita.com/EkaTaro3/items/93011839911b814e9527
* 2日目　

https://qiita.com/EkaTaro3/items/3a1cd3b29a7120033d57
* 3日目　

https://qiita.com/EkaTaro3/items/f3edb86fb10b8b4fb40a
* 4日目　

https://qiita.com/EkaTaro3/items/4c13b5bed593bbd424aa
* 5日目　

https://qiita.com/EkaTaro3/items/a111d6cb13e848a4fac1
* 6日目　

https://qiita.com/EkaTaro3/items/554da84d910ad25b71cd
* 7日目　

https://qiita.com/EkaTaro3/items/82765128d721a39d2695
* 8日目　

https://qiita.com/EkaTaro3/items/111aaa7ee093c5f027a5
* 9日目　

https://qiita.com/EkaTaro3/items/f75af61231356d9236cf
* 10日目　

https://qiita.com/EkaTaro3/items/37d7c19acea7bc777749
* 11日目　

https://qiita.com/EkaTaro3/items/a764cb32a6ff27215f08
* 12日目

https://qiita.com/EkaTaro3/items/f1b99d44f258e366de2d
* 13日目 


* 14日目


* 15日目

https://qiita.com/EkaTaro3/items/bbf5d74f2c895811fa27
* 16日目


* 17日目


* 18日目


* 19日目


* 20日目


* 21日目


* 22日目


* 23日目


* 24日目


* 25日目



## 謝辞（読み飛ばしてOK！）
私の書いた駄文にいいね等してくださった方ありがとうございます。
この一か月が過ぎてもQiitaに記事を定期的に投稿できるように頑張りたいと思います。
ファイルサーバーを業務中に再起動しちゃったので経緯と原因と対策を共有します

# この文章と目的
ファイルサーバーを業務中に再起動しちゃったので経緯と原因と対策を共有します。

# 経緯
- 私はサーバー管理は片手間で行っています。本業は別。
- ファイルサーバーのOSは Windows Server 2019 です。
- 勿論サーバーでも毎月の Windows Update 再起動は必須ですねー　**誰も使っていない夜中に**

サーバーの管理として Windows Update を確認する作業をほぼ毎日サーバーにリモートデスクトップ接続して
　＞　スタートボタン　＞　設定　＞　更新とセキュリティ
を行っていました。

- めんどくせー

モノぐさエンジニアは楽をするための苦労は厭いません！ ggr ！
結果、サーバーのデスクトップに
```
ms-settings:windowsupdate
```
という内容のショートカットを作成しダブルクリックすると、上記を一気に行う事が分かりました！
手数を減らして楽になった！やったねー

- ここで止めれば良かったー

調子に乗った私、上記で出てくる「更新プログラムのチェック」ボタンを押すまで出来ないか調べたところ！ ggrks ！
```
ms-settings:windowsupdate-action
```
とすれば出来ると分かりました。
　＞　スタートボタン　＞　設定　＞　更新とセキュリティ　＞　更新プログラムのチェック
やったー！

- しばらくは良かったですー

ですがある朝、一仕事終わり10時に片手間サーバー管理を始め、いつも通りにショートカットをダブルクリックしたところ警告も無しにサーバーが再起動を始めました。

- あれー？

# 被害
- 10時から15分位サーバーにアクセスが出来なくなった
- それ以前からアクセス中の方はそこまでの作業がパー（タヒ語）
ファイルサーバーですがデータベースへのアクセスなので（謎）

# 原因
「更新プログラムのチェック」ボタンは Windows Update で再起動が必要になった時点で「再起動する」ボタンになるので、それを押した事になり問答無用で再起動した。
　＞　スタートボタン　＞　設定　＞　更新とセキュリティ　＞　**更新プログラムのチェック**
のつもりが、
　＞　スタートボタン　＞　設定　＞　更新とセキュリティ　＞　**再起動する**

# 対策
上記ショートカットの内容を
```diff_shell
- ms-settings:windowsupdate-action
+ ms-settings:windowsupdate
```
と書き戻して「更新プログラムのチェック」ボタンは手動で押すに戻した。

# 謝辞
本当に申し訳ございませんでした。
YOLOv7でバウンディングボックスの色を指定する方法

YOLOv7でバウンディングボックスの色を指定する方法をメモしておきます。
YOLOv7では、バウンディングボックスの色がランダムになるため、この方法をメモすることにしました。

https://github.com/WongKinYiu/yolov7

Google Colaboratoryにて実行しました。
ランタイム->ランタイムのタイプを変更->T4 GPUを選択
```Python
from google.colab import drive
drive.mount('/content/drive')
```
```Python
%cd "/content/drive/MyDrive"
!git clone https://github.com/WongKinYiu/yolov7
```
```Python
%cd "/content/drive/MyDrive/yolov7"
!pip install -r requirements.txt
```

物体検出（結果はyolov7/runs/detectに保存される）
```Python
%cd "/content/drive/MyDrive/yolov7"
!python detect.py --weights yolov7.pt --conf 0.25 --img-size 640 --source "/content/drive/MyDrive/yolov7/inference/images/horses.jpg"
```

![horses.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/2c561c42-83f0-5d19-ec13-7ad5c19edaea.jpeg)

この画像が出力されます。
もう一回実行します。

![horses.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/aac998f5-1ae1-af81-0cd1-d127816d2c60.jpeg)


バウンディングボックスの色がランダムになることがわかります。

そこで、バウンディングボックスの色を指定するため、[detect.py](https://github.com/WongKinYiu/yolov7/blob/main/detect.py)の62行目に以下のコードを挿入します。
色の順番はBGRです。
今回は黄緑色に指定しました。
```detect.py
# 62行目
colors = [[20, 255, 181] for _ in names] # 色の順番はBGR
```

以下のコードをGoogle Colaboratoryにて再度実行します。
```Python
%cd "/content/drive/MyDrive/yolov7"
!python detect.py --weights yolov7.pt --conf 0.25 --img-size 640 --source "/content/drive/MyDrive/yolov7/inference/images/horses.jpg"
```

![horses.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/b2a1d9c5-ad5e-ff23-b412-3d5070c1de27.jpeg)


detect.pyを書き換えることで、バウンディングボックスの色を指定することができました。
この方法では、全てのクラスのバウンディングボックスの色がこの色に指定されてしまうため、クラスごとに色を変えられるように改善する必要がありそうです。
【SwiftUI】LinkPresentationを使ってTwitterの様なタイトル&OGP画像を簡単に表示する

# はじめに
昨日、「LinkPresentationを使ってURLからタイトルを取得する」という記事を書きました。

https://qiita.com/SNQ-2001/items/1abd1e1bed87d4e02a5f

今日はLinkPresentationを使ってタイトル&OGP画像を簡単に表示させてみます。

# サンプルアプリ
![Simulator Screen Recording - iPhone 15 - 2023-12-03 at 22.23.17.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/7abd068a-7eb8-0c68-069a-8c40e65eba00.gif)

# 実装
```ContentView.swift
import SwiftUI
import LinkPresentation

struct ContentView: View {
    @State private var url = ""
    
    @State private var metadata: LPLinkMetadata?
    
    var body: some View {
        List {
            Section {
                LabeledContent {
                    PasteButton(payloadType: String.self) { urls in
                        guard let url = urls.first else { return }
                        self.url = url
                    }
                    .labelStyle(.iconOnly)
                } label: {
                    TextField("URLを入力してください", text: $url)
                }
            }
            
            if let metadata {
                LPLinkSwiftUIView(metadata: metadata)
            }
        }
        .onChange(of: url) {
            Task {
                if let url = URL(string: url) {
                    metadata = try await fetchMetadata(url: url)
                }
            }
        }
    }
    
    private func fetchMetadata(url: URL) async throws -> LPLinkMetadata {
        let metadataProvider = LPMetadataProvider()
        return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<LPLinkMetadata, Error>) in
            metadataProvider.startFetchingMetadata(for: url) { metadata, error in
                if let error {
                    continuation.resume(throwing: error)
                } else if let metadata {
                    continuation.resume(returning: metadata)
                } else {
                    continuation.resume(throwing: LPError(.unknown))
                }
            }
        }
    }
}
```

```LPLinkSwiftUIView.swift
import SwiftUI
import LinkPresentation

struct LPLinkSwiftUIView: UIViewRepresentable {
    var metadata: LPLinkMetadata
    
    func makeUIView(context: Context) -> LPLinkView {
        let view = LPLinkView(metadata: metadata)
        return view
    }
    
    func updateUIView(_ view: LPLinkView, context: Context) {
        view.metadata = metadata
    }
}
```

# おわり
これ自作しようとすると大変そうですね
Appleが用意してくれてて助かります。

# 参考記事
https://qiita.com/shiz/items/93a33446f289a8a9b65d#lplinkview%E3%81%AE%E7%94%9F%E6%88%90
【MySQL Workbench】"Unhandled exception: 'ascii' codec can't decode byte XXXX" の解決方法

macOS 版 MySQL Workbench 使用時のエラーに関するメモ．

# 環境
- macOS Sonoma 14.1.2
- MySQL Workbench 8.0.33.CE

# エラー内容
![Screenshot 2023-12-03 at 20.31.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1483756/27d54054-fd7a-1061-fe3a-a6947b294db4.png)

Table Data Import Wizard を使用して CSV 形式のテーブルデータをインポートした際に上記画像のエラーが表示されました．

このエラーは，ファイル内に ascii 以外の文字コードが含まれていることが原因だと考えられます（私の場合はファイル内に UTF-8 の日本語文字が含まれていました）．

# 解決した方法
ターミナルで以下のコマンドから MySQL Workbench を実行すると， UTF-8 の文字が含まれたファイルでもインポートすることができます．

```zsh
> open /Applications/MySQLWorkbench.app
```

# インポートに成功したら...
![Screenshot 2023-12-03 at 22.07.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1483756/ed3a002e-5ff5-2749-0905-0ebb447c3f48.png)

インポートが完了した後，上記画像の赤枠のボタンを押すか，[SCHEMAS] 欄で右クリック>[Reflesh All] を選択すると，インポートしたデータがスキーマに反映されます．

# 参考
https://teratail.com/questions/342987
プリザンターのサーバスクリプトの良いところまとめ

本記事は、[OSSのノーコード・ローコード開発ツール「プリザンター」 Advent Calendar 2023](https://qiita.com/advent-calendar/2023/pleasanter) の3日目の記事です。

# 概要
[プリザンター](https://pleasanter.org) は [サーバスクリプト](https://pleasanter.org/manual?category=2250) によりサーバサイドでJavaScriptを実行することができます。これにより、データの加工や外部システムとの連携などが可能です。この記事では [サーバスクリプト](https://pleasanter.org/manual?category=2250) の良いところをまとめていきます。

# 内容
## CSVインポート時やAPIによる登録時にも動作
クライアントサイドのスクリプトを使用して特定の項目の値を変更する処理を組み込んだ場合、CSVのインポートやAPIによるデータ登録時には動作させることができません。[サーバスクリプト](https://pleasanter.org/manual?category=2250) を使用した場合、データが登録される直前で実行することができるため、CSVインポート時やAPIによる登録時にも動作させることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78230/e5f969ba-1b9d-fb39-cdac-c8d0321664b5.png)

サンプルコード（[サーバスクリプト](https://pleasanter.org/manual?category=2250)、更新前にチェック）
```JavaScript
// 分類A に 分類B と 分類C を結合した文字列を格納する
// 編集画面でもCSVインポートでもAPIによる登録時も動作する
model.ClassA = model.ClassB + model.ClassC;
```

## 外部システムとの連携時にAPIキーを隠蔽できる
[サーバスクリプト](https://pleasanter.org/manual?category=2250) を使用すると、管理者のみがアクセス可能な管理画面にコードを隠蔽することができます。そのため、外部のシステムへアクセスするためのAPIキーなどが一般利用者のブラウザに表出せずセキュアな連携を行うことができます。外部システムとの連携には、[HTTP Client](https://pleasanter.org/manual/server-script-http-client) を使用します。

```mermaid
sequenceDiagram
  actor U as Web Blowser
  participant P as Pleasanter
  participant S as Pleasanter<br/>Server Script<br/>ここにコードを書くと<br/>一般ユーザは見えない
  participant H as Other Web API
  U->>+P: HTTP
  P->>+S: 
  S->>+H: HTTP
  H-->>-S: API Response
  S-->>-P: 
  P-->>-U: HTML
```

## レコードの表示制御が行える
クライアントサイドのスクリプトでは行えないレコードの表示制御を行うことができます。特定の条件のレコードをユーザに見せたくない場合など [view.Filters](https://pleasanter.org/manual/server-script-view-filters) を使用して実現します。

サンプルコード（[サーバスクリプト](https://pleasanter.org/manual?category=2250)、ビュー処理時にチェック）
```JavaScript
// チェックA がオンになっているレコード以外は表示しない
view.Filters.CheckA = true;
```

## 一覧画面のレコードの並び替えを動的に行える
前述のフィルタと同様にクライアントサイドのスクリプトでは行えないレコードの並び替えを行うことができます。[view.Sorters](https://pleasanter.org/manual/server-script-view-sorters) を使用することで、特定のソート条件をスクリプトにより設定することができます。

サンプルコード（[サーバスクリプト](https://pleasanter.org/manual?category=2250)、ビュー処理時にチェック）
```JavaScript
// 数値A で降順のソートを行う
view.Sorters.NumA = 'desc';
```

## メールやチャットへの通知を行うことができる
[notification](https://pleasanter.org/manual/server-script-notification) を使用することで、メールやSlack、Teamsなどに任意の通知を行うことができます。

サンプルコード（[サーバスクリプト](https://pleasanter.org/manual?category=2250)、更新後にチェック）
```JavaScript
let notification = notifications.New();
notification.Type = 6;  //通知先Teamsの指定
notification.Address = 'https://***/***'; //通知先Teamsのwebhook urlを指定
notification.Title = '通知テスト';
notification.Body = 'サーバスクリプトから通知しています。';
notification.Send();
```

## おわりに
プリザンターは今後も業務に役立つノーコード・ローコード開発ツールとして進化を続けてまいります。応援よろしくお願いします。皆さん良いクリスマスをお過ごしください。
https://pleasanter.org
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78230/1764addf-fe7a-b707-0d06-d84ad7899014.png)

:star:GitHubにスターつけてもらえたら嬉しいです。
https://github.com/Implem/Implem.Pleasanter
[備忘録] Nextjsプロジェクトにstorybookを導入する場合に発生するModule not found対応

## これはなに
Nextjs プロジェクトに storybookを導入したときに以下のエラーが発生したので解決する方法を記録したもの

```
Module not found: Error: Can't resolve '@/stories/Button' in './src/stories'
```

### なぜ起きたか
Nextjs プロジェクトを作成した際に tsconfig.json に以下のエイリアスが作成される
```json:tsconfig.json
{
    "paths": {
        "@/*": ["./src/*"]
    }
}
```

作成されたエイリアスを使ってモジュールを参照するようにしていたところ、 typeScript の中ではエイリアスの認識が出来るものの storybook(内部的には webpack )側がそれを認識できないらしい

### 解決にあたって満たしたいこと
- モジュール参照エラーが解消してほしい
  - 当然
- エイリアスによるモジュール参照は引き続き行いたい
  - 相対パスで参照すれば解決するがそれはしたくない
- パスエイリアスの定義は tsconfig.json にのみ記述した状態を維持したい
  - 他の設定ファイルに同じエイリアス定義を書きたくない( DRY にしたい)

ネットで検索して解決自体はできたが、求める解答を得るのに非常に時間がかかってしまったため簡潔に備忘録を残すことにした

## 環境
- next: 14.0.3
- storybook: 7.6.3

## 再現手順
### Nextjs プロジェクトを作成する

```sh
npx create-next-app@latest

✔ What is your project named? … .
✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes
```

### storybook を導入する

```sh
npx storybook@latest init
```

### storybook をビルドして現時点では正常動作することを確認する

```sh
# npm
npm run build-storybook
# yarn
yarn build-storybook
```

当然ビルドは通る

### エイリアスを使ってモジュール参照するように変更する
storybook の import 文を書き換える

```ts:src/stories/Button.stories.ts
- import { Button } from './Button';
+ import { Button } from '@/stories/Button';
```

### storybook をビルドする

```sh
# npm
npm run build-storybook
# yarn
yarn build-storybook
```

Module not found エラーが発生する
```
Module not found: Error: Can't resolve '@/stories/Button' in './src/stories'
```

## 解決方法
`tsconfig-paths-webpack-plugin` を使用して tsconfig.json のパスエイリアスを参照できるようにして storybook の設定に組み込むと解決する

### プラグインのインストール
```sh
# npm
npm install --save-dev tsconfig-paths-webpack-plugin
# yarn
yarn add -D tsconfig-paths-webpack-plugin
```

### storybook の設定
設定を編集する

```ts:.storybook/main.js
import type { StorybookConfig } from "@storybook/nextjs";
// プラグインのインポート
import TsconfigPathsPlugin from "tsconfig-paths-webpack-plugin";

const config: StorybookConfig = {
  stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
  addons: [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-onboarding",
    "@storybook/addon-interactions",
  ],
  framework: {
    name: "@storybook/nextjs",
    options: {},
  },
  docs: {
    autodocs: "tag",
  },
  // ここから追加
  webpackFinal(baseConfig) {
    baseConfig.resolve!.alias = {
      ...baseConfig.resolve!.alias,
    };
    baseConfig.resolve!.plugins = [
      ...(baseConfig.resolve!.plugins || []),
      new TsconfigPathsPlugin(),
    ];
    return baseConfig;
  },
  // ここまで
};
export default config;

```

## まとめ
プラグインを導入して tsconfig.json のエイリアス設定をそのまま storybook(webpack)に認識させて解決した

## 参考
https://www.kosotudev.com/articles/storybook-webpacker
乱数固定の落とし穴(Python, PyTorch)

## 直面した問題
1. PyTorchで実装したある学習モデルを、他の学習スクリプトでも使おうと流用。
1. モデルが正常に機能しているか確かめるためにSEEDを固定し学習してみると、モデルの初期値が移植前のものと異なることが判明。
1. モデルのパラメータ等が同じになるよう見直すなどしてみるも、同じ初期値で初期化されない。

## 原因
- SEEDを固定しても乱数を使うタイミング(固定してから乱数を使う回数)が異なれば異なる乱数が得られます。
- 固定したい乱数(今回、モデルの初期値)の生成直前でSEEDを固定する必要があります。

## 解説
### 前提知識
Pythonの乱数固定はモジュールごとに行う必要があります。
PyTorchの環境では一般的に以下のようにPythonの組み込みrandomモジュール、numpy、PyTorchのSEEDを固定すれば良いと思います。

```python
import random
import numpy as np
import torch

def fix_seeds(seed=0):
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.backends.cudnn.benchmark = False
    torch.backends.cudnn.deterministic = True
```

下記記事参考にさせて頂きました。

https://qiita.com/north_redwing/items/1e153139125d37829d2d

**補足 2023/12/05
numpyの乱数固定については通常globalなnp.random.seed()よりもnp.random.RandomState()を使う方が良いというご指摘を頂きましたので補足致します。**

### 乱数の挙動について
乱数の挙動の詳細を見ていきます。
ramdomモジュールを例にしていますが、numpy、PyTorch共に考え方は同じと思われます。

```python
import random

a = [random.randint(0, 9) for i in range(5)]
print(a) # -> [9, 4, 9, 0, 5] (実行例、実行の度に結果は変わる)
```

このコードでは、0～9の範囲の長さ5の乱数列が生成されます。
乱数なので当然、実行の度に結果は変わります。

では次にSEEDを固定してみます。

```python
import random

random.seed(0)
a = [random.randint(0, 9) for i in range(5)]
print(a) # -> [6, 6, 0, 4, 8] (実行例、何度実行しても結果は同じ)
```

SEEDを固定すれば、何度実行しても同じ乱数列が得られます。
ここまでは問題ありません。

では次に乱数列を2つ作ってみます。
例では同じことを2回繰り返しているだけです。

```python
import random

random.seed(0)

a = [random.randint(0, 9) for i in range(5)]
print(a) # -> [6, 6, 0, 4, 8] (何度実行しても結果は同じ)

b = [random.randint(0, 9) for i in range(5)]
print (b) # -> [7, 6, 4, 7, 5] (何度実行しても結果は同じ)
```

乱数列aとbは何度実行しても同じなのですが、aとbは違う乱数列になっています。
aとbを同じ乱数列にするためには、bの乱数を得る前にもう一度SEEDを固定します。

```python
import random

random.seed(0)

a = [random.randint(0, 9) for i in range(10)]
print(a) # -> [6, 6, 0, 4, 8]

random.seed(0) # これを追加

b = [random.randint(0, 9) for i in range(5)]
print (b) # -> [6, 6, 0, 4, 8]
```

なお以下のように乱数列を10個生成するようにしてみると、結果はaとbを連結したものになっていることが分かります。
```python
import random

random.seed(0)

a = [random.randint(0, 9) for i in range(10)]
print(a) # -> [6, 6, 0, 4, 8, 7, 6, 4, 7, 5] (aとbを連結した形)

```

乱数は呼ばれる度にランダム（に見える）値を返す仕組みですが、SEEDの固定とはその数列をリセットするための操作となるため、同じ数列を得たい場合にはリセットしてから何回乱数が生成されているかの回数を同じにしなければなりません。
しかし実際には回数を同じにするより、必要な場面で毎回SEEDを固定し直すのが簡単です。

### 今回のトラブルの原因
今回は以下のようなコードを使っていました。

```python
import random
import numpy as np
import torch

fix_seeds(0) # とりあえずプログラムの先頭あたりでfixするのが一般的なのでそうしていた

# ～いろいろな初期化処理等～

model = MyModel()

# ～学習処理～

def fix_seeds(seed=0):
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.backends.cudnn.benchmark = False
    torch.backends.cudnn.deterministic = True
```

～いろいろな初期化処理等～
が移植前と移植先のコードで異なっており、その中で恐らく乱数が使用されていたため、モデルの初期化に使われる乱数列が変化したためこのようなトラブルが起こっていました。
プログラムの先頭だけではなく、以下のように固定したい対象（今回の場合モデルの初期化処理）の直前でSEEDを再固定することで解決しました。

```python
fix_seeds(0) # モデルの初期化直前で再固定
model = MyModel()
```

## まとめ
- 同じモデルや自作のクラス/関数を流用しても、利用する側のコードが異なれば乱数が変わる可能性があります。
- 乱数のSEED固定を行うと、生成される乱数列が初期化されます。
- 同じ数列を確実に得たい場合、その度にSEEDを固定するのが安全です。


とは言え、コードが大きくなると見逃す箇所も出てくると思います。
PyTorchのようにフレームワークを使う場合、関数の中で知らぬ間に乱数が使われているなどということもありそうです。
乱数の固定はいわゆる再現性の確認に使われると思いますが、そこばかりに注力しても無駄な労力を生じる可能性があるので、ほどほどにしておいた方が良いのかもしれません。
[Android] Contextって何者なん？~Application Context編~

## Contextって何者？
Androidではよく引数にcontextを渡すことがあります。(画面遷移のintent作成時など。)
私もAndroidアプリを開発を始めた際に、疑問に思ったことがあります。
ですので、今回はこの**Context**に注目してみようと思います。

## 大まかに2種類あるContext
実はAndroidにおけるContextは大きく分けて以下の2種類があります。
1. Application Context
1. Activity Context

今回は**Application Context**について考えてみて、明日Activity Contextについて記載したいと思います。

## Contextとは
まず、公式サイトの内容を見てみたいと思います。
https://developer.android.com/reference/android/content/Context
 >Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.

などと記載されていますが、要約すると、「アプリケーション環境に関するグローバル情報へのインターフェイスで、このクラスによってAndroid側のシステムの値やメソッドにアクセスできる。」ということだと思います。

## Application Context
では、先ほどから出てきているApplication Contextについてですが、
そもそものApplicationクラスを見てみると、Contextクラスを継承しています。
https://developer.android.com/reference/android/app/Application

そのためApplication Contextは、**アプリで基本的に一つしか存在しません。**
(明日も記載しますがActivity Contextは違います。)

これはとても大切なことでこのことを理解せずに実装すると、メモリリークを起こす可能性があります。
こちらに関しては、以下の記事などに詳しく記載されていましたのでここでは割愛します。
https://blog.meilcli.net/2018/12/26/185752
https://qiita.com/roba4coding/items/0585b8240873ec5e9c20

ということで、少しはContextについて理解できましたでしょうか？
明日はもう一つのActivity Contextについて記載したいと思います！
ではまた明日:writing_hand:
ライブドアブログの記事全部文字列で引っこ抜く

```python
import requests
from bs4 import BeautifulSoup
from pprint import pprint
import re


def content_get(url):
    html = requests.get(url).content
    soup = BeautifulSoup(html, 'html.parser')
    return soup


def cleanhtml(raw_html):
    cleanr = re.compile("<.*?>")
    cleantext = re.sub(cleanr, "", raw_html)
    return cleantext


url = "https://{id}.livedoor.blog/"
html = requests.get(url).content
soup = BeautifulSoup(html, 'html.parser')
sidebody = soup.find_all('div', {'class': "sidebody"})
pprint(sidebody)
urls = []
for i in sidebody:
    for a_tag in i.find_all('a'):
        url = a_tag.get('href')
        if url:
            urls.append(url)
pprint(urls)
for i in urls:
    true_url = i
    true_content = content_get(true_url)
    true_body = true_content.find('div', {'class': 'article-body-inner'})
    print(true_body.get_text())

```