OmniAICreator/Qiita-1.07M huggingface dataset:
図解即戦力 ISO 27001の規格と審査がこれ1冊でしっかりわかる教科書

https://www.amazon.co.jp/%E5%9B%B3%E8%A7%A3%E5%8D%B3%E6%88%A6%E5%8A%9B-27001%E3%81%AE%E8%A6%8F%E6%A0%BC%E3%81%A8%E5%AF%A9%E6%9F%BB%E3%81%8C%E3%81%93%E3%82%8C1%E5%86%8A%E3%81%A7%E3%81%97%E3%81%A3%E3%81%8B%E3%82%8A%E3%82%8F%E3%81%8B%E3%82%8B%E6%95%99%E7%A7%91%E6%9B%B8-%E6%A0%AA%E5%BC%8F%E4%BC%9A%E7%A4%BE%E3%83%86%E3%82%AF%E3%83%8E%E3%82%BD%E3%83%95%E3%83%88-%E3%82%B3%E3%83%B3%E3%82%B5%E3%83%AB%E3%82%BF%E3%83%B3%E3%83%88-%E5%B2%A1%E7%94%B0%E6%95%8F%E9%9D%96/dp/4297107546/ref=asc_df_4297107546/?tag=jpgo-22&linkCode=df0&hvadid=342428653904&hvpos=&hvnetw=g&hvrand=4254501101136112872&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009255&hvtargid=pla-814515640073&psc=1&th=1&psc=1

本書を手に取った背景
セキュリティのプリセールスをやっているのですが、ガイドライン関連をちゃんと勉強したことが有りませんでした。
グローバルにはこういったガイドラインはたくさんありますが、ひとつくらいちゃんと勉強すレバ、他のものも理解しやすくなるんじゃないかと。
ということで、国内ではよくきくISOについて勉強しようと思いました。

本書のテーマ
ISO/IEC27001を認証取得する企業、担当者を対象に制度、要求事項、ISMS構築、審査について解説している。

個人的なポイント
１，ISOとは
サービスに関して全世界で同じレベルのものを提供できるようにする。国際的な基準
ISMS-ACで審査を受けて認証取得する。

２，ISMS
組織体制の整備や訓練などの情報セキュリティマネジメントシステム。ISOはこれを維持することを目的としている。
ISMS管理者・事務局・担当者・内部監査員がいるがそれぞれに求められる役割がある。

３，組織の目的からブレイクダウンする
CISOハンドブックにも関係してくるところ。ISOの必要性がわかってくる。
内部・外部課題を明確にして組織にセキュリティを適用していく。

４，関連する専門組織
JPCERT,ISMS-AC,PPC,IPA,JNSA,などときどき聞く組織名もでてくる。なんとなく組織の名前は知ってたけどこの辺で関係してくる。
IPAが公開している脆弱性情報もISOが絡むと非常に重要。自社で導入している設備の関連脆弱性情報は日々ここでチェックすべき。

５，変更管理
組織に影響を与える業務、設備、システムの変更を管理すること。PMPも勉強したことがあってこの辺がしっくりこなかったので、あえてピックアップした。

１番心に残った部分
PMBOKのリスクマネジメント部分に特化した内容だった。PMPを取得していたので各論については抵抗なく入ってきたが、総論のほうが理解できてなくて、勉強できてよかった。

今後やれること
・プレゼン時に解説をいれる
・他のガイドラインも勉強する
・関連組織の情報を日々チェックする

まとめ
ガイドラインがどのように考えられてるか、今まではおぼろげだったがはっきり見えてきた気がする。GDPR、CISなど他のガイドラインも勉強していきたい。
CISOハンドブックなどとも紐づけながら、習得すべき内容だと感じた。
pako.jsで圧縮したgzipバイナリをpythonで展開する


前回、こんな記事を書きました。

https://qiita.com/masakun1150/items/e678b72b780caa61c594


今度はその逆を実装する必要が出てきたのですが、これはこれでハマったので備忘録

# まずは前回使ったzlibのライブラリpakoを使って圧縮する

### pako.js

https://github.com/nodeca/pako

## フロント側

```typescript
export const handleGzipData = (
  targetData: object | object[],
  isErrorAlert = true
): number[] | '' => {
  try {
　　// まずは文字列に変換
    const targetDataOfString = JSON.stringify(targetData)

    // python側ではバイナリ配列で渡す
    const gzipedUnit8Array = pako.gzip(targetDataOfString)

    return [...gzipedUnit8Array]
  } catch (error) {
    if (isErrorAlert && error instanceof Error) {
      errorHandring(error, `エラーコード：XXXX`)
    }

    return ''
  }
}
```

はじめはgzip圧縮されたgzip文字列をbase64encodeしてバックエンドにリクエストしていたのですが、Python側でうまくdecodeできないため、
以下の記事をみてバイナリ文字列なら展開できる旨が記載されていたので試してみることに！！

https://stackoverflow.com/questions/72336779/gzip-compression-different-output-for-the-same-input-using-python-gzip-and-js

そしたら案の定うまくいきましたとさ。

## バックエンド側

```python
import gzip

data = bytearray(data)
data = gzip.decompress(data)
data = data.decode()
data = json.loads(data)

```

# 一言

いやー多言語間の圧縮／解凍はハマりますね〜
Visual Studio Code 基本操作

■コマンド
設定 : Ctrl + ,
コマンドパレット : Ctrl + Shift + P
ターミナル : Ctrl + @

■設定
ユーザー設定
ワークスペース設定
フォルダ 設定

https://forest.watch.impress.co.jp/docs/review/1517819.html
【AZ-900】Azureの基本的構造

## はじめに
AZ-900の資格取得に向けた学習の中で学んだことを簡単にまとめます。

## Azureのリソース管理に使用するサービス

#### 1. Azureポータル
Azureの利用者がAzure上のリソースの作成/変更/削除を行う、webのグラフィカルな統合コンソール。

#### 2. Azure PowerShell、Azure CLI
利用者が使用しているパソコンなどから**コマンド操作**でリソースの管理が可能。

#### 3. Azure Cloud Shell
Azureポータルに組み込まれているwebベースのターミナル機能。Azure PowerShellやAzure CLIなどのSDK (開発ツール）のインストールを行わなくても、コマンドでAzureのリソースを管理できる。

#### 4. Azure Marketplace
事前構成済のAzureのアプリケーションやテンプレートを公開している。

## Azureアーキテクチャのコアコンポーネント
### Azureアカウント
Azureのリソースを管理するためのユーザーアカウント。個人のMicrosoftアカウントやGitHubアカウント、または組織アカウントでセットアップ可能。

### リソースグループ、サブスクリプション、管理グループ
- **リソース**
購入したAzureサービスと１対１の関係にある管理エンティティ。

- **リソースグループ**
リソースをまとめて管理するための論理的なコンテナ。
全てのリソースは必ず一つのリソースグループに所属する。
リソースは後からリソースグループを移動できるため、運用しながらリソースグループを分割することも可能。

- **サブスクリプション**
リソースグループの上位に位置付けられる論理的なコンテナ。Azureの請求書はサブスクリプション単位で発行される。

- **管理グループ**
サブスクリプションの上位に位置付けられる論理的なコンテナ。作成は必須ではなく、管理グループを別の管理グループに含めるという階層構造が取れる。

### Azure Resource Manager (ARM)
Azure内のリソースの作成/更新/削除を担うマネージドサービス。
各インターフェイスからのリソースの管理要求は必ずARMを経由する。

また、リソース作成時に参照される、リソース構成の定義情報を記載したJSON形式のファイルを`Azure Resource Manager テンプレート`という。ARMテンプレートは、**リソースの繰り返し再作成作業の自動化**などに効果を発揮する。

### 可用性ゾーン、リージョン、地域
- **可用性ゾーン(Availability Zones = AZ)**
独立したデータセンター、ネットワーク、電源、冷却装置などを備えた高可用性確保のためのサービス。

- **リージョン**
    - 一つ以上の可用性ゾーンから構成されるAzureサービスの展開地域。
日本では東日本リージョン(埼玉県)と西日本リージョン(大阪府)の二ヶ所でサービス展開されている。

    - Azureが指定する複数リージョン構成をする際に最適な組み合わせを**リージョンペア**といい、日本では東日本リージョンと西日本リージョンがリージョンペアに指定されている。

    - 特別なリージョンもあり、北米には**Azure Government**という物理的・論理的に独立したリージョンが存在する。
    - microsoftではない他の会社がリージョンの管理を行うケースもあり、ドイツリージョンはT-Systemsが、中国リージョンは12Vianetがデータセンターの管理を行なっている。

- **地域(Geo, Geography)** 
地理的・市場的にまとまった区域。リージョンは必ずいずれかの地域に含まれる。

## 参考書籍
「Microsoft認定資格試験テキスト AZ-900:Microsoft Azure Fundamentals」　須谷聡史/富岡洋/佐藤雅信
「世界に認められたAIスタートアップが、Djangoを使って感じる良さとは？」参加レポート #rc_tech

７月５日（水）、「[世界に認められたAIスタートアップが、Djangoを使って感じる良さとは？](https://revcomm.connpass.com/event/285318/)」に参加しました。

## 「Djangoの特徴とRevCommにおける選定理由」
資料: https://speakerdeck.com/revcomm_inc/djangonote-zheng-torevcommniokeruxuan-ding-li-you
スピーカーは小門 照太さん。
RevCommさんが運営しているサービス「[MiiTel](https://miitel.com/jp/)」のバックエンドで採用しているDjangoの利点についての解説でした。

RevCommさんは2019年頃にDjangoを導入していて、マイクロサービス化されており、Djangoアプリケーション数は約40とのことです。

Djangoの利点として、以下を挙げていました。
- セキュリティ対策が標準でサポートされている
- O/Rマッパー、マイグレーション管理が便利
- エコシステムの充実
- チーム開発のしやすさ

### セキュリティ対策が標準でサポートされている
XSS, CSRF, SQLインジェクションなど、はじめからセキュリティ対策が用意されている点が魅力的とのことです。
ここはフルスタックフレームワークならではの利点ですよね。
小さなフレームワークには好きなライブラリを組み合わせて利用できる良さはあるかと思いますが、ウェブサービスでよくあるセキュリティ問題について、Djangoを導入しておけばとりあえず対策が取れるのは大きな魅力だと思います。

### O/Rマッパー、マイグレーション管理が便利
SQLAlchemyのような他のO/Rマッパーと比べても処理を簡潔に書けて楽に開発できるのが良いとのことです。
ただし、SQLAlchemyの目的はDjangoのO/Rマッパーとは異なる部分があり、DjangoのO/Rマッパーにはない機能も多数存在するため、単純にコードの記述量だけで優劣を決めることはできないとも強調されていました。

また、`python manage.py shell` で対話モードでO/Rマッパーの操作を行える点、マイグレーションの仕組みが最初から用意されている点も利点として挙げられていました。

### エコシステムの充実
`INSTALLED_APPS`に値を追記するだけでサードパーティライブラリで機能を拡張できるため、必要な機能を早く実装できる点も利点とのことです。
Djangoの便利な拡張は[awesome-django](https://github.com/wsvincent/awesome-django)で公開されているので、興味がある方は一度チェックしてみてはいかがでしょうか。

### チーム開発のしやすさ
Djangoプロジェクトはアプリケーション単位で分離でき、似たようなディレクトリ構成になりやすいのでチームでの開発と相性が良いとのことです。
また、テストはデフォルトでテストケース間のデータを独立して扱える（テストメソッドごとにDBトランザクションが独立している）のも良いとのことです。

## 「快適なテスト体験を実現する、Djangoのテスト思想と工夫」
資料: https://speakerdeck.com/revcomm_inc/kuai-shi-natesutoti-yan-woshi-xian-suru-djangonotesutosi-xiang-togong-fu
スピーカーは近藤 智哉さん。
Djangoテスティングライブラリの基本的な考え方と、RevCommさんで実践されているテストの工夫についてのお話でした。

まず、`django.test.TestCase`を使ったテストクラスの実行の流れについて。
`django.test.TestCase`はPython公式の[unittest.TestCase](https://docs.python.org/ja/3/library/unittest.html#unittest.TestCase)を継承しているので基本的な流れは`unittest.TestCase`と同じですが、独自の機能も存在するので、それらがどのタイミングで呼ばれるのか把握しておいたほうがよいとのことでした。

また、テスト実行時に`test`コマンドのオプションで以下を指定する工夫もされているそうです。
- `--keepdb`で毎回マイグレーションが走らないようにしてDB初期化処理を高速化
- `--reverse` 、 `--shuffle` オプションで実行順を変える

テスタビリティの高いコード書き、テスティングライブラリの制限によりテストを実行できないことがないようにすることも重要とのことでした。

## 最後に
久々にDjango関連のイベントに参加できて楽しかったです。
どんなフレームワークにも言えることですが、フレームワーク作者の意図や慣例に反した設計にすると、そのフレームワークの利点を損なってしまうことがよくあります。開発する際は、今日のお話にあったようなDjangoの良い点を意識した上で、それを損なわないようにするにはどうするべきかを考えることが大事なのではないかな、と思いました。
また、今年は10月7日に[DjangoCongress
JP 2023](https://djangocongress.jp/)というオフラインイベントが開催されますが、RevCommさんからスピーカーとして登壇される方がいらっしゃるかもしれませんね！
グローバル変数msgについて知らなければならないこと全て: Solidity

あなたがこの記事を読んでいるということは、おそらくSolidityの公式ドキュメンテーションを既に読んだり、いくつかの初級チュートリアルで遊んだりして、msg.sender、msg.value、msg.dataといった構文に遭遇したことでしょう。そして、おそらく「msg.senderって何？」と疑問に思ったことでしょう。

## グローバル変数`msg`とは何か？

まず、Solidityには特別な変数と関数が存在します。これらはブロックチェーンに関する情報を提供したり、エラーハンドリングを支援したり、数学や暗号化関数を利用したり、そしてコントラクトアドレスやコントラクト自体に関する情報を提供するために使われます。

`msg`という変数は、これらの特別な変数の一つで、ブロックチェーンの特定のプロパティにアクセスするために使用されます。この`msg`変数には、いくつかの特別なプロパティがあり、それぞれについて詳しく見ていきましょう。

### `msg.sender`

`msg.sender`は、現在の関数呼び出しの送信者（呼び出し元）のアドレスを表します。これは通常、トランザクションの送信者のアドレスです。

以下に、`msg.sender`を使用した簡単なスマートコントラクトの例を示します。

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SimpleContract {
    address public owner;

    constructor() {
        owner = msg.sender;
    }
}
```

##### Remixの操作:

1. 上記のコードをRemix IDEに貼り付けます。
2. コンパイルします。
3. Deployボタンを押してコントラクトをデプロイします。
4. update関数を呼び出します。
5. senderボタンを押してmsg.senderの値を取得します。これはupdate関数を呼び出したアカウントのアドレスになります。

上記のスマートコントラクトは非常に単純です。コントラクトがデプロイされると、`msg.sender`を使用してコントラクトの所有者（`owner`）を設定します。これはコントラクトをデプロイしたアドレス、つまりトランザクションの送信者です。

🔽ACCOUNTにアドレスを選択し、デプロイする。

![スクリーンショット 2023-07-05 午後8.06.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3479750/aabc3a81-bc3e-2f89-c1e4-093dba79dc4a.png)

🔽この段階ではvalueの値はまだ`0`.
![スクリーンショット 2023-07-05 午後8.11.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3479750/4cac9944-b6cf-7062-b992-d7ddd09bee09.png)
🔽updateを押してからsenderを押すと、さっき設定したアドレスが入っている。
![スクリーンショット 2023-07-05 午後8.11.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3479750/fe066ebb-31c0-aff1-bacf-98d2c43fe02e.png)


### `msg.value`

`msg.value`は、トランザクションと共に送信されるEtherの量（Wei単位）を表します。これは通常、関数が`payable`である場合に利用します。

以下に、`msg.value`を使用した簡単なスマートコントラクトの例を示します。

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SimpleDonation {
    uint public totalDonations;

   

 function donate() public payable {
        totalDonations += msg.value;
    }
}
```

上記のコントラクトでは、`donate`関数は`payable`と宣言されています。これにより、この関数はEtherを受け取ることができます。関数が呼び出されると、`msg.value`を使用して送信されたEtherの量を取得し、それを`totalDonations`に追加します。

### `msg.data`

修正中

### 複数の`msg`プロパティを使用したコード

最後に、`msg.sender`、`msg.value`を同時に使用したスマートコントラクトの例を示します。

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Auction {
    address public highestBidder;
    uint public highestBid;

    function bid() public payable {
        require(msg.value > highestBid, "There already is a higher bid.");

        highestBidder = msg.sender;
        highestBid = msg.value;
    }
}
```

このコントラクトはオークションシステムを表現しています。ユーザーは`bid`関数を呼び出し、Etherを送信して入札します。`msg.value`を使って送信されたEtherの量を取得し、`msg.sender`を使って送信者のアドレスを取得します。入札が現在の最高入札額を上回っていれば、新たな最高入札者と最高入札額が更新されます。

🔽Deployしてvalueに２を設定した例
![スクリーンショット 2023-07-05 午後8.48.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3479750/bbc47421-34d7-2594-c6c0-6e524a2c990b.png)

まとめると、`msg.sender`、`msg.value`、`msg.data`はSolidityの重要なグローバル変数であり、それぞれがトランザクションの送信者、送信されたEtherの量、関数の引数など、重要な情報を提供します。これらの理解はSolidityでの開発に不可欠であり、慎重に扱う必要があります。

以上がSolidityの`msg`についての解説です。他にも疑問や質問があれば、ぜひお寄せください。Solidityでの開発を楽しみましょう！
systemd

centos7からsystemd採用
```
[linuxstudy@localhost ~]$ systemctl get-default
graphical.target
```
```
[linuxstudy@localhost ~]$ ls /etc/systemd/system/display-manager.service  -l
lrwxrwxrwx. 1 root root 35 Jun 21 02:03 /etc/systemd/system/display-manager.service -> /usr/lib/systemd/system/gdm.service
```
モバイル版Discodeのような画面の動きを作ってみた

# はじめに

>この記事は私が気になったことや主にパッケージなど挙げていき自分の為の記録または
参考になればと思い書いています。
自分自身ググり力などまだまだなので
少しでも同じような方の手助けになればと思って記事を書いています。

### 今回紹介するパッケージ

モバイル版のDiscodeのUIがすごくて自分でも作りたいなと思って作ろうと思いました。

検索をかけてみたところ(ググり力足りなくて知人に調べて見つけてもらえました:joy:)
このような記事を発見しました。

https://flutterawesome.com/discord-inspired-navigation-for-flutter-apps/

この記事を参考にしながら簡単なアプリを作っていこうと思います。
[パッケージはこちらになります。](https://pub.dev/packages/overlapping_panels/install)

https://pub.dev/packages/overlapping_panels/install

Tarminalで
```
$ flutter pub add overlapping_panels
```
と打ってpabspeck.yamlに追加すると準備完了です。

それぞれCenter,Left,RightのView画面を作った後にDiscodeのような動きになる画面を作成します。
(Center,Left RightPageは今回Textのみの表示になるので簡単に作れると思うので割愛しています)

コード

```php
import 'center_page.dart';
import 'package:discode/left_page.dart';
import 'package:discode/right_page.dart';
import 'package:flutter/material.dart';
import 'package:overlapping_panels/overlapping_panels.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        OverlappingPanels(
          left: Builder(builder: (context) {
            return const LeftPage();
          }),
          right: Builder(
            builder: (context) => const RightPage(),
          ),
          main: Builder(
            builder: (context) {
              return const CenterPage();
            },
          ),
        ),
      ],
    );
  }
}
```

ここでは**OverlappingPanel**のpackageを使ってそれぞれ動かして表示したい場所にSet
していきます。

そうすると...

![Qiita用.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3494485/34d2de3c-23ef-f026-502a-2d59109fa356.gif)



Discodeのような画面の動きになりました！

:::note warn
**OverlappingPanels**の中のプロパティに`onSideChange`があるのですがまだ自分は理解できていない部分がある為この記事では割愛させて頂いてます
またこのパッケージを使って何か作成する際に使うことがあれば記事にしたいと思います。
:::

以上Discodeの動きになるような**OverlappingPanels**パッケージの紹介でした。


何かご指摘や質問があればコメントでお気軽にご指摘等頂けると嬉しいです
この記事を読んで何かと参考になればと思います。
さくらのクラウドシェルでpipを使えるようにしよう！

前提として会員IDでログインした後、sudoコマンドを使用できるようにする必要があります。

    sakura@cloud-shell% curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py

    sakura@cloud-shell% sudo python3 get-pip.py

ログアウトしたらデータは全て削除されます。
因果推論とは何か？

統計学において、近年注目されている分野の1つに 「因果推論」 があります。 因果推論とは、データから 「因果関係」があるかどうかを推定する分析手法です。
 因果関係とは、 「原因とそれによって生じる結果との関係」のことです。
なぜ、このような分析手法が注目されているかというと、因果推論の結果が、 人間の意思決定に大きく関わってくる点にあると言えます。
ビジネスの現場における因果推論の役割を考えてみましょう。 業務の品質や効率を高める手法として、PDCA (Plan, Do、 Check、 Action) サイクルの枠組みが知られています。 これは、 何らかの業務改善に取り組むときに、 まずは施策を設計 (Plan) し、 次にその施策を実行 (Do) し、 そして施策の評価 (Check)を行い、その評価に基づいて改善案 (Action) を考え
る、という枠組みです。
因果推論は、 PDCAサイクルの中の 「評価」のプロセスで役立ちます。 例えば、 今月の売り上げを向上させるためにSNS広告施策を設計し、 実行したとします。 評価のプロセスでは、 「出稿した“SNS広告” (原因)によって、 “売り上げ” (結果) がどれぐらい向上したのか」を評価します。 SNS広告を出稿したあとに今月の売り上げが向上したら、 来月も同じ広告を出稿したいと思うはずです。

しかし、ここで考えてみてください。 今月の売り上げが向上したのは、本当にSNS広告が原因なのでしょうか?
もし､ SNS広告が原因でなければ、 来月も同じ広告を出稿するのは無駄なことかもしれません。 それを見極めるために、 「“SNS広告”が“売り上げ”に対してどれくらいの効果をもたらすのか」を推定する必要があります。 この推定の手法が因果推論です。
このように、適切な評価を行うことは、 適切な改善活動につながります。 これは広告施策に限りません。
例えば、 製薬会社の新薬の評価、行政機関の政策の評価など、 様々な評価プロセスで有効です。 つまり、何らかの施策を評価する場面で、 因果推論は非常に重要な役割を担っているのです。

日経ソフトウエア 2023年7月号
【特集】
Pythonで学ぶ統計　因果推論編
ブロックデバイス

lsblkコマンドで一覧表示
```
[linuxstudy@localhost ~]$ lsblk
NAME              MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda                 8:0    0   20G  0 disk 
├─sda1              8:1    0    1G  0 part /boot
└─sda2              8:2    0   19G  0 part 
  ├─centos-root   253:0    0 17.5G  0 lvm  /
  └─centos-swap   253:1    0  1.5G  0 lvm  [SWAP]
sdb                 8:16   0    8G  0 disk 
├─sdb1              8:17   0    1G  0 part /usbmemory2
├─sdb2              8:18   0    1K  0 part 
├─sdb5              8:21   0    1G  0 part 
├─sdb6              8:22   0    1G  0 part 
│ ├─datagroup-data1
                  253:2    0  612M  0 lvm  
│ └─datagroup-data3
                  253:4    0  400M  0 lvm  
└─sdb7              8:23   0    1G  0 part 
  └─datagroup-data2
                  253:3    0  428M  0 lvm  
sr0                11:0    1  9.5G  0 rom  /run/media/linuxstudy/CentOS 7 x86_64
[linuxstudy@localhost ~]$ ls /sda
ls: cannot access /sda: No such file or directory
[linuxstudy@localhost ~]$ ls /sda -d
ls: cannot access /sda: No such file or directory
[linuxstudy@localhost ~]$ ls /
bin   dev  home  lib64  mnt  proc  run   srv  tmp        usbmemory2  var
boot  etc  lib   media  opt  root  sbin  sys  usbmemory  usr
[linuxstudy@localhost ~]$ ls /dev/sda
/dev/sda
[linuxstudy@localhost ~]$ ls /dev/sda -l
brw-rw----. 1 root disk 8, 0 Jul  2 06:55 /dev/sda

```
マウス操作が苦手な人におすすめ！Vimiumを使って効率的にウェブ検索をしよう

# 1.はじめに
新生活が始まり、業務パソコンを使う人も増えているかもしれません。しかし、不慣れなトラックパッドの操作が苦手で、ウェブ操作が非効率な作業になっているかもしれません。そこで、Vimiumというブラウザ拡張機能が役立ちます。Vimiumは、キーボードショートカットを使用してウェブブラウザを操作することができるため、マウスやトラックパッドに頼らずにスムーズに操作できます。

この記事では、Vimiumのダウンロード方法から使い方まで詳しく紹介します。

# vimiumのダウンロード
Vimiumは、ウェブブラウザをキーボードショートカットで操作するためのブラウザ拡張機能です。この拡張機能は、ChromeとFirefoxの両方に対応しています。今回はchromeを例に説明します。

ダウンロードは以下のサイトから行うだけです！！

https://chrome.google.com/webstore/detail/vimium/dbepggeogbaibhgnhhndojpepiihcmeb


chromeを再起動して設定終了です。

# vimiumの使い方

## スクロール操作
| キー | 操作                           | 
| :--: | :----------------------------: | 
| j    | 1行分、下にスクロールする      | 
| k    | 1行分、上にスクロールする      | 
| d    | 半ページ分、下にスクロールする | 
| u    | 半ページ分、上にスクロールする | 
| G    | 一番下までスクロールする       | 
| gg   | 一番上までスクロールする       | 

## リンクを開く
| キー | 操作                                                                     | 
| :--: | :----------------------------------------------------------------------: | 
| f    | リンク箇所に、それを開くためのキー割り当てを表示する（現在のタブで開く） | 
| F    | リンク箇所に、それを開くためのキー割り当てを表示する（新規タブで開く）   | 

f もしくは F キーを押すと、以下のようにリンクが貼られている部分に、アルファベットが表示されます。

![スクリーンショット 2023-07-05 14.25.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648527/aeec4cb4-6003-f76e-076d-c6513b2893d2.png)
開きたいリンクに表示されたアルファベットのキーを押すと、そのリンクが開きます。例えば、「SA」と表示されているリンクであれば、sを押した後aと入力すればリンクを開くことができます。

## 履歴の操作
| キー | 操作                 | 
| :--: | :------------------: | 
| h    | 前のページに戻る     | 
| l    | 先のページに進む<br> | 

## その他
これまで説明したものは私がよく使うキーです。
「？」を入力することで他のショートカットも確認することができます！

![スクリーンショット 2023-07-05 14.29.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648527/f06a82ec-122c-87f0-13af-2b42ec440d2e.png)


# おわりに
この記事では、マウスやトラックパッドの操作が苦手な人向けにVimiumという便利なブラウザ拡張機能を紹介しました。Vimiumはキーボードショートカットを使用してウェブブラウザを操作することができるため、マウスやトラックパッドに頼らずにスムーズにウェブサイトを閲覧できます。
【Python】JSONの特定のキーだけ残して不要部分は削除する

# はじめに
JSONの不要な要素を削除するコードを書いたので記事にしておきます

# 実装
```python
import json

def filter_json(data):
    if isinstance(data, dict):
        filtered_data = {}
        for key, value in data.items():
            if key == "legacy":
                filtered_data[key] = value
            elif isinstance(value, (dict, list)):
                filtered_value = filter_json(value)
                if filtered_value:
                    filtered_data[key] = filtered_value
        return filtered_data
    elif isinstance(data, list):
        filtered_data = []
        for item in data:
            filtered_item = filter_json(item)
            if filtered_item:
                filtered_data.append(filtered_item)
        return filtered_data
    else:
        return None

# JSONファイルの読み込み
with open('input.json', 'r') as file:
    data = json.load(file)

# 不要なキーを削除
filtered_data = filter_json(data)

# 結果を新しいJSONファイルに書き込む
with open('output.json', 'w') as file:
    json.dump(filtered_data, file)
```

# おわり
これからも使いそうです
コード資産できてよかったです
手作業の負担を軽減！『Autofill』を使ったデータ入力の効率化

# 1.はじめに
業務時間入力などへのデータ入力時には、同様の入力作業の繰り返しに直面することがあります。これらの状況では、手作業を必要とする様々なタスクが発生し、疲れてしまいます。
今回は、そんな場面に役立つChromeの拡張機能『Autofill』を紹介します！

# 2.『Autofill』の導入方法
ご紹介するのは、『Autofill』という作業支援ツールです。これはChromeの拡張機能として利用することができる、便利なプラグインです。
![スクリーンショット 2023-07-05 11.51.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648527/7a97714a-9bb4-a3cb-f65e-69a769b87605.png)

『Autofill』をChromeに追加する方法は非常に簡単です。以下のリンクをクリックして、『Autofill』のページに移動します。そして、ページ上で表示される『Chromeに追加』ボタンをクリックするだけで、追加が完了します。

https://chrome.google.com/webstore/detail/autofill/nlmmgnhgdeffjkdckmikfpnddkbbfkkk?category=app/5-lifestyle&hl=ja

追加したら、タスクバーに固定しましょう。
タスクバーのピースアイコンを選択してください。かみなりマーク右側のがびょうマークを選択することで固定できます！
![スクリーンショット 2023-07-05 11.57.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648527/f79f5bfb-ec3b-28d6-65ae-474b0b9a5b1f.png)

chromeを再起動して設定完了です！


# 2.『Autofill』の使い方
このツールの主な使用方法は、WEBフォームの内容を「保存する」ことと「読み込む」ことです。それぞれの手順を順番に解説していきましょう。

## 2.1 保存する
Autofillを使ってフォームの内容を保存するためには、次の流れで作業を行います。
1.WEBページを開いてフォームに入力を行う
2.カミナリマークのアイコンを押してAutofillのメニューを開く
3.『Generate Autofill Rules』を押してフォームの内容を保存する

まずはWEBページを開き、内容を保存したいフォームに入力を行いましょう。本記事では情報処理推進機構のwebページを例に説明します。

https://form.qooker.jp/Q/ja/IPA/contact/

![スクリーンショット 2023-07-05 13.13.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648527/dd7da17d-f246-fb34-3083-2ec3faa996be.png)


フォーム内容の入力が終わったら、Chromeの右上にある『カミナリマーク』のアイコンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648527/fe9547ab-d39d-22ec-74a4-5e865a883acc.png)
[引用](https://miya-system-works.com/blog/detail/52)

アイコンを押すとアドレスバーの下あたりに『Autofill』のメニューが開きます。

ここで『③Generate Autofill Rules』を押すと、『Done』の表示とともにフォーム内容が保存されます。

![スクリーンショット 2023-07-05 13.18.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648527/d2ed5f6c-6172-ba76-a7e7-871aa6977cbf.png)
[引用](https://miya-system-works.com/blog/detail/52)

これで入力内容を保存することができました！

## 2.2 読み込む
次に、保存したフォームの内容を読み込んで再利用する方法について解説します。

保存したフォームの内容を自動的に反映させるために特別な操作は必要なく、保存したページを更新するだけです。つまり、フォームの内容を保存した後、次回以降にそのページを開く際には、自動的に保存された内容が読み込まれます!


# おわりに
作業時間入力やデータ入力の際には、同じ作業の繰り返しに疲れてしまうことがあります。しかし、Chromeの拡張機能『Autofill』を活用すれば、これらの作業を効率化することができます。フォームの内容を保存し、次回以降には自動的に読み込むことができるため、手作業の負担を軽減することができます。ぜひ『Autofill』を導入して、効率的な作業環境を実現してください。

# 参考

https://miya-system-works.com/blog/detail/52
【Rubocop】reversibleなmigrationではない。

rubocop のご指摘
```
C: Rails/ReversibleMigration: change_column_comment(without :from and :to) is not reversible.
```

#### 何をしたらご指摘を受けたのか

DBのカラムにコメントを追加したいと思い、以下のコマンドを用いてマイグレーションファイルにコメントを追加しました。

```
$ bundel exec rails g migration AddCommentsToTable
```

内容
```rb:200xxxxxxxxxx..._add_comments_to_table.rb
class Add CommentsToTable < .....
    def change 
        change_column_comment(:hoge, :fuga, 'hogehogefugafugaしたい')
    end
end
```

```
$ bundle exec rails db:migrate
```
```
確認
$ bundle exec rails db:migrate:status
```

```
up 200xxxxxxxxxx... Add comments to table
```

いけてる。

が、、、、、


実行したコマンド
```
$ rubocop -a
```
<br />
叱られた。

```
C: Rails/ReversibleMigration: change_column_comment(without :from and :to) is not reversible.
```
reversibleではないということは、rollbackしてもupのまま...?

```
$ bundle exec rails db:rollback
```
```
up 200xxxxxxxxxx... Add comments to table
```
upのままだ。

#### 原因
change_columnではrollbackできないということだった。(railsガイドより)


>change_columnコマンドは逆進できない（可逆的でない）点にご注意ください。

マイグレーションファイルで使用するchangeメソッドでは逆進の処理もやってくれるものだと思い込んでいました。

なので、今回マイグレーションはできたが、ロールバックのコマンドを打ってもUPのままだったみたいです。


#### 解決法
upメソッドかdownメソッドをchangeメソッドの代わりに用いる

```rb:200xxxxxxxxxx..._add_comments_to_table.rb
class Add CommentsToTable < .....
    def up 
        change_column_comment(:hoge, :fuga, 'hogehogefugafugaしたい')
    end
end
```

```
$ bundle exec rails db:rollback
```

```
down 200xxxxxxxxxx... Add comments to table
```

```
$rubocop -a
```

いけた。






<br />
<br /><br /><br /><br /><br /><br />
changeメソッドが使用できる定義についてこちらの記事にまとめられています。
https://prograshi.com/framework/rails/change-method-in-migration/


#### 参考
https://railsguides.jp/active_record_migrations.html

https://prograshi.com/framework/rails/change-method-in-migration/
PythonでPDFのテキスト毎の座標を取得する

# 結論
`pdfminer`を使用し、各テキストのレイアウトを取得することで座標が取得できます。

# 前提
記事を書いたときの`Python`と`pdfminer.six`のバージョンです
|名前|バージョン|
|-|-|
|Python|3.10.12|
|pdfminer.six|20221105|

# 準備
以下のサンプルコードを実行するために`pdfminer.six`をインストールしておきます。コンソールで下記のコマンドを実行します。
```
pip install pdfminer.six
```

# サンプルコード
```.py
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.layout import LAParams, LTTextContainer
from pdfminer.converter import PDFPageAggregator

def main():
    manager = PDFResourceManager()

    with open('sample.pdf', 'rb') as input:
        with PDFPageAggregator(manager, laparams=LAParams()) as device:
            # PDFPageInterpreterオブジェクトの取得
            iprtr = PDFPageInterpreter(manager, device)
            # ページごとで処理を実行
            for page in PDFPage.get_pages(input):
                iprtr.process_page(page)
                # ページ内の各テキストのレイアウト
                layouts = device.get_result()
                for layout in layouts:
                    # 罫線などのオブジェクトが含まれているとエラーになるのを防ぐため
                    if isinstance(layout, LTTextContainer):
                        # 各ページの左下を原点としている
                        # x0: テキストの左端のx座標
                        # x1: テキストの右端のx座標
                        # y0: テキストの下端のy座標
                        # y1: テキストの上端のy座標
                        # width: テキストの幅(x1 - x0)
                        # height: テキストの高さ(y1 - y0)
                        print(f'{layout.get_text().strip()}, x0={layout.x0:.2f}, x1={layout.x1:.2f}, y0={layout.y0:.2f}, y1={layout.y1:.2f}, width={layout.width:.2f}, height={layout.height:.2f}')

if __name__ == '__main__':
    main()
```

# 実行
サンプルコードを実行してみます。
用意した`sample.pdf`は以下の画像のようなPDFです。
<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3071987/ed56ebf4-2a7a-9061-fe26-37ecd1f105eb.png"/><img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3071987/67a8aebf-a874-047b-3b59-a5e8cb2a2ea9.png"/>

結果は下記となりました。
```.log
1 ページ目, x0=128.42, x1=303.47, y0=668.34, y1=704.34, width=175.05, height=36.00
サンプル 1, x0=311.47, x1=484.37, y0=569.46, y1=605.48, width=172.90, height=36.02
サンプル 2, x0=162.14, x1=507.95, y0=422.75, y1=494.78, width=345.81, height=72.02
サンプル 3, x0=66.14, x1=181.34, y0=329.19, y1=353.19, width=115.20, height=24.00
, x0=85.10, x1=90.38, y0=728.33, y1=738.89, width=5.28, height=10.56
2 ページ目, x0=104.42, x1=279.47, y0=663.90, y1=699.90, width=175.05, height=36.00
サンプル 4, x0=116.18, x1=346.63, y0=478.94, y1=526.94, width=230.45, height=48.00
サンプル 5, x0=341.47, x1=427.87, y0=292.35, y1=310.35, width=86.40, height=18.00
, x0=85.10, x1=90.38, y0=728.33, y1=738.89, width=5.28, height=10.56
```

確かに、テキストごとに座標が取れてきていることが分かります。
なお、4行目と8行目で空テキストの座標が取れてきています。これは、PDFをWordで作ったときに、いくつか含めてしまった改行が原因だと思います。

以上です。

# 参考

https://blog.imind.jp/entry/2019/10/18/025658
GitHub Actions で ChatGPT を使ってプルリクの要約とレビューをしてみる

# GitHub Actions で ChatGPT を使ってプルリクの要約とレビューをしてみる

## はじめに

まず、ChatGPT を使ったプルリクで実現したかったことは…

**レビューをしてもらいたくなるような仕組みを作りたかった**

からです。

プルリクを出して、レビューを受けるのはソフトウェアの開発では非常に良いことだと思いますが、レビュアーのレビュー内容によっては凹んでしまう内容もあると思います。

それを少しでも緩和するために、優しい言葉で AI に指摘された方がかなり楽です。
一人壁打ちもできます。

## Auto Review by OpenAI

自動レビューは GitHub Actions を利用します。

https://github.com/marketplace/actions/auto-review-by-openai

レビュー内容は以下のようにプルリクのコメントとして投稿されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19224/55baba0b-00e6-f6a8-19f9-5f6198242dbc.png)

## 実装方法

単純にレビューをするだけであれば、以下のようなワークフローを `.github/workflows` に配置するだけです。
ただし、このアクションは OpenAI の API を利用しますので、API キーの発行が必要になります。
事前に OpenAI の API キーを `secrets.OPENAI_API_KEY` に設定しておくだけです。
`target` にはカンマ区切りでレビュー対象のファイルの拡張子を書きます。
`language` にはレビューで投稿される言語をしていしますが、デフォルトでは英語になっているため、`日本語`を指定しています。

```yml
name: PR Review

on:
  pull_request:
    types: [ opened, synchronize ]

jobs:
  Build:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    permissions:
      contents: read
      pull-requests: write

    steps:
      - uses: actions/checkout@v3

      - uses: akiojin/auto-review-github-action@v0.2.1
        with:
          openai-api-key: ${{ secrets.OPENAI_API_KEY }}
          target: 'cs,yml'
          language: 日本語
```

## OpenAI の API キーの作成方法

[OpenAI platform](https://platform.openai.com/)

上記サイトの右上にある「Personal > View API keys」をクリックして、APIキーのリストを表示します。

![ss2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19224/967d98ac-fdf3-d585-46a0-95f8cef9ff10.png)

次に、「Create new secret key」を選択し、新しいAPIキーを作成します。

![ss1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19224/b57e9f1c-e833-a683-6be4-b00f63d02cf9.png)

「Name」に適当な名前を入力し、「Create secret key」を選択して鍵を作成します。

![ss3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19224/9a5249b2-9d7a-adb8-ea14-70b28da4ecb0.png)

作成したAPIキーをコピーします。

![ss4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19224/f09ee417-87ce-6027-437d-3a8c8dc129c0.png)

コピーしたAPIキーを`OPENAI_API_KEY`のようなわかりやすい名前でシークレット変数に設定します。

## さいごに

まだまだ、改善の余地はあると思いますが、目的である「**レビューをしてもらいたくなるような仕組みを作りたかった**」にはかなり近づけているのではないかと思います。
paizaスキルチェックでやらかした話

## paiza スキルチェックサービスでやらかした話

皆さんおなじみのスキルチェックで有名なpaizaというサービスをご存じですか？

私は就活の連絡が多く連絡数の整理のためにアカウントを削除し、別のメールアドレスの新しいアカウントを作成したのですが、新しいアカウントに来ていたスカウトに応募した際に、paizaの運営から連絡が入りました。

コード不正に引っかかっているとの連絡が、、、、。:disappointed_relieved:

不正の該当内容に関しては

- 問題及び提出コード、ヒントをブログやSNS等に掲載しない
- 回答やヒントを教えてもらったり他人のコードを利用しない
- 複数アカウントで事前に問題を見るなどの不正行為をしない

最後の複数アカウントで事前に問題を見るなどの行為をしない。知らず知らずの間にこちらの項目にヒットしてしまっていたのですね、、、。

あいにくpaizaの就活サービスが使用できなくなりました、、、。

でも、幸いなことに、paizaラーニングと自己満でのスキルチェックに関しては使えるとのことでした。故意ではないがためになかなかショックですね:disappointed_relieved:

paizaのスキルチェックはAtCoderと違って時間指定のコンテストとかではないからネットワークパトロール等で監視している感じですかね、、、。

他企業からのスカウトにも関連しているものなので、そのスキルランクの正当性が担保されていないといけないんですね。

コードスキルチェックに関してはしっかり規約等しっかり読んで取り組むことをお勧めします！

(paizaの運営から指摘があった際にはこの記事は削除します。)
Qiita API に Node.js の fetch() で認証つきのリクエストをする

# アクセストークンの発行

まず、認証のためのアクセストークン発行から実施します。公式ドキュメントの認証認可の項目どおりに進めていけば OK です

https://qiita.com/api/v2/docs#%E8%AA%8D%E8%A8%BC%E8%AA%8D%E5%8F%AF

以下のいずれかの方法で発行できます

- ユーザーの管理画面から発行
- OAuth を利用した認可フロー

今回は自分自身のアクセストークンだけ取得できれば十分なので、管理画面からの発行をやってみます。

まずは [ユーザーの管理画面](https://qiita.com/settings/applications) を開き、「新しくトークンを発行する」をクリックします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27471/e2616c0f-1b46-1361-30d0-50113a3edd55.png)

次は、アクセストークンの設定画面です

- 説明を埋める
    - アクセストークンの管理用に、あとから見てもわかりやすいように書いておきます
- スコープを決める
    - 今回は、読み込みだけできれば十分なので `read_qiita` だけにチェックを入れます
- 「発行する」をクリックする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27471/81261541-0c3c-5535-062f-4e5dbe97c205.png)

発行されたアクセストークンが一度だけ表示されるので、どこかに控えておきます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27471/65a50465-33ea-a293-e734-879acdd2f78b.png)

次回からはこのページを開いてもアクセストークンは表示されません。アクセストークンの削除ができるようになっています

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27471/a659e0b1-4a6b-9f8c-dd09-7a18bb81cab0.png)

# 公式 JavaScript ライブラリは古い

Qiita API を利用するための公式 JavaScript ライブラリ「[qiita-js](https://github.com/increments/qiita-js)」が用意されていますが、最終更新が2016年で TypeScript のバージョンも1系になっており（現在は5系）、残念ながらあまり使用が推奨されるような感じではありませんでした

https://github.com/increments/qiita-js

# Node.js の fetch() で認証つきのリクエストをする

今回はこのような形に落ち着きました。たとえば、`date`（= `2023-07-05` など）を指定して、その日 Qiita に投稿された記事一覧を取得する TypeScript のコードです。

```typescript
// アクセストークンは環境変数にセットして利用する
const accessToken = process.env.QIITA_API_ACCESS_TOKEN

// 指定した日に投稿された記事一覧を取得する関数
async function getItemsResponse(page: number, perPage: number, date: string) {
  // クエリパラメータの page, per_page, query を組み立てる
  const params = new URLSearchParams({ page: `${page}`, per_page: `${perPage}`, query: `created:>=${date} created:<=${date}` })
  // GET /api/v2/items にリクエストする
  const response = await fetch(
    `https://qiita.com/api/v2/items?${params}`,
    {
      // Authorization リクエストヘッダーに、Bearer の後にアクセストークンを付与する
      headers: {
        'Authorization': `Bearer ${accessToken}`,
      },
    }
  )
  return await response.json()
}
```

前回わかった「記事一覧 API の検索クエリでも記事の作成日時 `created` による絞り込みが使える」ということを利用しています。

https://qiita.com/yonex/items/d3ade7c1ae9897536b28

この部分がそれです

```js
query: `created:>=${date} created:<=${date}`
```

また、上記コード中で `response.headers` を `console.log()` に入れて確認してみると、認証なしだと1時間あたり60回までのレートリミットだったところ、認証が通ってレートリミットが1,000回まで拡大されていることがわかります。

```js
    'rate-limit' => { name: 'rate-limit', value: '1000' },
    'rate-remaining' => { name: 'rate-remaining', value: '929' },
    'rate-reset' => { name: 'rate-reset', value: '1688553383' },
```

無事に認証つきのリクエストをすることができました。次は、ここまでの機能を使って何かブラウザで確認できるようにしたいと思います :tada:
pathの初期化

```
[linuxstudy@localhost ~]$ su - linuxstudy       //ログインシェルを使用してユーザ変更
Password: 
Last login: Wed Jul  5 06:13:52 EDT 2023 on pts/0
this is /etc/profile                        //1   共通設定
/usr/local/bin:/bin:/usr/bin
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin
this is bash_profile                        //2   linuxstudy用
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin
this is bashrc                              //3   linuxstudy用
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin
this is /etc/bashrc                         //4   bashrcから呼び出される
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/linuxstudy/.local/bin:/home/linuxstudy/bin

```
pathの初期化について、参考にした資料
https://qiita.com/tomo0/items/674e06da84c921f5407a
一部呼ばれていないものがある。環境によって違う？
```
1, /etc/profile
2, /etc/profile/profile.d   /etc/profile.dならある     
3, ~/.bash_profile
4, ~/.bash_login            //自分の環境には見当たらない
5, ~/.profile               //自分の環境には見当たらない
6, ~/.bashrc
7, /etc/bashrc
```
正規表現

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/b49d39ed-92a5-8cf5-8278-7f0f21b3f66d.png)
１：使われる正規表現の書き方
・全角ひらがなのみ
^[ぁ-んー]*$

・全角ひらがなのみ(半角・全角スペースも許容)
^[ぁ-んー\s　]*$

・全角カタカナのみ
^[ァ-ンヴー]*$

・全角カタカナのみ(半角・全角スペースも許容)
^[ァ-ンヴー\s　]*$

・8桁の半角英数のみ
^[a-zA-Z0-9]{8}$
※ {8} の数値を変更することで桁数を変更することができます。

・12桁の半角英数記号のみ
^[\x20-\x7F]{12}$
※ {12} の数値を変更することで桁数を変更することができます。

・日本語（漢字、ひらがな、全角カタカナ）
^[ぁ-んァ-ヶー\u30a0-\u30ff\u3040-\u309f\u3005-\u3006\u30e0-\u9fcf]*$

・日本語（漢字、ひらがな、全角カタカナ）と半角スペース、全角スペースのみ
^[ぁ-んァ-ヶー\u30a0-\u30ff\u3040-\u309f\u3005-\u3006\u30e0-\u9fcf\s　]*$

・半角英数字と半角ハイフン、半角スペースのみ
^[0-9a-zA-Z\-\s]*$

・半角スペース、改行、タブ文字などの空白文字と全角スペースが含まれない文字列
[^\s|　]+

・IPアドレス(IPv4)形式
^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$

・URL形式
^http(s?)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*$
※ テキストフィールドは、128byteのため、テキストエリアを選択してください。

・郵便番号形式（日本国内）半角数字ハイフンあり
^\d{3}-\d{4}$

・郵便番号形式（日本国内）半角数字ハイフンなし
^\d{7}$

・郵便番号形式（日本国内）全角数字ハイフンなし
^[０-９]{7}$
cpp readfile

https://github.com/aizwellenstan/cpp_readfile
```c++
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>

std::string getWordByIndex(const std::string& sentence, int index) {
    std::istringstream iss(sentence);
    std::string word;
    for (int i = 0; i <= index; ++i) {
        if (!(iss >> word)) {
            return "";
        }
    }
    return word;
}

std::vector<std::string> readFile(const std::string& filePath) {
    std::vector<std::string> res;

    std::ifstream file(filePath);
    std::string keyword = "createNode ";
    std::string keyword2 = "xgm";
    
    if (file.is_open()) {
        std::string line;
        while (std::getline(file, line, ';')) {
            if (line.find(keyword) != std::string::npos && line.find(keyword2) != std::string::npos) {
                std::cout << line << std::endl;
                line = getWordByIndex(line, 1);
                if (std::find(res.begin(), res.end(), line) != res.end()) {
                    continue;
                }
                std::cout << line << std::endl;
                res.push_back(line);
            }
        }
        file.close();
    }
    return res;
}

int main() {
    std::string filePath = "D:/Test.ma";
    std::vector<std::string> res = readFile(filePath);
    for (const std::string& i : res) {
        std::cout << i << std::endl;
    }
    return 0;
}
```
AWS認定デベロッパーアソシエイト(DVA-C02) 合格体験記

# はじめに
将来的にAWSの6冠を達成したい、SAA取得後、若干期間が空いたということもあり、比較的難しいと言われているSOAの前にSAAよりは簡単と言われているDVAの取得をしようということで受験しました。

# スコア
**849/1000**

無事合格できました！
SAA受験時は時間いっぱいまで見直しやらフラグを立てた問題を悩んでたりしたのですが、今回は比較的見直しフラグの数も少なく、テストの実施と見直しで1時間弱くらいで終了しました。
SAAに比べてDVAの方が簡単？や問題の傾向もあると思いますが、前より各サービスの知見が若干深まり、ハンズオンをしたことでイメージしやすくなったのかなと考えています。

# 受験対策

### 書籍
- 徹底攻略 AWS認定デベロッパーアソシエイト[DVA-C01] 教科書

試験のバージョンは[DVA-C0**2**]なので、バージョンが古い。。。
バージョンが違うからと言って全く使わなかったわけではなく、
Code系などのあまり触れていないサービスなどについては主にこちらの書籍で勉強したり、調べたりもしてました。
最終的に、問題集実施前に一通りの内容を読み込みました。

### Web問題集
https://kws-cloud-tech.com/

- 資格会員コース

10問単位で実施できるので、4～5周くらい実施しました。
空いた時間を活用し、ちまちまとやってました。
間違えた問題の解説はよく読んで、それでもわからなければググるを繰り返しました。
解説に画像がある問題もあり、どうしてそれが正解なのかのイメージが湧きやすかったと思います。

### 公式模擬試験

以前は有料でしたが、無料になり、解説もありました。
問題文の長さやレベル感を把握するため必ず実施したほうが良いです。
テストの直前でもよいですが、Web問題集を一通りこなした後にやった方が良いと思います。
私は直前にやって、何問か間違え、かなり焦ったので。。。

# 受験方法
オンライン受験だと色々トラブルがあったりする可能性があるのと自宅だと集中できないような気がして、試験を受けるときは必ずテストセンターで受けるようにしています。
オンラインで受ける方が移動時間がなく、楽なのは理解していますが、色々トラブルの話を聞くと実はテストセンターの方が悩みが少なく試験自体に集中できるのでは？というのが個人的な意見です。

https://qiita.com/Syoji_Yonemoto/items/fdc53bd5c38b17a57299

- 受験トラブル

テストセンターは、平日の昼間に受験したからか結構空いていました。
土日に受験するならば早めの予約をした方がよいと思います。

# 抑えておくサービスなど
やはり開発系の試験なので、「Lambda」やら「APIGateWay」、「Code～」系のサービスについてはよく勉強しておいた方がよいと思います。
「Lambda」、「APIGateWay」については自分で作成してみると理解しやすいと思います。
「Lambda」の設定については実務で多少慣れていたこともあり、役に立ちました。

# おわりに
正直、そこそこ点が取れた？ことより落ちなかったことに対する安心感の方が大きかったです。
以前までは受験後そのまま画面に合格・不合格が出ていた思うのですが、出なくなりもやもやしながら帰宅しました。
結果はその日の夕方くらいに出ていたので、そこでようやく肩の荷が降りたというか安心することができました。
今回はちょっと時間をかけすぎたところもあるので、次に挑戦予定であるSOAは1ヶ月くらいで取得できるようにまた勉強していきたいと考えています。
Djangoで既存テーブルにmysqlのパーティショニングをする

# パーティショニングについての概要
https://dev.mysql.com/doc/refman/8.0/ja/partitioning-overview.html

# 今回やったこと 
既存であったアクセスログのテーブルをアクセス日時の月ごとにパーテーションする

# 手順
## 1. ダンプを取る
念の為ダンプは取っておきましょう
## 2. 外部キー制約を外す
外部キー制約があるとパーティショニングできません
したがって、外部キー制約がある場合は全て外します
Djangoのマイグレーションでは外部キー制約を外すことはできません（多分）
↓こんな感じでINTで定義し直したらいけそうって思うんですが
```python
# customer = models.ForeignKey(Customers, on_delete=models.CASCADE)
customer_id = models.IntegerField(null=True)
```
これだと既存カラムが削除されて新たにcustomer_idのカラムができてしまい、データが失われてしまいました

なので、`SHOW CREATE TABLE {テーブル名}`で外部キーを確認して直接クエリを流してDROPします
```sql
ALTER TABLE api_access_logs DROP FOREIGN KEY {外部キー};
```
## 3. 主キーをidとアクセス日時の複合主キーにする
パーテーションのキーとなるカラムは必ず主キーである必要があります
なので、主キーをidとアクセス日時の複合主キーに変更します
これもまたDjangoのマイグレーションではできないので直接クエリを流す必要があります
複合主キーに変更するには以下のステップで実行する必要があります
```sql
-- idからオートインクリメントを外す（主キー以外にオートインクリメントが設定されているとエラーになる）
ALTER TABLE api_access_logs CHANGE id id INT NOT NULL;
-- 主キーを削除
ALTER TABLE api_access_logs DROP PRIMARY KEY;
-- 複合主キーを作成
ALTER TABLE api_access_logs ADD PRIMARY KEY (id, access_datetime);
-- idにオートインクリメントを再設定
ALTER TABLE api_access_logs CHANGE id id INT AUTO_INCREMENT;
```
## 4. パーテーションの設定
これでようやく本題のパーテーションを設定できます
当然Djangoのマイグレーションではできません
こんな感じで月毎に設定しました
```sql
ALTER TABLE api_access_logs PARTITION BY RANGE COLUMNS(access_datetime) (
    PARTITION p202303 VALUES LESS THAN ('2023-04-01 15:00:00'),
    PARTITION p202304 VALUES LESS THAN ('2023-05-01 15:00:00'),
    PARTITION p202305 VALUES LESS THAN ('2023-06-01 15:00:00'),
    PARTITION p202306 VALUES LESS THAN ('2023-07-01 15:00:00'),
    PARTITION p202307 VALUES LESS THAN ('2023-08-01 15:00:00'),
    PARTITION p202308 VALUES LESS THAN ('2023-09-01 15:00:00'),
    PARTITION p202309 VALUES LESS THAN ('2023-10-01 15:00:00'),
    PARTITION p202310 VALUES LESS THAN ('2023-11-01 15:00:00'),
    PARTITION p202311 VALUES LESS THAN ('2023-12-01 15:00:00'),
    PARTITION p202312 VALUES LESS THAN ('2024-01-01 15:00:00'),
    -- 以下省略
);
```
タイムゾーンがUTCなので`15:00:00`にしています
パーテーションは自動追加されないので、あらかじめ大量に作っておいた方が良いです
ちなみに、MySQL5.7よりも前のバージョンではdatetimeやdateはパーテーションのキーにはできないようで、こんな感じにするみたいです
```sql
PARTITION BY RANGE (YEAR(created_at) * 100 + MONTH(created_at))
```
## 5. パーティショニングされているか確認
パーテーションのキーのカラムを含めたクエリをEXPLAINで確認します
```sql
EXPLAIN SELECT * FROM api_access_logs WHERE access_datetime BETWEEN '2023-06-10 00:00:00' and '2023-06-20 00:00:00';
```
partitionsにその月のパーテーションだけが入っていればOK
![スクリーンショット 2023-07-05 18.37.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322555/e97a7184-58f3-26f6-a9ba-1f49ef9d9806.png)
また、以下でパーテーションそれぞれの件数がわかります
```sql
SELECT TABLE_SCHEMA, PARTITION_NAME, PARTITION_ORDINAL_POSITION, TABLE_ROWS FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME =  'api_access_logs';
```

# 終わりに
既存テーブルにパーティショニングするのはかなり面倒臭いことを実感できました
ただ、基本的にはコードの修正することなく対応できたのは意外でした
また、想定はしてましたがレコードが多いとかなり時間がかかりました
今回1400万レコードほどありましたが、「**3. パーテーションの設定**」は30分以上かかりましたし
そもそも「**2. 主キーをidとアクセス日時の複合主キーにする**」をやるだけでも2時間以上かかってました
レコード数が多くなりそうなテーブルは最初からパーティショニングをしておきましょう
DBeaverのGUIからデータをエクスポート&インポートする

dumpしろよって感じですが、ローカルにデータなかった時等、簡易的にそれが最速で完了する場合のやり方をまとめておきます。

## 1. SQL形式でエクスポート

テーブルを開いたら下部のエクスポートボタンをクリック
![スクリーンショット 2023-07-05 17.58.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/0824b352-d7f5-4bb4-9093-b2596f560468.png)

SQL INSERTステートメントへのエクスポートを選択
![スクリーンショット 2023-07-05 18.01.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/32979012-c4fe-7946-c4b0-2c330d52bd71.png)

特に指定なければフォーマットなどはそのままに次へ
![スクリーンショット 2023-07-05 18.02.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/4323920f-acfc-dc69-89b8-180d1d8420ac.png)

![スクリーンショット 2023-07-05 18.02.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/c3e34807-0e82-c13f-c601-3b52798ab643.png)

ファイルの保存先と名前はわかりやすいところに指定しときましょう
![スクリーンショット 2023-07-05 18.02.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/0f82c4cc-305c-0daa-54c7-fe9d6289bed5.png)

続行すればエクスポートできます。
![スクリーンショット 2023-07-05 18.03.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/e83eb111-3864-fe09-bce7-83d8c12f74ae.png)

## 2. ファイル確認
INSERT文の形式でエクスポートができたことが確認できると思います。SQLをコピーしておきましょう。
![スクリーンショット 2023-07-05 18.05.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/9e1819dc-a7ae-c55d-5e80-d44022184835.png)

## 3. INSERT文実行
SQLエディタからコピーしたINSERT文を実行。ローカルだったらどんなに壊れようがどうでもいいですが、他のエンジニアも参照するような環境であればちゃんとトランザクションかけましょう。こういうのは慎重に...

## まとめ
あくまでも簡易的にやる時だけ。本番環境で安易にやるのはやめましょうね

## 最後に運動通信社について
運動通信社は「日本を世界が憧れるスポーツ大国にする」というビジョンを達成するべく、一緒に働く仲間を募集しています！
PMやアプリエンジニア、Webエンジニアなど色んな職種を募集しておりカジュアル面談大歓迎なので是非採用フォームよりご連絡ください！
ぜひ一緒に、自分たちも世の中もワクワクするサービスを作りましょう！

https://sportsbull.jp/about/#recruit

Greenも活用中です！

https://www.green-japan.com/company/9598
Pythonのsubprocessを使用してシェルコマンドを使いたいので下調べ

## はじめに
pythonからコマンドを実行したいなと調べたところ、subprocessモジュールというのを見つけた。これは、Pythonから外部プロセスを起動し、そのプロセスとのやり取りを行うことができるモジュールらしいので使い方を調べた。

## 基本の使い方
基本はsubprocess.run()の引数に配列で実行したいコマンドを渡すことが多そう
```python
import subprocess, sys

completedProcess = subprocess.run(['ls', '-la'])

print(res.returncode) #=> 終了コード
print(res.stdout)  #=> 標準出力
print(res.stderr) #=> 標準エラー出力

if completedProcess.returncode != 0:
    print('ls failed.', file=sys.stderr)
    sys.exit(1)
```
subprocess.run()の戻り値はCompletedProcess。以下CompletedProcessから取得できるもの
|プロパティ|説明|
|----|----|
|args|プロセスを起動するときに使用された引数|
|returncode|子プロセスの終了コード。一般に、終了ステータス 0 はプロセスが正常に終了|
|stdout|子プロセスから補足された標準出力です。バイト列、もしくは run() でエンコーディングが指定された場合、エラーの場合、text=True が指定された場合は文字列|
|stderr|子プロセスから補足された標準出力です。バイト列、もしくは run() でエンコーディングが指定された場合、エラーの場合、text=True が指定された場合は文字列|
|check_returncode()|returncode が非ゼロの場合、CalledProcessError が送出される|

### shell=True指定
*や|を使用した場合、shell=Trueをつけて文字列で渡すと実行できる
```python
cmd_str = "ps aux | grep python3"
subprocess.run(cmd_str, shell=True)
```
これは外部からの入力を直接コマンドに組み込むことができるということで、意図しないコマンドの実行やシステムへの悪意あるアクセスが可能にな流ということでもある。
使用時に入力を適切にエスケープまたは検証せずに使用すると、セキュリティ上の脆弱性が発生する可能性があるので注意して使えということらしい。

subprocess.run()はコマンド完了まで待つが、subprocess.Popen()を使用するとバックグラウンド実行などが可能になるっぽい。今回はsubprocess.run()で十分そうなので、必要ならまた調べる

## おわりに
シェルコマンドを使えるということは、`rm`で自分のディレクトリ消すなんてこともできるわけで、セキュリティには気を使わないと大惨事になる予感はしつつ、強力な機能でもあるので気をつけて活用した

## 参考
https://docs.python.org/ja/3/library/subprocess.html
Diff2Htmlでスクリプトタグが読み込まれないときの対処法

## 概要
difff2htmlを利用してスクリプトタグ（`<script></script>`）を読み込ませようとするとバグが発生して読み込まれない。

## 解決策
スクリプトタグの閉じタグが悪さをしているようなのでのスラッシュ（`/`）をバックスラッシュ(`\`)で打ち消す。
```
<script><\/script>
```
## 参照
https://github.com/kpdecker/jsdiff#api

https://github.com/rtfpessoa/diff2html#how-to-use
zotero: Purge Storage in My Library

zoteroの環境をセットアップし直した。

１．まず、添付ファイル保存用にはinfiniCLOUDのWebdabを設定した。20GBまで無料とのこと。zoteroにWebdab外部サーバーのセットができる。

２．困ったのはzoteroサーバーのpdfファイルを消す操作。で、見てると、zoteroのsettingのstorageに
##### Purge Storage in My Library
というボタンがある。説明は Use this to free up space for group libraries you own after disabling file syncing for My Library or switching to WebDAV storage.
文章が分かりづらいが試してみた。
ボタンを押すとMy Library内のpdfは消えた。その一方でGroup libraryのpdfはzoteroサーバーでキープしてくれてる。無料の300MBに収まった。

３．AndroidはSamsumg S8+にZoo for zotero, xodoを入れてる。Webdabもシンクロできた。ディスプレイ大きいし多くの論文はタブレットでも読めるな。

というわけでzotero悪くないです。
Rails map,flatten,pluckなどなど…

# はじめに
- 調べたり試行錯誤したりして実装したメソッドについて、自身の定着のためにまとめました
- 学習内容の備忘録です

# 目次
1. コミュニティIDの取得について(コードの【A】参照)
1. UserモデルとCommunityモデルのJoin操作について(コードの【B】参照)
1. コミュニティに所属するユーザーの投稿を取得することについて(コードの【C】参照)

以下の記述について、自分なりに苦労したため、まとめてみます。
```ruby:post.html.erb
#省略

def index
    @post = Post.new
    if current_user.communities.present?

      #【A】
      community_ids = current_user.communities
                                  .map(&:subtree_ids)
                                  .flatten

      #【B】
      community_user_ids = User.joins(:communities)
                               .where(communities: { id: community_ids })
                               .pluck(:id)

      #【C】
      @posts = Post.includes(:user)
                   .where(users: { is_deleted: false, id: community_user_ids })
                   .order(created_at: :desc)
                   .page(params[:page]).per(9)
    else
      
      #省略

    end
  end

#省略
```

# 1.コミュニティIDの取得について
【A】
```ruby
community_ids = current_user.communities
                            .map(&:subtree_ids)
                            .flatten
```
①map(&:subtree_ids)は、current_user.communitiesの各コミュニティに対してsubtree_idsメソッドを呼び出し、子孫コミュニティのIDの配列を取得
```ruby
.map(&:subtree_ids)
```
>補足
mapメソッドは、配列の各要素に対して指定されたブロックまたはメソッドを実行し、結果の配列を返す

>補足2
subtree_idsとは、「gem ancestry」のインスタンスメソッドで、返り値は、「自分のレコードと子レコード以下全てのレコードのidを取得」する。

②flattenメソッドは、多次元配列をフラットな一次元配列に変換
```
.flatten
```

# 2.UserモデルとCommunityモデルのJoin操作について
【B】
```ruby
community_user_ids = User.joins(:communities)
                         .where(communities: { id: community_ids })
                         .pluck(:id)
```
①joins(:communities)は、UserモデルとCommunityモデルを関連付けて結合
```ruby
User.joins(:communities)
```

②where(communities: { id: community_ids })は、結合したデータの中から指定されたコミュニティIDに該当するものを探してくる
```ruby
.where(communities: { id: community_ids })
```

③pluck(:id)は、結果の中からユーザーIDの配列を取得
```ruby
.pluck(:id)
```

# 3.コミュニティに所属するユーザーの投稿を取得することについて
【C】
```ruby
@posts = Post.includes(:user)
             .where(users: { is_deleted: false, id: community_user_ids })
             .order(created_at: :desc)
             .page(params[:page]).per(9)
```
①includes(:user)は、関連するユーザーレコードを事前に読み込んでN+1問題を回避
```ruby
Post.includes(:user)
```
>補足
N+1問題とは、必要なデータを取得するために必要以上のクエリを実行することで、効率性が低下する問題のこと。ここでは、Post.includes(:user)により、投稿と関連するユーザーのデータが一度のクエリで取得できている。

②where(users: { is_deleted: false, id: community_user_ids })は、ユーザーが削除されておらず、かつ指定されたユーザーIDに該当する投稿を選択
```ruby
.where(users: { is_deleted: false, id: community_user_ids })
```
③order(created_at: :desc)は、作成日時の降順で結果を並び替え
```ruby
.order(created_at: :desc)
```
④page(params[:page]).per(9)は、ページネーションを適用し、指定されたページの投稿を9つずつ取得
```ruby
.page(params[:page]).per(9)
```

# まとめ
- メモを見ながら何とか言語化できました
- 「説明してよ」と言われて、何も見ずに説明できるように、さらに理解を深めていきます

⚠️学習4ヶ月目の初学者による投稿です。
⚠️間違いがあるかもしれません。ご容赦ください。
⚠️ご指導、ご教授いただけると幸いです。