OmniAICreator/Qiita-1.07M huggingface dataset:
Azure Blob Storageに画像を保存してパブリックアクセスに

AzureBlobStorageへ画像をアップロードして保存。
その際にアップロードしたBlobのURLをパブリックで公開するときのメモ。

AzureBlobStorageへ画像を保存してそのURLはパブリックで〜ってどうやるの？
となり…調べた結果、このやり方でいけました。（ふう。すっきり）
誤字脱字、間違い等は個人の備忘録程度なので何卒ご容赦ください…。
前提としてAzureStorageアカウントは作成済みとして記載してます。
また、フロント側はReactを使用しているのですがそちらの記載も割愛。
（こんどしっかり纏める！）

## ライブラリのインストールから接続文字列の設定

```
dotnet add package Azure.Storage.Blobs
```
まずライブラリパッケージをインストール。
忘れずにファイルの先頭に追加。
```C#:TestController.cs
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Azure.Storage.Blobs.Specialized;
```
ストレージアカウントの接続文字をappsettings.jsonに記載します。
```appsettings.json
{
  "Logging": {
      "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
      }
    },
"AllowedHosts": "*",
"ConnectionStrings": {
  "StorageAccount": "ストレージアカウントの接続文字列"
  }
}
```
## Contollerの作成
```TestController.cs
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Azure.Storage.Blobs.Specialized;
//設定ファイルを読み込むために下記2つのパッケージを使用。
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;

namespace Testsite.Controllers;

[ApiController]
[Route("api/[controller]")]
public class TestController : ControllerBase
{
  // api/Test POST
  [HttpPost]
  public async Task<IActionResult> testPost(IFormFile image)
  {
    // 設定ファイルを読み込む
    var builder = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false);

    var configuration = builder.Build();

    // 設定ファイルからストレージアカウントの接続文字列を取得
    var connectionString = configuration.GetSection("ConnectionStrings").GetValue<string>("StorageAccount");

    // Blobの名前(Guidメソッドでランダムな文字列を生成) 
    var blobName = "images/" + Guid.NewGuid().ToString();

    try
    {
      var container = new BlobContainerClient(connectionString, "upload-file");

      // コンテナが作成されていなければ作成
      var createResponse = await container.CreateIfNotExistsAsync();

      // BLOB へのアクセス許可 PublicAccsesTypeを変更!
      await container.SetAccessPolicyAsync(PublicAccessType.Blob);
      
      var blob = container.GetBlobClient(blobName);

      // ファイルをBLOBにアップロードする。コンテントタイプはアップロードするファイルに合わせる。
      await blob.UploadAsync(image.OpenReadStream(), new BlobHttpHeaders { ContentType = image.ContentType });
      // アップロードしたファイルのURLを出力
      Console.WriteLine(blob.Uri.ToString());
      return Ok("ファイルアップロード成功");
    }
    catch (System.Exception)
    {
      throw;
    }
  }
}

```

コンテナのアクセスタイプを変更するには```SetAccessPolicyAsync(PublicAccessType.Blob)```このメソッド
指定したコンテナのアクセスタイプを指定できる。
```Blob```はblobに対するパブリックな読み取りアクセスを指定できるのだそう。
これでアップロードした画像を即座にパブリックアクセスにすることができました〜
またひとつ勉強になりました！

### 参考
https://learn.microsoft.com/en-us/dotnet/api/azure.storage.blobs.blobcontainerclient.setaccesspolicyasync?view=azure-dotnet

https://learn.microsoft.com/ja-jp/azure/storage/blobs/storage-quickstart-blobs-dotnet?tabs=visual-studio%2Cmanaged-identity%2Croles-azure-portal%2Csign-in-azure-cli%2Cidentity-visual-studio
【Firebase】`Package subpath './lib/auth/auth' is not defined by "exports" in...`というエラーが発生した場合の対処法

# Firebase Functionsのエミュレーターにてエラーが発生

Firebase FunctionsのエミュレーターにReact Native側から接続して関数を呼び出そうとした時に、`not-found`というエラーが出力されていました。原因を探ろうと**firebase-debug.log**を確認してみると、次のようなログが記録されていました。

```log
[error] ⬢  functions: Failed to load function definition from source: FirebaseError: Error occurred while parsing your function triggers.

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './lib/auth/auth' is not defined by "exports" in /Users/***/**/node_modules/firebase-admin/package.json
    at new NodeError (node:internal/errors:371:5)
    at throwExportsNotFound (node:internal/modules/esm/resolve:440:9)
    at packageExportsResolve (node:internal/modules/esm/resolve:692:3)
    at resolveExports (node:internal/modules/cjs/loader:482:36)
    at Function.Module._findPath (node:internal/modules/cjs/loader:522:31)
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:919:27)
    at Function.Module._load (node:internal/modules/cjs/loader:778:27)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object.<anonymous> (/Users/**/*.js:16:16) {"metadata":{"emulator":{"name":"functions"},"message":"Failed to load function definition from source: FirebaseError: Error occurred while parsing your function triggers.\n\nError [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './lib/auth/auth' is not defined by \"exports\"... 
```

# エラーの原因

ログを確認して調べてみると、原因は次のインポートらしいことが判明しました。

```typescript
import { Auth } from 'firebase-admin/lib/auth/auth';
```

# 問題の解決方法

上記の部分を次の内容に取り替えてみました。

```typescript
import type { auth } from 'firebase-admin';
```

そして、`auth.Auth`を使って`Auth`クラスを参照するように修正しました。

```typescript
private auth: auth.Auth | undefined = undefined;

constructor(@inject('Auth') auth: auth.Auth) {
    this.auth = auth;
}
```

そして、ビルドされたファイルが格納される**lib**ディレクトリを削除した後に、再ビルドを行いました。

```shell
yarn build && GOOGLE_APPLICATION_CREDENTIALS='./secrets/google-services.json' firebase emulators:start
```

結果、この問題については解決することができました。（この直後、これとは関係のないエラーで悩まされることにはなりましたが・・・）

# 参考にした記事

https://stackoverflow.com/questions/72661065/err-package-path-not-exported-with-firebase-admin-11-0-0-and-firebase-functions
paizaラーニング「ある数以上以下の要素の列挙 3 Python3編」

https://paiza.jp/works/mondai/array_primer/array_primer__array_ave_step3
#### 私の解答
```Python3
n = int(input())
a = list(map(int, input().split()))
total = sum(a)
k = total/n
for ele in a:
    if ele >= k:
        print(ele)
```
#### 解答例1
```Python3
N = int(input())

avg = 0
A = [0] * N
values = input().split()
for i in range(N):
    A[i] = int(values[i])
    avg += A[i]

avg /= N
for a in A:
    if a >= avg:
        print(a)
```
#### 解答例2
```Python3
N = int(input())
A = [int(x) for x in input().split()]

avg = sum(A) / N
for a in A:
    if a >= avg:
        print(a)
```
paizaラーニング「ある数以上以下の要素の列挙 2 Python3編」

https://paiza.jp/works/mondai/array_primer/array_primer__array_ave_step2
#### 私の解答
```Python3
n, k = map(int,input().split())
a = list(map(int, input().split()))
for ele in a:
    if ele >= k:
        print(ele)
```
#### 解答例
```Python3
values = input().split()
N = int(values[0])
K = int(values[1])

A = [0] * N
values = input().split()
for i in range(N):
    A[i] = int(values[i])

for a in A:
    if a >= K:
        print(a)
```
paizaラーニング「ある数以上以下の要素の列挙 1 Python3編」

https://paiza.jp/works/mondai/array_primer/array_primer__array_ave_step1
#### 私の解答
```Python3
n = int(input())
a = list(map(int, input().split()))
for ele in a:
    if ele >= 5:
        print(ele)
```
#### 解答例
```Python3
N = int(input())

A = [0] * N
values = input().split()
for i in range(N):
    A[i] = int(values[i])

for a in A:
    if a >= 5:
        print(a)
```
paizaラーニング「配列の最大最小 Python3編」

https://paiza.jp/works/mondai/array_primer/array_primer__array_minmax
#### 私の解答
```Python3
n = int(input())
a = list(map(int, input().split()))
print(max(a), min(a))
```
#### 解答例1
```Python3
N = int(input())

A = [0] * N
values = input().split()
for i in range(N):
    A[i] = int(values[i])

sorted_A = sorted(A)
print(sorted_A[N - 1], sorted_A[0])

```
#### 解答例2
```Python3
N = int(input())
A = [int(x) for x in input().split()]

print(max(A), min(A))

```
AWS SAA 合格記録

この度AWS SAAに合格したので記録を残そうと考えました。これから受ける方の参考になると幸いです。

### 前提・勉強時期
   SAA受験段階で基本情報・AWS CLF・LPIC 1を取得しています。
    実務経験としては開発・システム運用合わせて2年強です。
    AWSは実務未経験です。
    SAAの勉強は約1か月半ほどしました

### 勉強内容・使用教材

- 【SAA-C03版】これだけでOK！ AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座
初めはハンズオンもすべて実施していましたが、あまりにも進まなかったため最後の模擬試験2つだけ実施しました。所々模擬試験で不明だったところを聞き返すなどという利用の仕方をしました。

- 【SAA-C03版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集（6回分390問）
最初の3題だけ取り組みました。

これらの講座計5つの模擬試験を3周しました。
1週目はすべて正答率50～60%くらいでしたが、答え以外の解答についてもしっかり吟味しました。解答は60分ほどで終わりますが、1週目の復習は2hほどかかっていました。

- AWS認定ソリューションアーキテクト-アソシエイト問題集
Udemy以外にこちらの参考書も頭から読んで問題も解いていましたが、分野ごとに丁寧に解説されており有意義だったように感じます


### テスト所感・今後
Udemyの模擬試験同様60分強で解答が終わりました。CloudFrontについてよく聞かれた印象です。また、Udemyほど深くは問われなかったように感じました。
今後は年内はゆっくり休んで、DVAかSCSかLPIC2あたりに挑もうと思います。

(初のQiita投稿ですが、記事書くのに苦労しました…（笑）頑張ります…)
Pythonを改めてちゃんと学ぶメモ #2

# はじめに

ここは、Pythonを改めて学んでいるメモを都度書き出していきます。
※ 筆者のバックグラウンドは#1をご覧ください。

前：https://qiita.com/hideh_hash_845/items/58241f0ce0283e457a87
次：https://qiita.com/hideh_hash_845/items/b5e2264fc650c3e9d4b6

# 文法の気づき2
## リスト
- indexは0オリジンで、マイナス指定も可（Perlと同じ）
- リストの長さは`len()`
- 末尾に追加する時は`append()`
- 要素削除時、index指定なら`del()`, 内容指定時は`remove()`
- スライスができるが、~~後のindexは「最後の要素」ではなく「最後の要素の一個前」~~ __indexは「○○以上、◆◆未満」で指定するものと覚える__
~~~
my_list=["hoge", "huga", "moga", "aaa", "bbb", "ccc"]
my_list[1:3]
# ["huga", "moga"]
# "aaa"は含まれない！
~~~

## タプル
- タプル＝イミュータブルなリスト
- 要素の変更（代入･追加･削除･並び替え）ができない
- 書き換えてほしくないデータを安全に扱うことができる
- スライスのやり方はリストと同じ

## 辞書型
- いわゆる「キー：バリュー」。Perlでいうと「ハッシュ」
`my_dict = {"ore":1, "omae":2, "kare":3}`

## 集合型
- 重複の削除に使える
`my_val = {"ore", "omae", "omae", "ore"}`

## bool型
- `True`, `False` ＝先頭大文字

## 条件分岐
- 他の言語と一緒だが、ifの終わりが`:`であるが注意（セミコロンではない）
- 「●●でなく、◆◆ならば」を表すのは`elif`（elsifなどではない。言語によって癖が出る句）
- { }ではなくインデントで示す

## リストのinメソッド
~~~
# 適当なリストを定義
my_values = ['ore', 'omae', 'kare']
print(my_values[0])
print(my_values[1])
print(my_values[2])
for val in my_values:
    print(val)
~~~

## `range`メソッド
- 範囲を示す
- 最後が「未満」であるのが独特なので注意
~~~
# これは0～4まで
for i in range(5):
  print (i)
# これは5～9まで
for i in range(5, 10):
    print(i)
# `for (i=0; i<10; i=i+2)`のイメージ
for num in range(0, 10, 2):
    print(num)
~~~
## `zip`メソッド
- 複数のイテレータから同時に取り出す
~~~
my_values = ['私', 'あなた', '彼']
my_names  = ['田中', '佐藤', '鈴木']
for val, name in zip(my_values, my_names):
        print('{}は{}です｡'.format(val,name))
~~~
## `enumerate`メソッド
- イテレータからindex付きで取り出す
- 0オリジンに注意
~~~
my_names  = ['田中', '佐藤', '鈴木']
for i, name in enumerate(my_names):
        print('会員No.{}: {}'.format(i,name))
~~~
AWS SAMでGo+API Gateway+Lambda+DynamoDBのサーバレスAPI構築をIaC化した

# はじめに
前回Goで作成したAPIの環境構築をSAMでIaC化しました。

https://qiita.com/tkhs1121/items/7b193dcce19539f16761

# 環境
MacBook Air M1
開発言語 Go

# 手順

AWS CLIとSAM CLIをインストールします。

```shell
brew install awscli
brew tap aws/tap
brew install aws-sam-cli
```

AWSコンソールのIAMからアクセスキーを作成して、クレデンシャルを設定します。

```shell
aws configure
```

前回のソースディレクトリにSAMの`template.yaml`を追加します。

https://github.com/tkhs1121/go-serverless-app-3

## ソースコード
```yaml:template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: go-serverless-app-4

Resources:
  Function:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: PostAmazonURLFunction
      Handler: PostAmazonURLFunction
      Role: !GetAtt FunctionRole.Arn
      Runtime: go1.x
      CodeUri: .
      Timeout: 60
      Architectures:
        - x86_64
      Events:
        CatchAll:
          Type: Api
          Properties:
            Path: /post
            Method: POST
  Table:
    Type: AWS::Serverless::SimpleTable
    Properties:
      PrimaryKey:
        Name: id
        Type: Number
      TableName: table
      ProvisionedThroughput:
        ReadCapacityUnits: 1
        WriteCapacityUnits: 1
  FunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
      Policies:
        - PolicyName: DynamoDBPutItemPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - dynamodb:PutItem
                Resource: !GetAtt Table.Arn
```

go1.19へアップデートしていたりtemplate.yamlを含む微修正版。

https://github.com/tkhs1121/go-serverless-app-4

ソースコードの準備ができたら、SAMのビルドコマンドを実行します。
```shell
sam build
```

ビルドが完了したら、デプロイコマンドを`--guided`オプションをつけて実行します。
```shell
sam deploy --guided
```
指示に従って、必要な情報を入力します。

デプロイが完了したら、APIのエンドポイントへリクエストを送って問題ないか確認します。
私はAPI Gatewayからテストを選択してbodyに以下を入力して実行しました。
```json
{
    "url": "http://www.amazon.co.jp"
}
```

ステータスコード:200, "SUCCESS"が帰ってきたら成功です。

:::note warn
テストが完了したら、CloudFromationのスタックの削除をして、
リソースを削除することを忘れないようにしましょう！
:::

## 最後に
前回の記事で手作業でやった手順がほぼ自動化されたので環境構築が大分楽になったという感想です。
一からテンプレートを作成するのが初めてだったので、所々ハマって時間が掛かってしまったんですが勉強にはなりました。

次回は、登録したURLを取得したり、登録する際のアルゴリズムを修正するところまでいけたらと思います。

最後まで読んでいただきありがとうございました！:bow:
EC-CUBE4.2で商品一覧に会員限定商品のみフィルターを追加する

# 概要
会員限定機能を追加できるプラグインを作成しました。
https://www.ec-cube.net/products/detail.php?product_id=2671

こちらに会員限定商品のみをプラグインやコアファイルを弄ることなく一覧で表示する方法のご紹介です。


「mtb_product_list_order_by」テーブルに1項目を追加する

Cusotomizeディレクトリ内にRepositoryディレクトリを設置する

追加したディレクトリにProductWhereAdd.phpを追加する
```php:ProductWhereAdd.php
<?php
namespace Customize\Repository;

use Eccube\Doctrine\Query\QueryCustomizer;
use Eccube\Repository\QueryKey;
use Doctrine\ORM\QueryBuilder;

class ProductWhereAdd implements QueryCustomizer {

    public function customize(QueryBuilder $builder, $params, $queryKey)
    {
        if(!empty($params["orderby"]) && $params["orderby"]->getId() === 4) {
            //Whereだとカテゴリー時にエラーになるのでandで指定
            $builder->andWhere("p.only_member = 1");
        }
    }

    public function getQueryKey(): string
    {
        // 適用したいメソッドを指定することで、自動的に有効になる。ドキュメント参照
        //https://doc4.ec-cube.net/customize_repository
        return QueryKey::PRODUCT_SEARCH;
    }
}
```


## 参考にしたドキュメントやサイト
https://doc4.ec-cube.net/customize_repository

https://a-zumi.net/eccube4-product-order-by-update/



プラグインにその機能を含める場合はプラグイン削除の処理等を考えるとちょっと面倒でして、Cusotomizeディレクトリからカスタマイズしたほうがスマートだと思いプラグインには機能を含めませんでした。



## 含める場合の参考処理

インストール時に、「mtb_product_list_order_by」テーブルに1項目追加する
追加したidを取得してプラグインで定義したオリジナルのテーブルにリレーションを貼る
プラグイン削除時にオリジナルのテーブルからリレーション先の「mtb_product_list_order_by」を辿って、追加した項目だけを削除


「mtb_product_list_order_by」に判定用のカラム追加でもいいですが、なるべくEC CUBEデフォルトのテーブルは触りたくないところなので、このような処理になると思います。
paizaラーニング「重複の削除 Python3編」

https://paiza.jp/works/mondai/array_primer/array_primer__deduplication
#### 私の解答
```Python3
li = [1, 3, 5, 1, 2, 3, 6, 6, 5, 1, 4]

new_li = []
 
for ele in li:
    if ele not in new_li:
        new_li.append(ele)

sorted_new_li = sorted(new_li)
for ele in sorted_new_li:
    print(ele)
```
#### 解答例
```Python3
li = [1, 3, 5, 1, 2, 3, 6, 6, 5, 1, 4]

li_set = set(li)
sorted_li_set = sorted(li_set)

for ele in sorted_li_set:
    print(ele)
```
set(li)で重複の削除を行うことを覚えました。
Prefect 2をGitHub Actionsからデプロイするときには `PREFECT_API_URL` を指定しよう

「[Prefect Cloud + Fargateでバッチ処理のワークフローを作成するまで](https://qiita.com/ninomiyt/items/2879986208d60bf67864)」にあるように、私はPrefectの実行環境を整えていて、GitHub ActionsによるCI/CD処理の自動化を試みています。

Prefect 2で、GitHub ActionsからS3のStorageを利用してデプロイする処理を実装したく、次のように `PREFECT_API_KEY` を利用してデプロイする実装をしていました。

```yaml:.github/workflows/deploy.yml
# 本当はPythonのインストール等があるが省略
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        role-to-assume: ${{ env.AWS_ROLE }}
        role-session-name: github-deploy
        aws-region: ${{ env.AWS_REGION }}

    - name: Sample
      env:
        PREFECT_API_KEY: ${{ secrets.PREFECT_API_KEY }}
      run: |
        prefect deployment build sample_flow:sample_flow -n sample_flow -sb s3/sample_storage -t sample_tag
        prefect deployment apply 'sample_flow-deployment.yaml'
```

ところが、次のように「S3のblock typeに、（作成したはずの）sample_storageが存在しない」というエラーが出てしまいました。PCから　`prefect cloud login` を利用した場合は、同様のコマンドを実施すると問題なくデプロイできる状態だったので不思議に思ってました。

```
ValueError: Unable to find block document named sample_storage for block type s3
```

これは明示的に環境変数で `PREFECT_API_URL` を指定すると問題なくデプロイできるようになりました。おそらくworkspaceが特定できていなかったものだと思います。

```diff:.github/workflows/deploy.yml
    - name: Sample
      env:
        PREFECT_API_KEY: ${{ secrets.PREFECT_API_KEY }}
+         PREFECT_API_URL: "https://api.prefect.cloud/api/accounts/${{ secrets.PREFECT_ACCOUNT_ID }}/workspaces/${{ secrets.PREFECT_WORKSPACE_ID }}"
      run: |
        prefect deployment build sample_flow:sample_flow -n sample_flow -sb s3/sample_storage -t sample_tag
        prefect deployment apply 'sample_flow-deployment.yaml'
```

AWSではなくGCEを使っている実装例ですが、こちらの実装を参考にしました。

https://github.com/BennyJW/prefect2-docker-gce
ビスケットを叩く要領でデータを倍増するSQL

# はじめに
SQLのtips記事です。
大量データを使ってテストしたい場合に実際に利用したSQLを紹介しています。

# 件数倍増SQL
0件のempテーブルの件数を増殖するSQLです。
2つ目のSQLを実行する毎に、データ件数が2件、4件、8件、、、と倍々で増えていきます。
```
INSERT INTO emp VALUES (1); -- 最初の1件
INSERT INTO emp SELECT id+(SELECT count(*) FROM emp) FROM emp; -- 実行毎に倍増する
```
# その他データ作りに役立つこともある関数
- ROWNUM(Oracle)
取得件数毎に一意にナンバリングしてくれるので、採番で便利です
 `select rownum from emp`
- mod
rownum等と組み合わせることで、余りの値でグルーピング・属性を分けることができます
 `SELECT id,mod(id,10) FROM emp`
- trunc(dbms_random.value*n)
random関数は0~1の値を取るので、n倍(1000倍など)してtruncすることで特定の範囲の整数値をランダムに生成できます。

# おわりに
これで突然「○○を満たす大量データを明日までに作って」と無茶ぶりされても
大丈夫なはず、です。お役に立てば幸いです。
Grep コマンド早見表: 複数のアクセスログから条件抽出 (自分用)



AWS の ELB ログ、CloudFront ログなど、複数のファイルから、条件を絞り込んで欲しいアクセスログを抽出する方法の個人的なまとめです。

正規表現とか忘れちゃうんですよね。

# 1. 特定のステータスで、アセット以外のアクセスだけを抽出する

## 404 ページを見つける

`grep -e` オプションで、正規表現を含む

```
cat [アクセスログファイル名] | grep -e 'HTTP\/1.1\\" 404'
```


## .png 等の画像拡張子以外のアクセスログを抽出する


`grep -v` オプションで、正規表現を含まない

```
cat [アクセスログファイル名] | grep -v '\.png"
```

## 合わせ技

画像フォーマット以外の404リクエストをアクセスログから抽出する方法

- `HTTP/1.1\" 404` 文字列が含まれる行
- `.png` `.jpg` `.jpeg` `.gif` `.webp` `.mp4` `.mov` 文字列が含まれない行

```
cat [アクセスログファイル名] | grep -e 'HTTP\/1.1\\" 404' | grep -v "\.(png|jpg|jpeg|gif|webp|mp4|mov)"
```
【Python】コンソールへのクリック位置を取得する

# はじめに
cursesライブラリを使って、コンソールへのマウスクリックの位置を取得する。コンソールアプリを作ろうと思い、そのための自分用メモ。



# cursesライブラリ
cursesって何？という説明は以下。

> curses ライブラリは、VT100s や Linux コンソール、さまざまなプログラムが提供するエミュレーション端末といったテキストベースの端末（ターミナル）のために、端末に依存しないスクリーン描画や、キーボードの処理を提供します。端末はカーソルの移動や、画面のスクロール、領域の消去といった共通の操作を行うための様々な制御コードをサポートします。端末の種類によって大きく異なる制御コードを使うことがあり、しばしば独特の癖があります。

https://docs.python.org/ja/3/howto/curses.html

また、より詳しい説明は以下。

https://docs.python.org/ja/3/library/curses.html#module-curses


## Windowsの場合
cursesはWindowsには標準で入っていないので、Windows用のライブラリをインストールしておく。

```
> pip install windows-curses
```

https://pypi.org/project/windows-curses/



# マウスイベントの取得
マウスイベントを取得するサンプルコード。
- コンソールへ「クリック」した位置に座標(x, y)を印字
- 「ダブルクリック」で画面を消去
- 「q」を押すと終了

```mouse_xy.py
import curses


def mouse_xy(stdscr):
    curses.mousemask(curses.ALL_MOUSE_EVENTS)
    while True:
        event = stdscr.getch()
        if event == ord('q'):
            break
        if event == curses.KEY_MOUSE:
            _, x, y, _, bstate = curses.getmouse()
            if bstate == curses.BUTTON1_CLICKED:
                stdscr.addstr(y, x, f'({x}, {y})')
            elif bstate == curses.BUTTON1_DOUBLE_CLICKED:
                stdscr.erase()


if __name__ == '__main__':
    curses.wrapper(mouse_xy)
```

実行の様子は以下。
![mouse_xy.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/1157d1ea-d095-8010-2d99-b24f406510d3.gif)


## curses.mousemask(mousemask)
`mousemask`で指定したマウスイベントを許可する。以下のフラグを指定可能(一例)。マウスイベントを取得する前に実行しておく。

|フラグ名|イベント|
|---|---|
|curses.ALL_MOUSE_EVENTS|すべてのマウスイベント |
|curses.BUTTON1_PRESSED|左ボタンを押した時 |
|curses.BUTTON1_RELEASED|左ボタンを離した時 |
|curses.BUTTON1_CLICKED|左ボタンをクリックした時 |
|curses.BUTTON1_DOUBLE_CLICKED|左ボタンをダブルクリックした時 |
|curses.BUTTON1_TRIPLE_CLICKED|左ボタンをトリプルクリックした時 |

BUTTONの番号を変えれば、真ん中ボタン(BUTTON2)、右ボタン(BUTTON3)など対応するボタンを変更できる。(ボタンは1～5まで使える)

イベントを許可する例として以下のようにすると、左ボタンのクリック時かダブルクリック時のみイベント通知が発生するようになる。

```python
curses.mousemask(curses.BUTTON1_CLICKED | curses.BUTTON1_DOUBLE_CLICKED)
```


## stdscr.getch()
キーが押されるのを待ち、入力されたキーを取得する。マウス入力の場合は、`curses.KEY_MOUSE`キーとして取得する。


## curses.getmouse()
`stdscr.getch()` が`curses.KEY_MOUSE`を返して、マウス入力の通知後にこのメソッドを呼ぶと、マウスイベントを取得できる。取得した結果は以下のタプル形式。

```python
(id, x, y, z, bstate)
```

|タプルの値| 意味|
|---|---|
|id|デバイスの種類|
|x|x座標|
|y|y座標|
|z|未使用|
|bstate|イベントの種類|


## stdscr.addstr(y, x, str)
(`y`, `x`)の位置から、`str`の文字列をコンソールに印字(上書き)。

## stdscr.erase()
ウィンドウをクリアする。

## curses.wrapper(func)
`func`にコンソール処理をまとめた関数を指定する。`func`の第一引数にはウィンドウオブジェクト(`stdscr`)が渡されるので、必ず用意しておく。他に引数が必要な場合は、続けて`wrapper`に渡しておくと、そのまま`func`の第二引数以降に渡される。

`wrapper`を介して実行することで、`curses`の初期設定と例外発生時の端末状態の復元を`wrapper`にお任せできる。


# おわりに
**curses**ライブラリを使って、簡単にマウスをクリックした位置が取れた。これで何かしらコンソールをクリックして遊べるものが作れそう。

他にも、文字や背景に色を付けたりカーソルを移動させたりと、できることは多そう。それでは！
【FileMaker】前回のスクリプトの結果が更新されずに引き継がれるケース

# 概要
タイトルそのまま。
メインスクリプトの中でサブスクリプトを実行し、サブスクリプトの結果を受け取る処理を複数回行う際に、一つ前のスクリプトの結果が引き継がれることがある。

# 具体例
上記の事象が起きるスクリプトの一例は以下の通り。

```スクリプト:メイン
変数を設定[ $i ; 値: 0 ]
Loop
    変数を設定[ $i ; 値: $i+1 ]
    スクリプトを実行[ 指定: 一覧から ; 「サブ」 ; 引数: $i ]
    変数を設定[ $スクリプトの結果 ; 値: Get(スクリプトの結果) ]
    Exit Loop If[ $i=2 ]
End Loop
```
```スクリプト:サブ
If[ Get(スクリプト引数) = 1 ]
    現在のスクリプト終了[ テキスト結果: 1]
Else
    現在のスクリプト終了[ テキスト結果: ]
End If
```

この場合、メインスクリプトを動かすと、変数「$スクリプトの結果」は以下のようになります。
```スクリプト:$スクリプトの結果
$スクリプトの結果[1] … 1
$スクリプトの結果[2] … 1
```
スクリプト:サブの4行目でスクリプトの結果を明示しない場合、前回のスクリプトの結果がそのまま引き継がれるようです。

[公式ドキュメント](https://fmhelp.filemaker.com/help/12/fmp/jp/html/func_ref2.32.71.html)によると「サブスクリプトが結果を返さない場合、スクリプト結果の内容は空になります」とありますが、スクリプトの結果を複数回受け取る場合にスクリプトの結果を明示していない場合、前回のスクリプトの結果をそのまま引き継ぐようです。

$スクリプトの結果[2]を空欄にしたい場合は、スクリプト:サブの4行目でスクリプトの結果を明示すると良いです。
```スクリプト:サブ
If[ Get(スクリプト引数) = 1 ]
    現在のスクリプト終了[ テキスト結果: 1 ]
Else
    現在のスクリプト終了[ テキスト結果: "" ]
End If
```

## 追記
最近の[公式ドキュメント](https://help.claris.com/ja/pro-help/content/get-scriptresult.html)には上記のケースが説明されていました。
アジャイル開発 スクラム概要

アジャイル、スクラムについて学ぶ機会がありましたので、まとめてみました。
まず下記スクラムガイドをさらっとみてみる
何書いてあるかよくわからん。。。。。

https://scrumguides.org/docs/scrumguide/v2020/2020-Scrum-Guide-Japanese.pdf

# そもそもアジャイル開発とは
アジャイルとは「素早い、身軽な、機敏な、頭の回転が速い」という意味の形容詞で、アジャイル開発とは開発手法の一つです。
一つの対象の開発スケジュールを要件をもとに決め、WBS引いて「設計→構築→テスト→リリース」という一連の開発サイクルを行うウォーターフォール型と違い、
一つの機能やビジネスユニットなどの細かい単位で開発サイクルを区切り、何度も開発サイクルを繰り返します。

何度もサイクルを繰り返すことで、変化に対応しやすいという利点があります。
例えばメンバが参画したり辞めたり流動性が高くても、開発サイクルを分けているので変化に強い。
一度テストなどで失敗や抜け漏れがあった場合、次の開発サイクルで対処したり修正に強かったりします。

変化の早い、正解がない不確実な環境で、素早く確認しながら動く必要のある領域に向いている領域
つまり大規模で継続的に新規機能を追加するアプリケーション開発や、長期間でWBSで期間を見積もることが難しく、見積もっても大幅にスケジュールが変動するようなプロジェクトに向いているメリットがあります。

# スクラムとは
アジャイル開発でのプロジェクト管理の仕組みの一つです。

アジャイル開発にはデメリットもあります。
メンバが目的ややるべきことなどの共通認識を持っていなければ、
* このプロジェクトのゴール（O）はなんだっけ？
* 達成するための指標（KR）はなんだっけ？
* 今どこのサイクルで何をやっているんだっけ？
* それが終わるのはいつだっけ？
など見失い、結果スケジュールがズルズルと伸びやすいというところがあります。

共通認識を見失わないため、定期的に認識合わせや、成果に対してのレビューを行うような手法＝フレームワークの一つがスクラムです。
他にもXP（エクストリームプログラミング）とかカンバンなどのフレームワークがあります。

https://scrumguides.org/docs/scrumguide/v2020/2020-Scrum-Guide-Japanese.pdf

# スクラムを始めるにあたって

まずスクラムとは手法の一つであり、こうしなければならないというものではなく、プロジェクトメンバで活用することでプロジェクトがうまくいけばなーというものです。
当然プロジェクトを進め、フィードバックが悪ければ改善していくものですし、プロジェクトメンバの性質によっては、手法を変える必要もあります。
つまりここで記載することも正解ではなく、実施しフィードバックをもとに改善していくものなので、そんなものなんだなレベルの理解で問題ないと思います。

https://scrumguides.org/docs/scrumguide/v2020/2020-Scrum-Guide-Japanese.pdf

上記は2020年のスクラムガイドですが、最新のスクラムガイドはIT以外でもプロジェクトを進める上で採用されているため、下記の2017年バージョンの方がエンジニアとして馴染みがあるかもしれません。

https://scrumguides.org/docs/scrumguide/v2017/2017-Scrum-Guide-Japanese.pdf
OpenSSL のビルド

OpenSSL を使ったプログラムをデバッグしたくなった時に読むメモ。大体は以下を参照すれば OK。

[OpenSSL Cookbook 3rd Edition - 1.1.2  Building OpenSSL](https://www.feistyduck.com/library/openssl-cookbook/online/openssl-command-line/building-openssl.html)

例えば OpenSSL 1.1.1 を試したいときは次のようにする。`no-shared` がポイントらしい。


```
git clone https://github.com/openssl/openssl.git
cd openssl
git checkout OpenSSL_1_1_1 -b OpenSSL_1_1_1
./config --prefix=$HOME/local no-shared --debug
make
```

この時点で apps/openssl コマンドが出来ているので、特定のバージョンの CLI を試したいだけならこれだけで良い。他のプログラムで使いたい場合は make install で `--prefix` で指定した場所にインストールする。

```
make install
```

例えば CMakeLists.txt で使いたい場合はインストールした位置を指定する。

```
include_directories(~/local/include)
link_directories(~/local/lib)
```

私の場合なぜかローカルの openssl をリンクする時だけ dl と pthread を追加で指定する必要があった。ビルドの仕方が間違ってるのかな?

```
add_executable(hoge hoge.cpp)
target_link_libraries(hoge crypto dl pthread)
```

これで openssl の中までデバッグできて気持ちいい。
ローカルのGoのバージョンアップはめちゃ簡単な話

# 前提
Macでしか試していません。
古いバージョンのGoは、Go公式からダウンロードしてインストールした場合。

# バージョンアップ方法
[Goの公式サイト](https://go.dev/doc/install)でDownloadボタンを押して、インストールを進めるだけです。

既にGoがインストール済みの場合は以下のポップアップがでます。OKを押すだけで古いバージョンを削除したうえで、新しいバージョンをインストールしてくれます。
![Goのインストール.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221948/040cb241-bcc1-ebf5-531d-d2d786461f27.png)

自分で古いGoをアンインストールする手間もないので、めちゃ楽ですね！
Docker Desktop × PostgreSQL 環境構築メモ

## ローカル環境構築時の私的メモ

### 環境

* OS: Windows11
* WSL: Ubuntsu
* Docker: Docker Desktop
* DB: PostgreSQL

### 簡単な作業内容

#### Docker Desktopはインストールしてセットアップしておく

#### PosgreSQLのコンテナイメージを以下コマンドでDL（Pull）

~~~
$ docker pull postgres
~~~

#### データの永続化のためのフォルダ作成

WindowsのCドライブ配下にデータ用のフォルダを作成してみる。

~~~
$ mkdir c:\pgData
~~~

おもむろに以下のコマンドを実行

~~~
docker run -p 5432:5432 -d -it --name postgresql -e POSTGRES_PASSWORD=hoge -v c:\pgData:/var/lib/postgresql/data postgres
~~~

pgAdmin4などでアクセスしてみる。

ログインしてデータを登録などして、dockerコンテナを停止後、再度起動してアクセスしてみると
ちゃんとデータは残っていることを確認。
課題管理票等から大量にgithubのissueを作るときのtips

## 基本

githubのURLクエリでのissue作成

https://docs.github.com/ja/issues/tracking-your-work-with-issues/creating-an-issue#creating-an-issue-from-a-url-query

## Tips

### シート内容を改行区切りでつなげてテキストにする

こういうやつ
```
"&C7&"
"&C8&"
"&C9&"
"&C10&"
"&C11&"
...
```

1. 数字連番をシートで作る
1. =""&C"&`A1`&"&"" みたいに書いて文字列作成
1. エディタにコピー => 改行区切りする最後のbody作成セルにコピー

### URLエンコード

普通にやると # とか & がURL扱いされるので ENCODEURL() を使う


### 複数ラベル

カンマ区切りで行ける `hoge,fuga`
マイグレーションを自作する 

# カラムを編集する indexにする
```rb
create_table :users do |t|
  t.string :name, index: true
  t.string :email, index: { unique: true, name: 'unique_emails' }
end
```
# 編集したものをまた編集したいときはどうすればいいのか?
```
bin/rails g migration ChangeNameOnProducts
```
もう一度このカラムについて編集したくなった。
```
************@mbp ********** % bin/rails g migration ChangeNameOnProducts  
      invoke  active_record
    conflict    db/migrate/20221227074540_change_name_on_products.rb
Another migration is already named change_name_on_products: /Users/*********/Repos/********/db/migrate/20221227063442_change_name_on_products.rb. Use --force to replace this migration or --skip to ignore conflicted file.
```
カラムの定義を変更して
またカラムの定義を変更したくなったときは
直接マイグレーションを編集するよりもマイグレーションファイルを削除して新たに作成してもいいのだろうか？

### マイグレーションファイルは削除してもいいのか
マイグレーションファイルはいわばデータベースを作成した歴史です。
ですので実行されてup状態になっているマイグレーションファイルは絶対に削除したり編集したりしてはいけません。
もし誤ってup状態のマイグレーションファイルを削除してしまった状態でアプリをサーバーにアップした時、エラーが出てしまうためです。

マイグレーションファイルを削除する際は必ずdown状態になっているのを確認してから削除するようにしましょう。
### 出典
https://pikawaka.com/rails/migration#:~:text=%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B%E9%9A%9B%E3%81%AF%E5%BF%85%E3%81%9Adown%E7%8A%B6%E6%85%8B%E3%81%AB%E3%81%AA%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%82%92%E7%A2%BA%E8%AA%8D%E3%81%97%E3%81%A6%E3%81%8B%E3%82%89%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%97%E3%81%BE%E3%81%97%E3%82%87%E3%81%86%E3%80%82
応用情報技術者試験に5か月で一発合格した勉強法（未経験）

<h4>前提</h4>
2022年10月に受験し、合格。成績は以下。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2665395/f8b76146-f8f4-b8e4-4142-c69205297040.png" width=200,align="left" >

勉強を始めたのは同年5月。
当時IT経験ゼロでしたが、それまで所属していた編集部門からエンジニアばかりのデジタル部門に異動し、大パニック。必要に駆られて勉強を始めました。
応用情報にしたのは「ITパスポートはさすがに簡単すぎる」「基本情報はプログラミング知識が必要なので、むしろ応用の方が取得しやすいのでは？」などと考えたからです。
コツコツ勉強した結果、約5か月で合格しました。
勉強時間は100時間くらいで、難易度は行政書士試験と同じくらいかなという手応えです。6割で合格という点、択一と記述がある点、一般常識で解ける問題も結構ある点など、分野こそ違いますが似ています。

<h4>勉強方法</h4>
<strong>◉午前対策</strong>


まずは<strong>「キタミ式イラストIT塾 応用情報技術者」</strong>というテキストを２周ほどしました。イラストが豊富で、日本語も平易で分かりやすく、結果的にはこれを選んで大正解でした。
一番有名な「合格教本」も買いましたが、未経験の私には難しすぎて何のこっちゃ分かりませんでした汗

２周ほどして大枠は理解できたので、次は<strong>「応用情報技術者試験ドットコム」</strong>という神サイトで、「おすすめ過去問」（10回分）をひたすら解きまくりました。
応用情報の午前択一は、過去問から似た問題の出る割合が高い(4割くらい)です。過去問10回分を完璧にし、4割ほど正解できるようにしておけば、あとは残り6割のうち3分の1当てるだけで合格基準点（60点）を突破できるので、過去問を丸暗記するのが一番の近道だと思います。
「おすすめ過去問」を10周ほどして、最後には97%ほど正答できるまで完成させました。

<strong>◉午後対策</strong>
午後対策を始めたのは、試験1ヶ月前の9月からです。それまではずっと午前択一の過去問を繰り返していました汗

午後は「情報セキュリティ」だけ必須ですが、あとは「経営戦略」「システムアーキテクチャ」「ネットワーク」「データベース」「組み込みシステム開発」「情報システム開発」「プログラミング」「プロジェクトマネジメント」「サービスマネジメント」「システム監査」から、好きなテーマを四つ選択する方式です。

私にはテクノロジ系は何のこっちゃわからないので、迷わず<strong>「文系セット」</strong>一択でした。

※文系セットとは・・・経営戦略、プロジェクトマネジメント、サービスマネジメント、システム監査のこと。技術的な知識をほぼ必要とせず、一般常識とビジネス知識と「国語力」のみでゴリ押せる。

上記の通り、新しい知識の習得はほぼ不要（午前択一を解けるレベルでOK）なので、あとは慣れるだけです。
<strong>「応用情報技術者　午後問題の重点対策」</strong>という、過去問をやたら詳しく解説してくれているテキストを1ヶ月解きまくり、仕上げていきました。
本番では予定通り「文系セット」を選択。練習では時間が余っていましたが、本番では意外と時間がかかり、最後の「システム監査」を最後まで解けなかったのが大失敗でした。
が、「サービスマネジメント」がたまたまほぼ完答できたこともあり、合格できました。
時間配分には気をつけましょう。。。

<h4>結論</h4>

応用情報技術者試験は、未経験でも合格できます！！！！！


※「試験合格法」をテーマに執筆したので、受かるためのテクニックという視点でばかり書いており「試験を受けることで何が得られるか」という本質を見失っている感じがしますが、実際はテクノロジ系・ストラテジ系・マネジメント系全てでちゃんと学びがあったので、受ける意味のある試験です！
TalknoteApiの利用開始方法

公式ドキュメントの手順通りに進めていく。
https://developer.talknote.com/doc/#top

## Talknote管理者設定よりAPI公開設定にする 

この際にクレデンシャル情報をもらう

## トークンの取得

1. 管理者よりリダイレクトURLをもらい、ブラウザでアクセスする。
1. Outh認証画面にいくため、`F12`で開発者ツールを開いた後、ログイン情報を入力しログインする。
1. 開発者ツールのネットワークのGETリクエストのペイロードに`Code`が表示されるため控える。
1. curl等でPOSTリクエストを実行する。
1. 成功すれば、json形式でアクセストークンが返却される。

コマンド例

```bash
curl -X POST -d "client_id=da271bfbab783480eb3fcf8aadf5a0d07c17dddd" -d "client_secret={secret}" -d "redirect_uri=https://company.talknote.com/example.com/auth_validate" -d "grant_type=authorization_code" -d "code={code}" https://oauth.talknote.com/oauth/token
```

説明付き

```bash
curl -X POST 
-d "client_id=da271bfbab783480eb3fcf8aadf5a0d07c17dddd" # talknoteの公開APIのid
-d "client_secret={secret}"  # talknoteの公開APIのsercret key
-d "redirect_uri=https://company.talknote.com/example.com/auth_validate" # talknoteの公開APIのurl
-d "grant_type=authorization_code"  # 固定
-d "code={code}" # ブラウザでアクセスした際にOuthで発行されるコード
https://oauth.talknote.com/oauth/token # 固定
```

## APIの利用

使う際はヘッダーに`X-TALKNOTE-OAUTH-TOKEN`をつける

```bash
curl -X POST -H "X-TALKNOTE-OAUTH-TOKEN:{token}" -d 'message=iine' https://eapi.talknote.com/api/v1/group/post/417600
```
pythonでほかのスクリプトファイルを呼び出す方法と変数をほかファイルから呼び出す方法

短く書く。

呼び出すにはimportを使う

```py
// コードA
print ("Hello, World!")
```

```py
// コードB
import CodeA
```

変数を呼び出すにはimportを使う。そして、呼び出す側にも変数を作り、中身は　コードの名前.変数名　のような形式にする。

```py
// コードA
variabletest = Hello, World!
```

```py
// コードB
import CodeA
hensu = CodeA.variabletest
print (hensu)
```
エンジニアとして過ごした２０代を振り返ってみる

## 概要

２０２２年も終わりなので最後に自分の今までのエンジニア活動を振り返る記事を書こうと思い立ちました。

内容的にも今年から書き始めたnoteへと投稿しました。
自分自身の備忘録として供養したいと思います。

https://note.com/kenmaro/n/nbcf0f94d952b

これを持ってアドベントカレンダーの最終投稿としたいと思います。

皆さんも良いお年をお迎えください。


@kenmaro
2022/12/27主にITとかセキュリティの記事

食料農業分野がメール詐欺の標的に - 粗悪な食品が出回るおそれも
https://www.security-next.com/142518

日経の記事クリッピングサービスにサイバー攻撃 - 個人情報が流出か
https://www.security-next.com/142508

職員の複数アカウントに不正アクセス - 奈良県立医科大
https://www.security-next.com/142474

OP25B(12/26)
https://xtech.nikkei.com/atcl/nxt/mag/nnw/18/031500132/121900010/

ノートンが銀行アプリを「警告」再インストールを呼びかけ(12/26)
https://xtech.nikkei.com/atcl/nxt/mag/nnw/18/031800050/121900046/

追跡機能付きプラ製品をリサイクル、三井化学ら「Pla-chain」の4社(12/26)
https://xtech.nikkei.com/atcl/nxt/news/18/14387/

富士通の法人向けネット回線サービス「FENICS」に不正アクセス、TKCなど被害(12/26)
https://xtech.nikkei.com/atcl/nxt/news/18/14389/

病院を襲ったランサムウエア攻撃の真相
委託事業者のデータセンター経由で感染拡大　2023年1月の完全復旧を目指す(12/27)
https://xtech.nikkei.com/atcl/nxt/mag/nnw/18/041800012/121900194/

2022年の一大事、ウクライナ侵攻がサイバーセキュリティーにもたらした変化(12/27)
https://xtech.nikkei.com/atcl/nxt/column/18/00138/122401189/

100種類以上あった「暗号スイート」をたった5種類に、TLS 1.3が互換性を捨てたワケ(12/27)
https://xtech.nikkei.com/atcl/nxt/column/18/02306/121900003/

IT業界で50代中途採用が倍増、プロジェクト危機回避や若手採用への好影響を期待(12/27)
https://xtech.nikkei.com/atcl/nxt/column/18/00577/121600049/

QRコードがきっぷ代わりに、リアルタイムの車内映像で防犯
チケットレス・QRコード・次世代通信で旅客サービス向上(12/27)
https://xtech.nikkei.com/atcl/nxt/column/18/02290/00004/

手持ちのWindows 10マシンを有効活用、Miracastでサブディスプレイに(12/27)
https://xtech.nikkei.com/atcl/nxt/column/18/02267/111800016/

無線LANのフルノシステムズで個人情報流出か、50倍増のアクセス数で判明(12/27)
https://xtech.nikkei.com/atcl/nxt/column/18/00598/070100196/

ビジネスモデルで「成果」を設定　その質と量が対価を決める(12/27)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/092900316/122000004/

デジタル化の真価は日常にある　ネット上の今を感じ続けよう(12/27)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020900021/122000128/

ETLなしでデータ分析や機械学習　「両利き」のMySQL HeatWave(12/27)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/021800278/122000013/

ビジネス活用機会が増える　教師あり学習と因果推論(12/27)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/112500326/122000003/

無線LANがつながりにくい　スイッチの設定ミスが顕在化(12/27)
https://xtech.nikkei.com/atcl/nxt/mag/nnw/18/041800004/122000059/
12/19 ミーティング用資料 【チーム開発】


## チーム開発の目的/テーマ
・初めてのチーム開発なので、チームでの開発の進め方を学ぶ。
・１番経験が浅いので、チームメンバーから学んでできることを増やす。

・Recursionのコースのアプリしか作ったことないので、敵のアルゴリズムやスコア計算などに挑戦したい。
・OOPやデザインパターンを開発で使ったことないので使ってみたい。
　　・ステージのバリエーション増やす
　　・後からキャラの追加ができる
　　・とか、、

## ゲームの世界観

### 年齢：20～30代
##### おばあちゃんを敵キャラから守るゲーム。
ステージは和室。みかん、座布団、饅頭、お茶などのアイテム。
おばあちゃんはゴールまでゆっくりしか歩けない。
壁を動かしたり、マスにみかんを置いたりして襲ってくる敵から守る。
ステージクリアすると、おばあちゃんがお礼の手紙をくれる。
手紙は、【親切な人】証明書みたいな感じでツイートできる。

##### 赤ちゃんを敵キャラから守るゲーム。
赤ちゃんは矢印キーで動かせない。プレイヤーはマスにおもちゃやミルクを置いて赤ちゃんを動かす。

### 性別：男性or女性 

##### 食べ物を使って敵を太らせて倒すゲーム
敵のサイズを小さくて素早く設定。爆弾の代わりに敵に食べ物（イチゴとかバナナとか）を与えて太らせる。
敵は食べると太って動きが遅くなる。食べ過ぎるとサイズが大きくなるので、壁に挟まって倒せる。

各ステージをクリアするとスペシャルケーキをゲットできる。
集めたケーキをボスステージで使って倒す。ケーキはハイカロリーなのでダメージ大。
      
### 遊ぶタイミング:家でゆっくり、爆弾じゃなくてリラックスできる舞台にする
観光名所、地図、絵画をゲームの舞台にする
##### モナリザの顔がステージ
ステージの壁は全部壊せるものに設定。ボンバーマンみたいに敵を倒しながら壁も壊していく。
ステージ背景のモナリザの顔が全部見えたらクリア。
壁を壊すほど敵からの逃げ場がなくなって難しくなる。
壁が少なくなると敵の移動速度が速くなる。

or

敵キャラが絵を黒い絵の具で塗っていくので、全部隠れるまでに敵を倒すと、水が流れて黒い絵の具が消えてクリア。


##### 福岡の地図とラーメンなどのアイテム

### 敵の色
色で状態/強さが変化
- 赤
 猛スピード

- 橙
ちょっと速い

- 黄
 活動中

- 青
プレイヤーから逃げる

- 紫
サボる/寝てる
  動かない

### 敵の動き

##### 単調
##### 近づくと追いかけてくる
###### 最短距離
###### 寄り道しながら

##### 障害物に隠れて待ち伏せするやつ
##### 爆弾を投げてくるやつ
###### ２マスくらいの攻撃をしてくるやつ
##### 爆弾から逃げる頭のいいやつ
##### ダメージを受けると分裂して増えるやつ
##### たまに共食いする頭の悪いやつ
##### 倒すと特別なアイテムをくれるやつ
##### プレイヤーの動きと反対に動くやつ
##### プレイヤーから遠ざかる変なやつ



### ステージ
#### 天候を変える

#### バリエーション

- 火山
 爆弾の威力up

- 海
 爆弾の威力down

- 発電所
磁石アイテム、敵キャラを引き寄せる
磁石の障害物、 近づく時速く動けるand遠ざかる時ゆっくりしか動けない

- 森

- 洞窟

- 雪

- 橋で繋がった島

- 敵が登れない階段とか

#### レベルアップ

- 障害物が降ってきて道を塞ぐようになる

- 一定時間で壁が移動する

- でかいボスキャラ

- ステージが広くなる

- 障害物が多い　→　爆弾を少なくして壁に隠れて進むようにする

- 障害物が少なく敵が多い →　隠れる場所が少ないので敵を誘導して倒す

#### 障害物
爆弾の威力によって壊せるかどうか決まる
- 草 →　木
- 小石　→　岩
- 雪だるま　→ 氷
- 焚火　→　炎の渦、吸い込んで敵に吐き出す（カービィ）
- 小さな磁石　→ 大きな磁石
- 小さな海藻　→ 大きな海藻
## 2D or 3D
### 2D
・絵画や地図を平面ステージのモデルにする


### 3D
・高さの違う部分と階段を作って、１つのステージ内に変化を付けられる
　　→ 弱い敵は登ってこれない、
・グリット以外のステージで遊べる
![イメージモデル2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2245453/1a72362d-e5bd-9230-9332-e8550b459980.jpeg)
![3d-0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2245453/4ff14130-26be-5156-1884-b012ec8c5313.png)
Hey you, Kotlinでenumをwhenする時にelse使うなyo!

ラッパーというよりむしろルー大柴みたいなタイトルになってしまいましたが、enum便利ですよね。
今日はKotlinの`when`句の引数にenumを入れる時の心がけについてです。

といっても、言いたいことは超シンプル。
「`else`を使うな」
これだけです。

サンプルで説明します。
```kotlin
enum class Hoge {
    FOO,
    BAR,
    BAZ,
}

fun extractMessage(hoge: Hoge): String {
    return when(hoge) {
        Hoge.FOO -> "FOO!"
        Hoge.BAR -> "BAR!!"
        else -> "Something else"
    }
}
```

割とやりがちですよね。
もちろん、普通に動きます。
ですが、ここで `else` を使ってしまうと、せっかくのenumのメリットが半減してしまいます。

どういう時に困るのか。
それはenumの種類が増えた時です。

```kotlin
enum class Hoge {
    FOO,
    BAR,
    BAZ,
    QUX, // 新しく増えた
}
```

`else`を使ってると、enumに新しい値が増えたとしてもなにも言ってもらえません。
```kotlin
fun sample(hoge: Hoge): String {
    return when(hoge) {
        Hoge.FOO -> "FOO!"
        Hoge.BAR -> "BAR!!"
        else -> "Something else"
        // 普通にコンパイル通る
    }
}
```
仮に、「QUXの時は"QUX!!!"と出したい」という要件だったとしましょう。
でも、コンパイルエラーが出ないので、割と普通に修正漏れが発生してバグの温床になります。

一方`else`を使わずにすべてのケースを網羅する形で書いていたら、新しいenumが増えた時にちゃんとコンパイラがエラーを出してくれます。
```kotlin
fun sample(hoge: Hoge): String {
    return when(hoge) {
        Hoge.FOO -> "FOO!"
        Hoge.BAR -> "BAR!!"
        Hoge.BAZ -> "Something else"
        // コンパイルエラー 
    }
}
```
これによって、「あ、ここも修正するんだった」ということに事前に気づけて、大変にハッピーです。

ということで、復唱します。
「Hey you, Kotlinでenumをwhenする時にelse使うなyo!」
SuiteCRMにカスタムバリデーションを入れる

SuiteCRMは日本ではあまり使われていないんですかね。
情報が少なくて英語で頑張って調べています。

# SuiteCRMとは

SugarCRMをベースにしたOSSのSFA/CRMシステムなのですが、とても多機能でWebデータベースとして使えそうだと感じています。
SugarCRMがOSS提供を辞めたためにSuiteCRMに引き継がれたようです。

https://suitecrm.com/

一般的なデータベースにおけるテーブルに当たるのが"モジュール"です。
自身で数値やテキストフィールドをぽちぽち追加していくことでモジュールを作成できます。
モジュールが完成すると、半自動でモジュールに対するCRUDの画面を作成してくれるので便利です。

モジュールにはREST APIでアクセスも可能なので、参照画面を別に作成すればある程度のことはできるのではと思っています。

# カスタムバリデーションを入れる
自動でCRUD画面を作ってくれるのは嬉しいんですが、登録時にカスタムでバリデーションをかけたいことがあります。
例えば、氏名登録欄で姓名の間にスペースを入れて欲しいとか。
jQueryでのチェックロジックを独自に作成して対応したのでその方法を備忘録として残しておきます。

## editviewdefs.phpの編集
modules/[モジュール名]/metadata/editviewdefs.phpを編集します。
バリデーションロジックを記述するjavascriptファイルの場所を指定します。

```:editviewdef.php
array (
  'EditView' =>
  array (
    'templateMeta' =>
    array (
      'includes'=>array(
        array(
          'file'=>'modules/[モジュール名]/validate.js'
        )
      ),
・・・
```

'includes'の部分を追加してください。

## validate.jsの編集
editviewdef.phpに記述した場所にバリデーションファイルを作成します。
今回はmodules/[モジュール名]にvalidate.jsというファイル名で作成しました。

```:validate.js
$(document).ready(function(){
  $('input[name="name"]').blur(all_check);
});
function all_check(){
    result = true;

    // 氏名
    var student_name = $('input[name="name"]').val().trim();
    $('input[name="name"]').val(student_name);
    if(student_name.length > 0 && student_name.indexOf(' ') < 0){
      alert('氏名は、姓と名の間にスペースを入れてください');
      all_check =false;
    }

    $('input[id="SAVE"]')[0].disabled = !result;
    $('input[id="SAVE"]')[1].disabled = !result;
}
```
nameフィールドの入力を終えた際にall_check()に記述したチェックが走るようにしています。
チェックが通らない場合はSAVEボタンをクリックできないようにします。

このjsファイルは画面読み込み途中でincludeされるので、イベントへの設定はdocument.ready内で呼び出しています。

## まとめ
色々調べるとバリデーションの方法は色々あるようです。
例えば
* SAVEボタンをクリックした時に呼び出す関数を上書きする
* addToValidateまたはaddToValidateCallback関数を読み込む
* customViewEditクラスを作成する

どれもちょっと上手くいかなかった＆修正が複雑だったので、上記に記載した方法で対応しました。

あと、モジュールをビルドし直すとmetadataなどが上書きされる可能性もあるのでバックアップは取っておいた方がいいです。
私はSuiteCRM全体をGitHubで管理しています。
jpgからtxtへ変換する

後輩から「jpgの画素値を書いたテキストファイルに変換したい」と相談を受けた．`pyinstaller`でexe化し，配布してあげることに．
以下，書いたコード．

```python:JPGかPNGを変換する.py
from PIL import Image
from numpy import savetxt, array
from glob import glob
from os.path import dirname, splitext, basename

def jpg2txt(img_path):
    # tsvファイルで保存
    savetxt(f"{dirname(img_path)}/{splitext(basename(img_path))[0]}.txt",
               array(Image.open(img_path).convert("L")),
               fmt="%.0f",delimiter="\t",newline="\n",
               # encording="utf-8"
               )
    return

def main():
    path = input(r"""どのフォルダの画像をテキストに変換しますか？ 

C:\Users\.... みたいな感じで入力してください。
中止したいときは、ctrl + C を押してください。

>>> """)
    
    pics = glob(path+r"\*jpg")
    pngs = glob(path+r"\*png")
    if len(pngs) ==0:
        pass
    else:
        for png in pngs:
            pics.append(png)
    
    for pic in pics:
        print(f"{pic} を変換しました")
        jpg2txt(pic)
    return
    
main()
input()
```
`pyinstaller`でexeにする．自分の引き出しのなさゆえ，`np.savetxt`を使ったばかりに `exclude --numpy`ができず，数百メガバイトとなってしまった．
[Rails,Javascript]モーダルウィンドウの実装

チーム開発で活用しようと思った技術をアウトプット投稿
## 目的
以下のようなモーダルウィンドウの実装
商品の写真をクリックすると以下のように表示、背景の黒い部分をクリックすると戻る
![スクリーンショット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950107/b51c11a5-777b-d3f4-a5ff-cc91f28a67c4.png)




## 開発環境
- Ruby 3.1.2
- Rails 6.1.7
- bootstrap併用(bootstrap自体は実装機能と関係なし)

## 実装手順
### 1.モーダルの設置
モーダルを表示させたいページに以下の記述を行う
例)`index`ページに`show`ページの内容を表示したい場合は`index`に記述
```(例)index.html.erb
<div id="mask" class="hidden"></div>
<div id="modal" class="hidden"></div>
```
### 2.CSSの設定
今回はbootstrap併用のため
`app/javascript/stylesheets`の中に`product.css`を作成
`app/javascript/packs/application`内に以下を記述
```application.js
import "../stylesheets/product.css"
```
これでproduct.cssが読み込まれるようになるのでモーダル処理の記述
#mask等の#はiｄで適応する箇所を指定している
```product.css
/*#mask、つまり背景の設定*/
#mask {
  background-color: rgba(0, 0, 0, 0.5);/*背景色の指定*/
  position: fixed; /* スクロールで移動しないようmodalの位置を固定 */
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
  z-index: 1; /*z-index を持つ要素はより小さな要素の上に重なる*/
}

#modal {
  position: fixed;/* スクロールで移動しないようmodalの位置を固定 */
  top: 20vh; /*上端からの距離の指定、今回は画面上から２０％の位置から配置*/
  left: 0; /*左からの距離の指定*/
  right: 0; /*右からの距離の指定*/
  margin: 0 auto; /*上２つと合わせ中央に配置される*/
  transform: translate(0, 0) scale(1); /*平行移動（translate）、拡大縮小（scale）の指定*/
  transition: 0.2s; /*上記trancsformに要する時間、今回は0.2秒*/
  z-index: 2; /*z-index を持つ要素はより小さな要素の上に重なる*/
  width: 50vw; /*モーダルの幅の指定、今回はvwを用いて画面の50%幅でモーダルを作成*/
  background-color: white; /*背景色*/
  border-radius: 8px; /*角の処理、指定することで丸みを帯びる*/
}

#mask.hidden {
  opacity: 0; /*透明度*/
  visibility: hidden; /*視認性、最初は隠しておきたいのでhiddenを指定*/
}

#modal.hidden {
  opacity: 0;
  visibility: hidden;
  transform: translate(0, 0) scale(0.8);
}
```
### 3.モーダル化したい部分の部分テンプレート化、及びコントローラー処理
モーダルとしてshowページを指定したいのでshowページを部分テンプレート化

また今回は商品一覧で、商品の写真をクリックした際にshowページを呼び出したいので、indexのlink_toに`remote: true`を指定しJS形式で送信させる
```index.html.erb
〜省略〜
<%= link_to product_path(product.id), remote: true do %>
  <%= image_tag product.get_image(180, 120), class: 'm-1 img-fluid mb-1' %>
<% end %>
〜省略〜
```
今回はshowページの表示だけなので、特にコントローラーに記述する必要は無いが、送信形式で表示処理を分けたい場合は以下のように記述
```products_controller.rb
def show
# 〜省略〜
  respond_to do |format|
    format.html
    format.js # js形式で送信された場合はこちらが適応され、js.erbを探す
  end
# 〜省略〜
end
```
### 4.jsファイルの作成
`app/views/public/products`内に(要はindex,showと同じフォルダに)`show.js.erb`を作成
-  `partial:`に先程作成した部分テンプレート
- `locals:`に部分テンプレート内に必要な変数を記述
```show.js.erb
<!--モーダルを取得-->
var modal = document.getElementById('modal');
<!--マスクを取得-->
var mask = document.getElementById('mask');

<!--// # モーダルの中身をshowビューに置き換え-->
<!--// # 部分テンプレートを指定する-->
modal.innerHTML = '<%= escape_javascript(render partial: 'show', locals: { cart: @cart, genres: @genres, product: @product}) %>';

<!--// # hiddenを削除してモーダルとマスクを表示させる。-->
modal.classList.remove('hidden');
mask.classList.remove('hidden');

<!--// # モーダルの外側（マスク）をクリックするとhiddenクラスが書き込まれ、モーダルとマスクが再び非表示となる-->
mask.addEventListener('click', () => {
  modal.classList.add('hidden');
  mask.classList.add('hidden');
  });
```
これで完成です！

## 参照
以下記事を参照させて頂きました、ありがとうございます

https://qiita.com/MitsuTurtle/items/3283832f5df2d3fcbeb7
【Go】並列実行の勉強がてらスリープソートを書いてみよう

# まえがき
ゴルーチン、まるで分らない…Fuseです
そんな悩みを解決しようと並列実行が使えそうなものを考えてはみたもののなかなか思いつかない、
そんな中、Wikipediaを漁っていたら…
>バケットソートのバケツをメモリ空間の代わりに時間に置き換えたもので、もともと掲示板4chanに投稿されたアイデアである。
>「全要素の最大値×スリープさせる単位時間」で実際にソートできてしまう。それが役に立つ事例は例題程度であろう。

[バケットソート/スリープソート](https://ja.m.wikipedia.org/wiki/%E3%83%90%E3%82%B1%E3%83%83%E3%83%88%E3%82%BD%E3%83%BC%E3%83%88#.E3.82.B9.E3.83.AA.E3.83.BC.E3.83.97.E3.82.BD.E3.83.BC.E3.83.88)より引用
### これだッ！！
というわけで作ってみました。
## 完成品
https://replit.com/@FRMKDJ/SleepSort?v=1
## ソースコード
https://github.com/Fuses-Garage/SleepSort/settings
## 環境
・Go 1.17.5 linux/amd64
・デプロイ先兼IDE Repl.it
## 解説(?)
```main.go
package main

import (
	"fmt"  //入出力に使う
	"sync" //非同期実行に使う
	"time" //sleepに使う
)

func main() {
	var answer []int //ソート後のスライスがここに入る
	var numbers []int
	var n int
	var wg sync.WaitGroup
	print("ソートする要素数を入力してください（整数）：")
	_, err := fmt.Scan(&n) //整数の個数Nを受け取る
	checkErr(err)          //エラーチェック
	wg.Add(n)              //カウンタをN個増やす
	for i := 0; i < n; i++ {
		var num int
		fmt.Printf("%d個めの要素を入力してください（整数）：", i+1)
		_, err := fmt.Scan(&num)       //整数を1つ受け取る
		numbers = append(numbers, num) //配列に追加
		checkErr(err)                  //エラーチェック

	}
	for i := 0; i < n; i++ {
		go SleepWait(numbers[i], &answer, &wg) //非同期実行
	}
	wg.Wait()                  //全部終わったら
	fmt.Printf("%d\n", answer) //答えを表示
}
func checkErr(e error) { //エラーチェック用関数
	if e != nil {
		panic(e)
	}
}
func SleepWait(n int, sl *[]int, wg *sync.WaitGroup) {
	defer wg.Done()                                 //終わったらカウンタを減らす
	time.Sleep(time.Millisecond * time.Duration(n)) //数字ミリ秒待機
	*sl = append(*sl, n)                            //答えを示すスライスにくっ付ける
}
```
というわけでこれがコードの全体図です。
作る中で印象に残ったポイントをいくつか解説したいと思います。
### main関数
```（一部抜粋）main.go
func main() {
	var answer []int //ソート後のスライスがここに入る
	var numbers []int
	var n int
	var wg sync.WaitGroup
	print("ソートする要素数を入力してください（整数）：")
	_, err := fmt.Scan(&n) //整数の個数Nを受け取る
	checkErr(err)          //エラーチェック
	wg.Add(n)              //カウンタをN個増やす
	for i := 0; i < n; i++ {
		var num int
		fmt.Printf("%d個めの要素を入力してください（整数）：", i+1)
		_, err := fmt.Scan(&num)       //整数を1つ受け取る
		numbers = append(numbers, num) //配列に追加
		checkErr(err)                  //エラーチェック

	}
	for i := 0; i < n; i++ {
		go SleepWait(numbers[i], &answer, &wg) //非同期実行
	}
	wg.Wait()                  //全部終わったら
	fmt.Printf("%d\n", answer) //答えを表示
}
```
入力値の受け取りとゴルーチンの実行で二回forを回さないと入力待ちの間に時間がたってバグります（一敗）
他に方法がある可能性もありますが、私の知識ではこれしか思いつきませんでした。
関数側の変更を元の値にも反映したいので、スライスは**ポインタで渡します。**
また、関数の非同期実行時には`go`をつけ忘れないように気をつけましょう。（一敗）
WaitGroupのカウントに要素数を入れて待ちます。
全てのゴルーチンの実行が終わったら（WaitGroupのカウントが0になったら）、
結果を出力して終了です。

### SleepWait関数
```（一部抜粋）main.go
func SleepWait(n int, sl *[]int, wg *sync.WaitGroup) {
	defer wg.Done()                                 //終わったらカウンタを減らす
	time.Sleep(time.Millisecond * time.Duration(n)) //数字ミリ秒待機
	*sl = append(*sl, n)                            //答えを示すスライスにくっ付ける
}
```
まずは`defer`で関数終了後にWaitGroupのカウントを1つ減らすようにあらかじめ定義し、
nミリ秒経ったら渡されたスライスの最後にnをくっ付けて終了します。
# あとがきと感想
わかりづらく敬遠していたゴルーチンですが、これで少し理解が深まったと思いました。
こういった変なアルゴリズムはいくつかあるので、また機会があったら触ってみたいですね。
Cloudflare でサブドメインのサイト登録をするには

# 結論

>  [Subdomain setup · Cloudflare DNS docs](https://developers.cloudflare.com/dns/zone-setups/subdomain-setup/)

- 「Enterprise アカウント」が必要です。（Enterprise 契約のアカウントでのみ、解放しています。）

# サブドメインのサイト登録

- `subdomain.example.tld` のような形のサイト登録になります。

- `tld` に入るのは、例えば `jp`、`co.jp`、`com` など、ドメインを購入する際に選ぶようなものです。

# Cloudflare アカウントの種類

以下のように Enterprise と Standard アカウントの2種類があります。

- https://developers.cloudflare.com/api/operations/accounts-list-accounts

```shell
% http -A creds GET https://api.cloudflare.com/client/v4/accounts \
Content-Type:application/json | jq -r '.result[]|{name:.name, type:.type}'

{
  "name": "Enterprise Account",
  "type": "enterprise"
}
{
  "name": "Self-Serve Account",
  "type": "standard"
}
```

# Enterprise アカウントでの登録例

Enterprise 契約のアカウントがあれば、任意のプラン（Free/Pro/Biz/Ent） でサイト登録できます。

![image-20221227165554717](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20221227165554717.png)

![image-20221227170849173](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20221227170849173.png)

# Standard アカウントでの登録エラー

Standard アカウントでは、サブドメインのサイト登録は許可されていません。

![image-20221227170006538](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20221227170006538.png)

# 参考：アカウント種類別のユーザ権限管理

以下の記事で紹介されているユーザ権限管理は Enterprise アカウントでのみ提供されています。

- [Available roles · Cloudflare Fundamentals docs](https://developers.cloudflare.com/fundamentals/account-and-billing/account-setup/account-roles/)

- [Cloudflare アカウントのユーザ管理 - Qiita](https://qiita.com/khayama/items/ed58d825fa7b7bb1c413)

ただし、以下の公式ブログにある通り、まもなく Standard アカウントでも上記 RBAC 機能が提供される予定です。

> [Now all customers can share access to their Cloudflare account with Role Based Access Controls](https://blog.cloudflare.com/rbac-for-everyone/)
>
> Starting today, role based access controls (RBAC), and all of our additional roles will be rolled out to users on every plan! Whether you are a small business or even a single user, you can ensure that you can add users only to parts of Cloudflare you deem appropriate.

# 参考

- [IBM Cloud Internet Services (CIS) にサブドメインを登録する - Qiita](https://qiita.com/khayama/items/0d9e8d44a45d1f9a467e)
ISO27001の勉強

CIA（Confidentiality, Integrity, Availability）Information security

秘匿性（Confidentiality）、完全性（Integrity）、可用性（Availability）からなり、情報セキュリティに関する組織の方針のガイドとなる考えであること。
Availablity　必要なユーザのみアクセスできること。

XFrame
XFrame-Deny 画面の上に隠ボタンなどを置けない
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Frame-Options
Kotlinのnullチェックはif elseでやるな



Kotlinでnullチェックし、nullではない場合とnullの場合でif/elseを使うのは個人的にダサいと思うので共有。
やる場合はlet/?:runを使うべし！

これはダサい例

```kotlin

if (userIcon != null) {
    // userIconが存在 → ユーザー画像を設定
    Image(
        painter = painterResource(id = userIcon),
        contentDescription = null,
    )
} else {
    // userIconがnull → デフォルト画像を設定
    Image(
        painter = painterResource(id = R.drawable.default_image),
        contentDescription = null,
    )
}

```

if/elseの場合はuserIconがある場合に、再度userIconを渡している。。
Kotlin風のモダンな書き方

```kotlin

userIcon?.let {
    // userIconが存在 → ユーザー画像を設定
    Image(
        painter = painterResource(id = it),
        contentDescription = null,
    )
} ?: run {
    // userIconがnull → デフォルト画像を設定
    Image(
        painter = painterResource(id = R.drawable.default_image),
        contentDescription = null,
    )
}

```

nullではない場合に、let以降でitでuserIconを取れる。
Kotlin風な書き方でかけて良くなった！！

参考記事
https://ticktakclock.hatenablog.com/entry/2020/02/22/203408
Goのmoduleのtidyに失敗した時の話

`go mod tidy` をローカルで実行してモジュールのお掃除をしようとしたところ、以下のエラーになりました。

```
% go mod tidy
go: go.mod file indicates go 1.19, but maximum version supported by tidy is 1.18
```

英語弱者の私は、「 `go mod tidy` は廃止されたのかー。この間できたばかりの機能だったのになー。」などと勘違いしてしまいした。
違います。
原因は `go.mod` に記載のGoのバージョンよりもローカルのGoのバージョンが古いだけでした。
ローカルのGoのバージョンを1.19にしてエラーがでなくなりました！

記事にするほどの内容でもないですが、念の為、備忘録として残しておきます。