min117の日記

自分にとっての自分自身という謎

pi42に作ったLaravel+mariaDBアプリのデータベースを Wrangler Cloudflare D1 に置き換える試行

WranglerでCloudflareにデプロイ

D1データベース本格的に使っていきたい。

完成イメージはこんな感じ。手元の年表アプリをCloudflareに移植する。

Wrangler(管理ツール)インストール

CLIでCloudflareを管理するツールwranglerをインストールする。

$ wrangler login

$ wrangler init がエラーになる(我が家のCPUが古すぎるのが原因)ので使わない

代わりに手作業で(wrangler initと)同じことをする。

wrangler init でできてしまったファイルを手動で削除して進める。

wranler.toml と src/index.js を↑で作成。

Cloudflareにデプロイ

デプロイすればURLが発行される。

$ wrangler deploy

$ curl 発行されたURL

hello world 大成功!

D1データベースを作る

$ wrangler d1 create nenpyo-db

テーブルを作ってD1に適用する。

$ wrangler d1 execute nenpyo-db --remote --file=schema.sql

$ nvim schema.sql

$ wrangler d1 execute nenpyo-db --remote --file=schema.sql

$ wrangler d1 execute nenpyo-db --remote --command="SELECT * FROM users;"

$ nvim src/index.js

$ wrangler deploy

おっしゃ動いた!

 

 

bitFlyer口座開設(ビットコイン1,000円もらえる)URL

 

 



 

 

 

 

 

 

その買うを、もっとハッピーに。|ハピタス

 

edit-commanline シェルのコマンドを「エディタで編集」してEnterで実行

edit-commandline

こんなことできるんか

youtu.be

最高だな。勉強になる。

 



 

 

bitFlyer口座開設(ビットコイン1,000円もらえる)URL

 

 



 

 

 

 

 

 

その買うを、もっとハッピーに。|ハピタス

 

macOS 🍎 Swiftでウィンドウアプリ(時計)アップロードAppもできた

アップロードアプリもSwiftだけでできた

最高すぎる。



 

 

bitFlyer口座開設(ビットコイン1,000円もらえる)URL

 

 



 

 

 

 

 

 

その買うを、もっとハッピーに。|ハピタス

 

eStatのAPIで取得したデータの読み取り知見ノウハウ📖myKINRIestatClaude00Flask_metaAPI🐍

====================================================
e-Stat API メタ情報読み取りノウハウ
myKINRIestatClaude00Flask_metaAPI読み取り知見ノウハウ.txt
作成: 2025-05  対象statsDataId: 0004006260(令和3年経済センサス)
====================================================

■ 背景・教訓
----------------------------------------------------------------------
e-Stat API の各統計表は、コード体系が統計ごとに全く異なる。
「cdArea='00000'」「cdTime='2021000000'」「cdTab='01'」のように
コードをハードコードで推測すると、STATUS=1(該当データなし)が返り
何も表示されない。

正しいアプローチ:
  「まずメタ情報APIを叩いて、そこに書いてある値をそのまま使う」


■ STEP 1: メタ情報APIを叩く
----------------------------------------------------------------------
エンドポイント:
  https://api.e-stat.go.jp/rest/3.0/app/json/getMetaInfo

必須パラメータ:
  appId       : アプリケーションID
  statsDataId : 統計表ID(URLの sid= の値)

例:
  GET https://api.e-stat.go.jp/rest/3.0/app/json/getMetaInfo
      ?appId=XXXX&statsDataId=0004006260

レスポンス構造:
  d['GET_META_INFO']['METADATA_INF']['CLASS_INF']['CLASS_OBJ']
  → リスト。各要素が1つの「次元」に対応する。

各次元の構造:
  {
    '@id':   'tab',        ← 次元ID(tab/cat01/cat02/cat03/area/time など)
    '@name': '表章項目',
    'CLASS': [             ← その次元のコード一覧(1件のときはdictになる点に注意)
      { '@code': '201-2021', '@name': '企業等数', '@level': '' },
      { '@code': '204-2021', '@name': '事業所数(海外支所を含む)', ... },
      ...
    ]
  }

注意: CLASS が1件のとき list ではなく dict で返ってくる。
      必ず以下のように正規化する:
        items = obj['CLASS']
        if isinstance(items, dict):
            items = [items]


■ STEP 2: コードの罠に注意する
----------------------------------------------------------------------
推測で書いたコードと実際のコードが全然違うことがある。
実際に遭遇した例(statsDataId=0004006260):

  次元      推測したコード   実際のコード
  -------   --------------   ----------------------
  tab       '01'             '201-2021'(名称+年度が入った形式)
  cat02     '1'              '0'=総数, '1'=法人, '2'=個人
  cat03     '0'              '00'(2桁ゼロパディング)
  cat01 level  1=大分類     '1'=特殊集計(AR/AB), '2'=大分類(A〜S), '4'=中分類
  cdArea    '00000'          実際は '00000' で合っていたが不要(単一値なら省略可)
  cdTime    '2021000000'     実際は '2021000000' で合っていたが不要(同上)

→ 単一値しか存在しない次元(area/time)は cdArea/cdTime を省略するのが安全。
  絞り込まなくても同じデータが返る。


■ STEP 3: デバッグ用エンドポイントを必ず作る
----------------------------------------------------------------------
Flaskアプリに以下のルートを追加しておく:

  @app.route('/api/meta_codes')
  def api_meta_codes():
      meta = get_meta()
      result = {}
      for k, items in meta.items():
          result[k] = [{'code': i.get('@code'), 'name': i.get('@name'),
                        'level': i.get('@level')} for i in items]
      return jsonify({'ok': True, 'meta': result})

アプリ起動後にブラウザで:
  http://192.168.3.11:49002/api/meta_codes

を開けば、全次元・全コード・名称・levelが一目で確認できる。
このJSONを見てから app.py のコードを書く。


■ STEP 4: データAPIに渡すコードはメタから取得した値をそのまま使う
----------------------------------------------------------------------
NG(ハードコード推測):
  params = {
      'cdTab':   '01',
      'cdCat02': '1',
      'cdCat03': '0',
      'cdArea':  '00000',
      'cdTime':  '2021000000',
  }

OK(メタ駆動):
  meta = get_meta()
  tab_code  = meta['tab'][0]['@code']       # '201-2021'
  area_code = meta['area'][0]['@code']      # '00000'
  time_code = meta['time'][0]['@code']      # '2021000000'
  # → これをそのまま params に渡す

セレクトボックスもメタ駆動にする(Jinja2例):
  {% for opt in tab_options %}
  <option value="{{ opt.code }}">{{ opt.name }}</option>
  {% endfor %}

  # Python側:
  tab_options = [{'code': i['@code'], 'name': i['@name']} for i in meta['tab']]


■ STEP 5: APIレスポンスのエラーを握り潰さない
----------------------------------------------------------------------
STATUS=0 が正常。STATUS=1 は「該当データなし」。

NG(エラーを握り潰す):
  def parse_stats(raw):
      try:
          ...
      except (KeyError, TypeError):
          return {}   ← 何が起きたか分からない

OK(詳細を返す):
  result_code = d['GET_STATS_DATA']['RESULT']['STATUS']
  if str(result_code) != '0':
      raise RuntimeError(
          f"STATUS={result_code}: {d['GET_STATS_DATA']['RESULT']['ERROR_MSG']}\n"
          f"パラメータ: {params}"
      )

フロントエンドのステータスバーにエラー内容をそのまま表示すると、
どのパラメータが問題だったか一目で分かる。


■ STEP 6: level フィルタの注意点
----------------------------------------------------------------------
cat01(産業分類)の @level は統計によって全く異なる。

今回の例(0004006260):
  @level='1' → AR(全産業), AB(農林漁業)など特殊集計コード
  @level='2' → A農業林業, B漁業... など標準的な大分類
  @level='4' → 01農業, 02林業... など中〜細分類

level でフィルタするときは:
  filtered = [item for item in cat01_items if int(item.get('@level', 99)) <= level]

ただし @level が空文字の場合(tab次元など)は int() で例外になるので:
  try:
      lv = int(item.get('@level', 99))
  except (ValueError, TypeError):
      lv = 99


■ 汎用メタ取得関数テンプレート
----------------------------------------------------------------------
_meta_cache = None

def get_meta():
    global _meta_cache
    if _meta_cache is not None:
        return _meta_cache
    r = requests.get('https://api.e-stat.go.jp/rest/3.0/app/json/getMetaInfo',
                     params={'appId': API_KEY, 'statsDataId': STATS_ID},
                     timeout=30)
    r.raise_for_status()
    d = r.json()
    status = d.get('GET_META_INFO', {}).get('RESULT', {}).get('STATUS', -1)
    if str(status) != '0':
        raise RuntimeError(f"MetaInfo APIエラー: {d['GET_META_INFO']['RESULT']}")
    objs = d['GET_META_INFO']['METADATA_INF']['CLASS_INF']['CLASS_OBJ']
    meta = {}
    for obj in objs:
        items = obj['CLASS']
        if isinstance(items, dict):
            items = [items]          # ← 必須: 1件のときdictになる
        meta[obj['@id']] = items
    _meta_cache = meta
    return meta


■ 別統計に応用するときのチェックリスト
----------------------------------------------------------------------
[ ] statsDataId を URL の sid= から取得
[ ] /api/meta_codes でコード一覧を確認
[ ] tab コードはメタから動的取得(ハードコードしない)
[ ] cat0N のコードはメタの @code をそのまま使う(ゼロパディングに注意)
[ ] @level の意味はその統計固有(1=大分類とは限らない)
[ ] 単一値の次元(area/time)はパラメータ省略でOK
[ ] STATUS != '0' を例外として投げ、フロントに表示する
[ ] CLASS が dict のとき list に正規化する処理を入れる

====================================================

 

[fedora myKINRIestatClaude05Flask]$ python3 - << 'EOF'
import requests, json
API_KEY = 'xxx'
r = requests.get('https://api.e-stat.go.jp/rest/3.0/app/json/getMetaInfo',
    params={'appId': API_KEY, 'statsDataId': '0003376209'})
d = r.json()
objs = d['GET_META_INFO']['METADATA_INF']['CLASS_INF']['CLASS_OBJ']
for obj in objs:
    items = obj['CLASS']
    if isinstance(items, dict): items = [items]
    print(f"\n=== {obj['@id']} ({obj['@name']}) ===")
    for i in items[:5]:
        print(f"  code={i['@code']}  level={i.get('@level','')}  name={i['@name']}")
    if len(items) > 5: print(f"  ... (+{len(items)-5}件)")
EOF

=== tab (表章項目) ===
  code=13  level=  name=寄与度
  code=14  level=  name=前期比
  code=15  level=  name=前期比・年率
  code=16  level=  name=寄与度・年率

=== cat01 (国内総生産_実質季節調整系列) ===
  code=100  level=1  name=国内総生産(支出側)
  code=120  level=2  name=民間最終消費支出
  code=130  level=3  name=民間最終消費支出_家計最終消費支出
  code=150  level=4  name=民間最終消費支出_家計最終消費支出_除く持ち家の帰属家賃
  code=180  level=2  name=民間住宅
  ... (+18件)

=== time (時間軸(四半期)) ===
  code=1980000406  level=3  name=1980年4~6月期
  code=1980000709  level=3  name=1980年7~9月期
  code=1980001012  level=3  name=1980年10~12月期
  code=1981000103  level=3  name=1981年1~3月期
  code=1981000406  level=3  name=1981年4~6月期
  ... (+112件)
[fedora myKINRIestatClaude05Flask]$ nvim
app.py     files.zip  templates/

 

 

 

 

bitFlyer口座開設(ビットコイン1,000円もらえる)URL

 

 



 

 

 

 

 

 

その買うを、もっとハッピーに。|ハピタス

 

GraphQLアプリを作る(fedora42)

GraphQL動いた

面白い。

ぐりぐり試す。

www.udemy.com

 

Claudeプロンプト

  • 【GraphQL入門】RESTに代わるモダンAPIのGraphQLでニュースアプリAPIを構築しながら基礎を学ぶ入門講座
  • 本講座を学習する流れ1:02

  • GraphQLって何?6:42
  • GraphQLとREST APIの違いって何?メリットは?8:18
  • Apolloって何?4:19

  • GraphQLサーバー用にプロジェクトを作成しよう2:47
  • ApolloServerを使ってローカルサーバを構築する準備をしよう6:56
  • リゾルバを定義してリゾルバについて理解しよう4:23
  • 実際にApolloでローカルサーバーを立ち上げよう4:00
  • Playgroundを実際に使ってGraphQLを体感してみよう3:00
  • スキーマ定義をHackerNews用に拡張してみよう5:34
  • 拡張したスキーマでGraphQLを叩いて確認してみよう4:05
  • リゾルバとは何か?

  • ミューテーション(Mutation)をスキーマとリゾルバで定義しよう6:21
  • 実際にMutationクエリを叩いてニュースを投稿してみよう4:07
  • スキーマ定義を別ファイルで管理してリファクタリングしよう7:05

  • Prismaって何?8:13
  • Prismaでデータベースの初期化から始めよう3:21
  • Prismaのスキーマ設定を行おう6:43
  • Prismaクライアントでデータベース操作をしてみよう8:50
  • データの永続化ができているかを確認しよう1:56

  • サーバーとPrismaを連携させてデータベースから情報を取り出そう3:13
  • contextをリゾルバで利用してデータベースにアクセスしよう4:16
  • 実際にGraphQLを叩いてデータベースに値を格納してみよう3:37
  • PrismaStudioを使ってデータベースの中身を確認しよう3:06

  • ユーザー認証のためのUserモデルを追加してみよう3:08
  • UserとLinkモデルの関係を明示的に示そう(外部キー設定)5:23
  • サインアップとログインのMutationを設定しよう5:04
  • リゾルバを管理するためのリファクタリングしよう3:47
  • Mutationリゾルバで新規登録のロジックを作成してみよう5:16
  • JWTを使って新規登録するユーザーをトークン化してみよう4:14
  • ユーザーログインのリゾルバを作成しよう5:07
  • ニュース投稿用のリゾルバを作成しよう3:37
  • ユーザーIDを取得するためのutil.jsファイル作成を始めよう5:05
  • ユーザートークンを複合する関数を生成してみよう4:35
  • ユーザーIDを全てのリゾルバで呼び出せる設定をしてみよう6:23
  • postリゾルバを完成させよう2:04
  • Linkリゾルバの実装を始めよう4:13
  • リゾルバ階層って何?parent引数の意味を理解しよう4:41
  • Userリゾルバの実装を始めよう1:47
  • サーバー側のリゾルバコードをリファクタリングしてみよう1:58
  • 正常に認証できるのかフローを確認してみよう6:24
  • ログインして実際にニュースを投稿してみよう3:50

  • サブスクリプション機能って何?3:13
  • ApolloサーバーでPubSub設定を始めてみよう3:58
  • Subscriptionリゾルバ(受信側)の設定を始めよう4:19
  • Mutationで送信側のpostリゾルバを修正しよう3:13
  • 実際にリアルタイム通信ができるか確認してみよう4:06
  • ニュースに投票できる機能を追加してみよう6:55
  • 投票用のGraphQLスキーマを作成してみよう3:23
  • 投票用リゾルバを作成してみよう5:59
  • 新しく投票するリゾルバの実装をはじめよう4:55
  • 【※次のレクチャーにおける訂正】1:04
  • VoteとLinkとUserモデルの新しい関係性を考慮しよう4:32
  • 受信側のSubscription設定を行おう2:17
  • リアルタイムで投票ができるのか確認してみよう6:23

  • ボーナスレクチャー ↑ この順番でGraphQLを習得したい。まずは自宅サーバにカンタンなアプリを作る(まずはシンプルな成功をする)体験をしたい。例えば以前作ったようなシンプルな体重管理アプリ(体重グラフを描く)とか。でもたしかGraphQLは「関係性」を描く?ものと聞いた気がするので、例えば仕事で集まった名刺情報を、自分なりの「関係性」のカテゴリで整理するような名刺管理アプリとかを将来はつくってみたい。そのシンプル版(小さな成功)できるならこのアプリからはじめてもいい。 手を動かして覚えたいから、コードを示しながら教えて。



 

bitFlyer口座開設(ビットコイン1,000円もらえる)URL

 

 



 

 

 

 

 

 

その買うを、もっとハッピーに。|ハピタス

 

最近、昼休みのネットが異常に遅い

ネット遅すぎ問題 2026/4-5月

・職場

・モバイル

・自宅

どれも平日の昼にそろって遅くなる。

たぶん原油危機が関係していると思う

 

職場

 回線は太いはずだけど昼に異常に遅くなる。人数が半端ない(アクセス者が多い)からとしても、これまでの統計的な動きからは大きく外れていると思う。年度が開けてからのこの2ヶ月が特に酷い

モバイル

 楽天モバイルが昼に完全に使いものにならなくなってる。通信速度が10Kbpsとか。メール確認すら危うい。古いHUAWEI(2013年頃)はかなり昔シンプルなAndroidで、通信速度が常に表示される。

自宅

 光回線、自宅では平日昼は使う人間がいないのに、たまたま平日に家にいた人間が、体感で遅い現象がここ数ヶ月で発生。自宅にまで影響が及んでいる

 

AIハードウェア:価格 vs 推論速度 / 消費電力

7B Q4_K_M モデル(Ollama / llama.cpp)、シングルユーザー推論

 
クイック切替
 
凡例
色:
通常
ファンレス(スロットリングあり)
EVO-X2

 

www.youtube.com

 

bitFlyer口座開設(ビットコイン1,000円もらえる)URL

 

 



 

 

 

 

 

 

その買うを、もっとハッピーに。|ハピタス

 

mac mini M4 も良いけど Ryzen AI 128GB も良さげ(ChartJS で比較グラフ)

今さら最強マシンを知る

なにこれ自宅サーバ刷新に最高じゃん。かなり良さげ。けど完全に品薄

www.youtube.com

ChartJSでグラフにする

2次元デカルト座標でグラフにする

AIハードウェア:価格 vs 推論速度 / 消費電力EVO-X2 追加

7B Q4_K_M モデル(Ollama / llama.cpp)、シングルユーザー推論

X 軸:
 


(人間は3次元の動物なので直感的に理解できるのは2次元まで)

 

しかし発売当初から+10万円くらい値上がりしてるっぽい。半導体不足ひどすぎ。今は買うときではないな。

 

 

 

AIハードウェア:価格 vs 推論速度 / 消費電力EVO-X2 追加

7B Q4_K_M モデル(Ollama / llama.cpp)、シングルユーザー推論

X 軸:
 


 

bitFlyer口座開設(ビットコイン1,000円もらえる)URL

 

 



 

 

 

 

 

 

その買うを、もっとハッピーに。|ハピタス