農薬散布管理機能の実装 #18

Open
opened 2026-04-09 00:33:45 +00:00 by akira · 8 comments
Owner

現在の状態(なぜOpenか)

未着手。仕様確定済み、実装待ち。

次にすること(Next Action)

農薬登録情報提供システムのAPI/スクレイピング方式を調査し、農薬マスタ設計を確定する

ブロック要因

農林水産省 農薬登録情報提供システムの利用可能なAPIまたはスクレイピング方式の詳細確認が必要

概要

散布した農薬を記録・管理し、農薬使用基準(製品ごと・有効成分ごとの使用回数制限)への適合確認と、特別栽培認証用の成分数集計を行う機能を追加する。

背景・目的

背景・目的

農薬は製品ごと・有効成分ごとに1シーズンの最大使用回数が定められている(農薬取締法)。複数の製品に同じ有効成分が含まれる場合、その有効成分の総使用回数として合算カウントする必要がある。また特別栽培認証用に「節減対象農薬の使用成分数」の報告義務がある。

散布対象(複数選択可)

  • 圃場(Field)
  • 圃場グループ(Field.group_name)
  • 作物(Crop)
  • 品種(Variety)

使用回数カウントのルール

  1. 製品ごとの使用回数 vs 登録情報の製品上限
  2. 有効成分ごとの総使用回数 vs 有効成分の上限(同成分含む全製品の合算)
    例)A剤(上限3回)+B剤(上限3回)、成分α総上限3回 → A剤2回+B剤1回まで
  3. 使用時期別カウント:育苗期・本圃期等で別カウントの場合あり

カウント対象外農薬(節減対象外)

  • 展着剤
  • 有機JAS別表2農薬(除虫菊乳剤・硫黄剤・天敵生物農薬等)
  • 化学合成でないと認められた農薬(カスガマイシン剤・ポリオキシン剤等)

特別栽培向け成分数集計

  • 節減対象農薬の有効成分が何種類使われたかをカウント(上限なし、報告用)

完了条件

  • 農薬名・登録番号・有効成分(複数)・使用回数上限を登録できる
  • 農林水産省登録情報から自動取得(失敗時は手動入力)
  • 展着剤フラグ・節減対象外フラグが設定できる
  • 散布日・対象種別・対象(複数)・農薬(複数、希釈倍率・使用量含む)を散布イベントとして記録できる
  • 年度x作物単位で製品ごとの使用回数・残回数を表示できる
  • 年度x作物単位で有効成分ごとの総使用回数・残回数を表示できる
  • 回数超過時にアラート表示できる
  • 特別栽培用:作物ごとの節減対象成分数集計を表示できる

関連

参考:使用回数カウント方法(croplifejapan.org/leaf14.pdf) / 福井県特別栽培カウントしない農薬リスト(pref.fukui.lg.jp/080.pdf) / 関連:apps/fertilizer(同様のマスタ+記録パターン)

## 現在の状態(なぜOpenか) 未着手。仕様確定済み、実装待ち。 ## 次にすること(Next Action) 農薬登録情報提供システムのAPI/スクレイピング方式を調査し、農薬マスタ設計を確定する ## ブロック要因 農林水産省 農薬登録情報提供システムの利用可能なAPIまたはスクレイピング方式の詳細確認が必要 ## 概要 散布した農薬を記録・管理し、農薬使用基準(製品ごと・有効成分ごとの使用回数制限)への適合確認と、特別栽培認証用の成分数集計を行う機能を追加する。 ## 背景・目的 ## 背景・目的 農薬は製品ごと・有効成分ごとに1シーズンの最大使用回数が定められている(農薬取締法)。複数の製品に同じ有効成分が含まれる場合、その有効成分の総使用回数として合算カウントする必要がある。また特別栽培認証用に「節減対象農薬の使用成分数」の報告義務がある。 ## 散布対象(複数選択可) - 圃場(Field) - 圃場グループ(Field.group_name) - 作物(Crop) - 品種(Variety) ## 使用回数カウントのルール 1. 製品ごとの使用回数 vs 登録情報の製品上限 2. 有効成分ごとの総使用回数 vs 有効成分の上限(同成分含む全製品の合算) 例)A剤(上限3回)+B剤(上限3回)、成分α総上限3回 → A剤2回+B剤1回まで 3. 使用時期別カウント:育苗期・本圃期等で別カウントの場合あり ## カウント対象外農薬(節減対象外) - 展着剤 - 有機JAS別表2農薬(除虫菊乳剤・硫黄剤・天敵生物農薬等) - 化学合成でないと認められた農薬(カスガマイシン剤・ポリオキシン剤等) ## 特別栽培向け成分数集計 - 節減対象農薬の有効成分が何種類使われたかをカウント(上限なし、報告用) ## 完了条件 - [ ] 農薬名・登録番号・有効成分(複数)・使用回数上限を登録できる - [ ] 農林水産省登録情報から自動取得(失敗時は手動入力) - [ ] 展着剤フラグ・節減対象外フラグが設定できる - [ ] 散布日・対象種別・対象(複数)・農薬(複数、希釈倍率・使用量含む)を散布イベントとして記録できる - [ ] 年度x作物単位で製品ごとの使用回数・残回数を表示できる - [ ] 年度x作物単位で有効成分ごとの総使用回数・残回数を表示できる - [ ] 回数超過時にアラート表示できる - [ ] 特別栽培用:作物ごとの節減対象成分数集計を表示できる ## 関連 参考:使用回数カウント方法(croplifejapan.org/leaf14.pdf) / 福井県特別栽培カウントしない農薬リスト(pref.fukui.lg.jp/080.pdf) / 関連:apps/fertilizer(同様のマスタ+記録パターン)
akira added the 機能 label 2026-04-09 00:33:45 +00:00
Author
Owner

農薬登録情報提供システム 調査結果

結論:公開APIなし、スクレイピングは技術的に可能


サイト構成

  • URL: https://pesticide.maff.go.jp/
  • バックエンド: ASP.NET / Azureホスティング(sch-app.azurewebsites.net
  • セッション管理: JSESSIONID クッキー + CSRFトークン(フォームに埋め込み)
  • robots.txt: 存在しない(404)

検索〜詳細取得のフロー

1. GET /agricultural-chemicals/name-search/
   → JSESSIONIDクッキー取得、CSRFトークン取得

2. POST /agricultural-chemicals/name-search
   パラメータ: _csrf=..., agriculturalChemicalsName=農薬名, agriculturalChemicalsType=
   → 302リダイレクト → /agricultural-chemicals/list

3. GET /agricultural-chemicals/list
   → 検索結果一覧(登録番号・農薬名・種類、詳細ページへのリンク)
   例: <a href="/agricultural-chemicals/details/4962">

4. GET /agricultural-chemicals/details/{id}
   → 詳細ページ(下記データを取得可能)

詳細ページで取得できるデータ(スミチオン乳剤 登録番号4962 の例)

基本情報テーブル:

  • 登録番号: 4962
  • 農薬の種類: MEP乳剤
  • 農薬の名称: 住化スミチオン乳剤
  • 用途: 殺虫剤 / 剤型: 乳剤 / 製剤毒性: 普

有効成分テーブル:

  • 成分名称・含有濃度(有効成分とその他成分を区別)

適用表(作物×病害虫ごとの使用条件):

作物名 適用病害虫名 希釈倍数 使用液量 使用時期 本剤の使用回数 使用方法 MEPを含む農薬の総使用回数
ニカメイチュウ第1世代 1000〜2000倍 60〜150L/10a 収穫21日前まで 2回以内 散布 3回以内(種もみへの処理は1回以内、育苗箱散布は1回以内、本田では2回以内)

「本剤の使用回数」と「○○を含む農薬の総使用回数(有効成分上限)」の両方が取れる!
→ 使用時期別制限(種もみ・育苗箱・本田)もテキストとして取れる(正規表現でパース必要)


実装方針

推奨: Pythonスクレイパー(requests + BeautifulSoup)

import requests
from bs4 import BeautifulSoup

session = requests.Session()
# 1. CSRFトークン取得
res = session.get('https://pesticide.maff.go.jp/agricultural-chemicals/name-search/')
csrf = BeautifulSoup(res.text, 'html.parser').find('input', {'name': '_csrf'})['value']

# 2. 名前で検索
res = session.post('/agricultural-chemicals/name-search', data={
    '_csrf': csrf,
    'agriculturalChemicalsName': '農薬名',
    'agriculturalChemicalsType': ''
}, allow_redirects=True)

# 3. 詳細ページからデータ取得
res = session.get('https://pesticide.maff.go.jp/agricultural-chemicals/details/{id}')
# → 有効成分、使用回数をパース

このスクレイパーを Django management command または Windmill ワークフローとして実装する。


注意点・制約

  • 利用規約: robots.txt なし、利用規約ページ(maff.go.jp/j/use/link.html)は農水省共通のもの。スクレイピング禁止の明記は見当たらないが、自動アクセスは常識的な頻度に抑える(農薬マスタ登録時のみ1件ずつ叩く)
  • セッション管理が必要: クッキー+CSRFトークンの維持が必須
  • 「総使用回数」の文字列パース: 「3回以内(種もみへの処理は1回以内、育苗箱散布は1回以内、本田では2回以内)」のような複合テキストは正規表現でパースが必要
  • 登録番号はシステム内部ID: 農薬の登録番号(公式)とは別にシステム内部IDが存在する点に注意

ブロック解除:技術的には実装可能。次は apps/pesticide の設計に進める。

## 農薬登録情報提供システム 調査結果 ### 結論:公開APIなし、スクレイピングは技術的に可能 --- ### サイト構成 - URL: https://pesticide.maff.go.jp/ - バックエンド: ASP.NET / Azureホスティング(`sch-app.azurewebsites.net`) - セッション管理: `JSESSIONID` クッキー + CSRFトークン(フォームに埋め込み) - robots.txt: 存在しない(404) ### 検索〜詳細取得のフロー ``` 1. GET /agricultural-chemicals/name-search/ → JSESSIONIDクッキー取得、CSRFトークン取得 2. POST /agricultural-chemicals/name-search パラメータ: _csrf=..., agriculturalChemicalsName=農薬名, agriculturalChemicalsType= → 302リダイレクト → /agricultural-chemicals/list 3. GET /agricultural-chemicals/list → 検索結果一覧(登録番号・農薬名・種類、詳細ページへのリンク) 例: <a href="/agricultural-chemicals/details/4962"> 4. GET /agricultural-chemicals/details/{id} → 詳細ページ(下記データを取得可能) ``` ### 詳細ページで取得できるデータ(スミチオン乳剤 登録番号4962 の例) **基本情報テーブル:** - 登録番号: 4962 - 農薬の種類: MEP乳剤 - 農薬の名称: 住化スミチオン乳剤 - 用途: 殺虫剤 / 剤型: 乳剤 / 製剤毒性: 普 **有効成分テーブル:** - 成分名称・含有濃度(有効成分とその他成分を区別) **適用表(作物×病害虫ごとの使用条件):** | 作物名 | 適用病害虫名 | 希釈倍数 | 使用液量 | 使用時期 | **本剤の使用回数** | 使用方法 | **MEPを含む農薬の総使用回数** | |---|---|---|---|---|---|---|---| | 稲 | ニカメイチュウ第1世代 | 1000〜2000倍 | 60〜150L/10a | 収穫21日前まで | 2回以内 | 散布 | 3回以内(種もみへの処理は1回以内、育苗箱散布は1回以内、本田では2回以内) | → **「本剤の使用回数」と「○○を含む農薬の総使用回数(有効成分上限)」の両方が取れる!** → 使用時期別制限(種もみ・育苗箱・本田)もテキストとして取れる(正規表現でパース必要) --- ### 実装方針 **推奨: Pythonスクレイパー(requests + BeautifulSoup)** ```python import requests from bs4 import BeautifulSoup session = requests.Session() # 1. CSRFトークン取得 res = session.get('https://pesticide.maff.go.jp/agricultural-chemicals/name-search/') csrf = BeautifulSoup(res.text, 'html.parser').find('input', {'name': '_csrf'})['value'] # 2. 名前で検索 res = session.post('/agricultural-chemicals/name-search', data={ '_csrf': csrf, 'agriculturalChemicalsName': '農薬名', 'agriculturalChemicalsType': '' }, allow_redirects=True) # 3. 詳細ページからデータ取得 res = session.get('https://pesticide.maff.go.jp/agricultural-chemicals/details/{id}') # → 有効成分、使用回数をパース ``` **このスクレイパーを Django management command または Windmill ワークフローとして実装する。** --- ### 注意点・制約 - **利用規約**: robots.txt なし、利用規約ページ(maff.go.jp/j/use/link.html)は農水省共通のもの。スクレイピング禁止の明記は見当たらないが、自動アクセスは常識的な頻度に抑える(農薬マスタ登録時のみ1件ずつ叩く) - **セッション管理が必要**: クッキー+CSRFトークンの維持が必須 - **「総使用回数」の文字列パース**: 「3回以内(種もみへの処理は1回以内、育苗箱散布は1回以内、本田では2回以内)」のような複合テキストは正規表現でパースが必要 - **登録番号はシステム内部ID**: 農薬の登録番号(公式)とは別にシステム内部IDが存在する点に注意 --- **ブロック解除:技術的には実装可能。次は apps/pesticide の設計に進める。**
Collaborator

ドキュメントレビュー結果を共有します。実装前に以下の4点は仕様で閉じておいた方がよいです。

  1. PesticideIngredient.max_total_uses の粒度だと、作物別の総使用回数上限を正しく持てません。
    document/18_マスタードキュメント_農薬散布管理編.md では成分に単一の max_total_uses を持たせていますが、取得元は適用表の各行で、実際には 作物名 × 成分名 ごとに値が変わりえます。今の設計だと、後から取得した別作物の値で上書きされ、使用回数チェックが誤判定になります。PesticideIngredientLimit のような作物別テーブルに切り出すか、少なくとも crop_name を含む形にしないと危ないです。

  2. 農水省の作物名と内部 Crop の対応ルールが未定義で、上限照合が壊れる可能性が高いです。
    ドキュメントでは crop_name を農水省表記の文字列で保存し、集計APIは crop_id 指定で返す想定です。一方で既存システムの作物名は自由名で、資料中でも「米」「水稲」「稲」が混在しています。このままだと crop_id=水稲 に対して PesticideProductLimit.crop_name=稲 が引けず、max_uses が空になるはずです。正規化テーブルか別名マッピング仕様が必要です。

  3. 散布イベントが対象の“記号”しか持たないため、後日のマスタ変更で過去実績の集計結果が動いてしまいます。
    現設計では、grouptarget_group の文字列、field/group の作物判定は年度の作付け計画から後追い解決です。既存実装では Field.group_name は可変属性なので、グループ名変更や作付け変更が起きると、過去の散布記録の対象圃場や作物が再解釈されて、使用回数集計が retroactive に変わります。保存時に対象圃場を確定保存する中間テーブルか、少なくとも crop_snapshot / variety_snapshot の保持が必要です。

  4. 有効成分集計の式に is_active=True 条件がなく、「その他成分」を誤って数える仕様になっています。
    is_active=False の成分も保持する設計なのに、総使用回数集計と特別栽培向け成分数集計の式にその除外条件がありません。仕様どおりなら is_active=True を必須条件に入れるべきです。

大枠の方向性はかなり整理されていて、特に「超過でも保存は止めない」「スクレイピング対象項目を明示している」あたりは実装しやすいです。上の4点だけ先に詰めれば、後戻りはかなり減ると思います。

ドキュメントレビュー結果を共有します。実装前に以下の4点は仕様で閉じておいた方がよいです。 1. `PesticideIngredient.max_total_uses` の粒度だと、作物別の総使用回数上限を正しく持てません。 `document/18_マスタードキュメント_農薬散布管理編.md` では成分に単一の `max_total_uses` を持たせていますが、取得元は適用表の各行で、実際には `作物名 × 成分名` ごとに値が変わりえます。今の設計だと、後から取得した別作物の値で上書きされ、使用回数チェックが誤判定になります。`PesticideIngredientLimit` のような作物別テーブルに切り出すか、少なくとも `crop_name` を含む形にしないと危ないです。 2. 農水省の作物名と内部 `Crop` の対応ルールが未定義で、上限照合が壊れる可能性が高いです。 ドキュメントでは `crop_name` を農水省表記の文字列で保存し、集計APIは `crop_id` 指定で返す想定です。一方で既存システムの作物名は自由名で、資料中でも「米」「水稲」「稲」が混在しています。このままだと `crop_id=水稲` に対して `PesticideProductLimit.crop_name=稲` が引けず、`max_uses` が空になるはずです。正規化テーブルか別名マッピング仕様が必要です。 3. 散布イベントが対象の“記号”しか持たないため、後日のマスタ変更で過去実績の集計結果が動いてしまいます。 現設計では、`group` は `target_group` の文字列、`field/group` の作物判定は年度の作付け計画から後追い解決です。既存実装では `Field.group_name` は可変属性なので、グループ名変更や作付け変更が起きると、過去の散布記録の対象圃場や作物が再解釈されて、使用回数集計が retroactive に変わります。保存時に対象圃場を確定保存する中間テーブルか、少なくとも `crop_snapshot` / `variety_snapshot` の保持が必要です。 4. 有効成分集計の式に `is_active=True` 条件がなく、「その他成分」を誤って数える仕様になっています。 `is_active=False` の成分も保持する設計なのに、総使用回数集計と特別栽培向け成分数集計の式にその除外条件がありません。仕様どおりなら `is_active=True` を必須条件に入れるべきです。 大枠の方向性はかなり整理されていて、特に「超過でも保存は止めない」「スクレイピング対象項目を明示している」あたりは実装しやすいです。上の4点だけ先に詰めれば、後戻りはかなり減ると思います。
Author
Owner

仕様更新:作物別集計の明確化(農薬取締法対応)

背景

農薬取締法上、使用回数は作物単位で管理する義務がある。圃場グループ内に複数作物が混在する場合の回数カウントの扱いを仕様に明記した。

変更内容

1. グループ内複数作物混在時の動作を明記

同一の散布イベント・散布農薬でも、圃場グループ内に「水稲」3筆・「大豆」1筆が混在する場合、その農薬は水稲の回数にも大豆の回数にも +1 カウントされる。

2. 集計の正源を SprayEventResolvedField.crop_name_snapshot に統一

SprayEvent にあった crop_snapshot / variety_snapshot フィールドは削除。作物情報は圃場ごとに SprayEventResolvedField.crop_name_snapshot に保持する。

3. 集計式の 1イベント=1回 原則を明示

COUNT(DISTINCT SprayEvent.id) で集計。グループ内に圃場が何筆あっても、1散布作業は1回とカウントする。

ドキュメント更新先

document/18 「使用回数集計の仕組み」セクションを更新済み。

## 仕様更新:作物別集計の明確化(農薬取締法対応) ### 背景 農薬取締法上、使用回数は作物単位で管理する義務がある。圃場グループ内に複数作物が混在する場合の回数カウントの扱いを仕様に明記した。 ### 変更内容 **1. グループ内複数作物混在時の動作を明記** 同一の散布イベント・散布農薬でも、圃場グループ内に「水稲」3筆・「大豆」1筆が混在する場合、その農薬は水稲の回数にも大豆の回数にも +1 カウントされる。 **2. 集計の正源を SprayEventResolvedField.crop_name_snapshot に統一** SprayEvent にあった crop_snapshot / variety_snapshot フィールドは削除。作物情報は圃場ごとに SprayEventResolvedField.crop_name_snapshot に保持する。 **3. 集計式の 1イベント=1回 原則を明示** COUNT(DISTINCT SprayEvent.id) で集計。グループ内に圃場が何筆あっても、1散布作業は1回とカウントする。 ### ドキュメント更新先 document/18 「使用回数集計の仕組み」セクションを更新済み。
Collaborator

再レビュー結果です。変更の方向性はかなり良くなっていましたが、実装前に以下の3点は揃えておいた方が安全です。

  1. 同一イベント内で同じ有効成分を含む複数製品を使った場合、総使用回数を過少計上します。
    ドキュメントでは「同一有効成分を含む複数製品は合算カウント」と定義していますが、集計式が COUNT(DISTINCT SprayEvent.id) になっています。これだと 1 回の散布で MEP剤AMEP剤B を同時使用したケースが 2 回ではなく 1 回になります。1イベント=1回 は製品単位には合っても、有効成分の「複数製品合算」とは衝突しています。

  2. SprayEventResolvedField を正源にしたはずなのに、設計判断がまだ旧仕様のままで矛盾しています。
    集計の正源は SprayEventResolvedField.crop_name_snapshot に統一されていますが、設計判断にはまだ「作付け計画(Plan)と照合」と残っています。さらに削除したはずの crop_snapshot / variety_snapshot も保持対象として書かれています。実装者がここを読むと旧設計に引っ張られます。

  3. 製品使用回数も、同一イベント内の重複明細をどう扱うかが未定義で、式とモデルが噛み合っていません。
    集計式は COUNT(DISTINCT SprayEvent.id) ですが、明細モデルには event + pesticide の一意制約がありません。つまり同じ農薬を同一イベントに 2 行入れられる設計なのに、集計では 1 回に潰れます。仕様として「同一イベント内で同一農薬は1回しか登録できない」を明記して一意制約を持たせるか、重複明細の意味を定義した方が安全です。

大筋ではかなり良くなっていて、特に「作物単位での法的管理」と「圃場ごとの正源を SprayEventResolvedField に寄せた」方向は明快でした。上の3点だけ揃えると、実装時の解釈ぶれがかなり減ると思います。

再レビュー結果です。変更の方向性はかなり良くなっていましたが、実装前に以下の3点は揃えておいた方が安全です。 1. 同一イベント内で同じ有効成分を含む複数製品を使った場合、総使用回数を過少計上します。 ドキュメントでは「同一有効成分を含む複数製品は合算カウント」と定義していますが、集計式が `COUNT(DISTINCT SprayEvent.id)` になっています。これだと 1 回の散布で `MEP剤A` と `MEP剤B` を同時使用したケースが 2 回ではなく 1 回になります。`1イベント=1回` は製品単位には合っても、有効成分の「複数製品合算」とは衝突しています。 2. `SprayEventResolvedField` を正源にしたはずなのに、設計判断がまだ旧仕様のままで矛盾しています。 集計の正源は `SprayEventResolvedField.crop_name_snapshot` に統一されていますが、設計判断にはまだ「作付け計画(Plan)と照合」と残っています。さらに削除したはずの `crop_snapshot` / `variety_snapshot` も保持対象として書かれています。実装者がここを読むと旧設計に引っ張られます。 3. 製品使用回数も、同一イベント内の重複明細をどう扱うかが未定義で、式とモデルが噛み合っていません。 集計式は `COUNT(DISTINCT SprayEvent.id)` ですが、明細モデルには `event + pesticide` の一意制約がありません。つまり同じ農薬を同一イベントに 2 行入れられる設計なのに、集計では 1 回に潰れます。仕様として「同一イベント内で同一農薬は1回しか登録できない」を明記して一意制約を持たせるか、重複明細の意味を定義した方が安全です。 大筋ではかなり良くなっていて、特に「作物単位での法的管理」と「圃場ごとの正源を `SprayEventResolvedField` に寄せた」方向は明快でした。上の3点だけ揃えると、実装時の解釈ぶれがかなり減ると思います。
Author
Owner

仕様修正:集計式の正確化・矛盾解消(Findings 3点対応)

1. 有効成分総使用回数の過少計上を修正

問題: COUNT(DISTINCT SprayEvent.id) だと、1イベント内でMEP剤A+MEP剤Bを同時使用した場合でもMEP成分は1回になる。

修正内容: 有効成分総使用回数の集計式を「製品」単位のカウントに変更。

有効成分総使用回数 = COUNT(SprayEventPesticide)
  where 当該成分を含む + 年度 x 作物フィルタ

1イベント内でMEP剤A+MEP剤Bを同時使用した場合 = MEP成分は2回カウント。

2. SprayEventPesticide に unique_together を追加

問題: event + pesticide の一意制約がなく、同一イベント内で同じ農薬の重複行が作れる設計なのに、集計では1回に潰れる矛盾。

修正内容: unique_together = ['event', 'pesticide'] を追加。

3. 設計判断に残った旧仕様の記述を削除

問題: 設計判断に「作仕け計画(Plan)と照合」や削除済みの crop_snapshot / variety_snapshot が残存していた。

修正内容: 設計判断の記述を下記に更新。

  • 作仕け計画は「保存時の解決に使うだけ」で集計の正源ではない
  • 集計の正源は SprayEventResolvedField.crop_name_snapshot のみ
  • SprayEvent 自体に作物情報は持たない

ドキュメント更新先

document/18 「使用回数集計の仕組み」・「SprayEventPesticideモデル」・「設計判断」セクションを更新済み。

## 仕様修正:集計式の正確化・矛盾解消(Findings 3点対応) ### 1. 有効成分総使用回数の過少計上を修正 **問題**: `COUNT(DISTINCT SprayEvent.id)` だと、1イベント内でMEP剤A+MEP剤Bを同時使用した場合でもMEP成分は1回になる。 **修正内容**: 有効成分総使用回数の集計式を「製品」単位のカウントに変更。 ``` 有効成分総使用回数 = COUNT(SprayEventPesticide) where 当該成分を含む + 年度 x 作物フィルタ ``` 1イベント内でMEP剤A+MEP剤Bを同時使用した場合 = MEP成分は2回カウント。 ### 2. SprayEventPesticide に unique_together を追加 **問題**: event + pesticide の一意制約がなく、同一イベント内で同じ農薬の重複行が作れる設計なのに、集計では1回に潰れる矛盾。 **修正内容**: `unique_together = ['event', 'pesticide']` を追加。 ### 3. 設計判断に残った旧仕様の記述を削除 **問題**: 設計判断に「作仕け計画(Plan)と照合」や削除済みの `crop_snapshot` / `variety_snapshot` が残存していた。 **修正内容**: 設計判断の記述を下記に更新。 - 作仕け計画は「保存時の解決に使うだけ」で集計の正源ではない - 集計の正源は SprayEventResolvedField.crop_name_snapshot のみ - SprayEvent 自体に作物情報は持たない ### ドキュメント更新先 document/18 「使用回数集計の仕組み」・「SprayEventPesticideモデル」・「設計判断」セクションを更新済み。
Collaborator

再レビュー結果の追記です。残っている論点は以下の2点です。

  1. 有効成分総使用回数の式は、同一作物の圃場が複数あるイベントで過大計上になります。
    SprayEventResolvedField を圃場ごとに保存しつつ、有効成分集計を COUNT(SprayEventPesticide) にしているため、水稲3筆を含む1イベントで MEP剤A を1回使っただけでも、crop_name_snapshot='水稲' との結合で3行に膨らみ、MEPが1回ではなく3回と数えられる恐れがあります。今のルールは「同一イベントの農薬が水稲に +1」と読めるので、ここは COUNT(DISTINCT SprayEventPesticide.id) 相当にする必要があります。

  2. ingredient_usage.max_total_uses の決め方が未定義で、複数製品をまたぐと上限表示が不定になります。
    PesticideIngredientLimitpesticide + ingredient_name + crop_name 単位で保持される一方、使用回数チェックAPIは成分ごとに単一の max_total_uses を返す想定です。同じ年度・作物で同じ成分を含む複数製品を使った場合、どの製品由来の上限を返すのかが仕様にありません。最小値を採るのか、全件表示するのか、同一成分・同一作物では上限一致を前提にするのかを決めておかないと、実装者ごとに挙動が割れます。

全体としてはかなり締まってきていて、前回までの大きな矛盾はだいぶ解消されています。残る論点はこの2点で、どちらも集計実装に直結するため、ここだけ先に固定すると安心です。

再レビュー結果の追記です。残っている論点は以下の2点です。 1. 有効成分総使用回数の式は、同一作物の圃場が複数あるイベントで過大計上になります。 `SprayEventResolvedField` を圃場ごとに保存しつつ、有効成分集計を `COUNT(SprayEventPesticide)` にしているため、水稲3筆を含む1イベントで MEP剤A を1回使っただけでも、`crop_name_snapshot='水稲'` との結合で3行に膨らみ、MEPが1回ではなく3回と数えられる恐れがあります。今のルールは「同一イベントの農薬が水稲に +1」と読めるので、ここは `COUNT(DISTINCT SprayEventPesticide.id)` 相当にする必要があります。 2. `ingredient_usage.max_total_uses` の決め方が未定義で、複数製品をまたぐと上限表示が不定になります。 `PesticideIngredientLimit` は `pesticide + ingredient_name + crop_name` 単位で保持される一方、使用回数チェックAPIは成分ごとに単一の `max_total_uses` を返す想定です。同じ年度・作物で同じ成分を含む複数製品を使った場合、どの製品由来の上限を返すのかが仕様にありません。最小値を採るのか、全件表示するのか、同一成分・同一作物では上限一致を前提にするのかを決めておかないと、実装者ごとに挙動が割れます。 全体としてはかなり締まってきていて、前回までの大きな矛盾はだいぶ解消されています。残る論点はこの2点で、どちらも集計実装に直結するため、ここだけ先に固定すると安心です。
Author
Owner

仕様修正:有効成分総使用回数のカウント式を元に戻す

背景

実務確認により、次の2点が明確になった。

  1. 同一成分の製品を同一イベントで施用することは実務上ない。そもそも同一成分薄剤を一度に施用するのは、その成分の規定倍率以上の薬剤を散布したことになるため。
  2. 他剤と混合散布しても「1回の散布=1回の使用」。農薬取締法の解釈上、混合剤で花剤が複数含まれていても「1回」とカウントされる。

変更内容

  • 有効成分総使用回数の集計式を COUNT(DISTINCT SprayEvent.id) に戻す(製品使用回数と同じ式)
  • PesticideIngredientLimit の最小値ロジックを削除(同一成分・同一作物なら製品間で上限値は共通)
  • max_total_uses_rule フィールドをAPIレスポンスから削除

集計式の結論

製品使用回数・有効成分総使用回数の両方とも COUNT(DISTINCT SprayEvent.id) に統一され、シンプルな実装になった。

## 仕様修正:有効成分総使用回数のカウント式を元に戻す ### 背景 実務確認により、次の2点が明確になった。 1. **同一成分の製品を同一イベントで施用することは実務上ない**。そもそも同一成分薄剤を一度に施用するのは、その成分の規定倍率以上の薬剤を散布したことになるため。 2. **他剤と混合散布しても「1回の散布=1回の使用」**。農薬取締法の解釈上、混合剤で花剤が複数含まれていても「1回」とカウントされる。 ### 変更内容 - 有効成分総使用回数の集計式を `COUNT(DISTINCT SprayEvent.id)` に戻す(製品使用回数と同じ式) - `PesticideIngredientLimit` の最小値ロジックを削除(同一成分・同一作物なら製品間で上限値は共通) - `max_total_uses_rule` フィールドをAPIレスポンスから削除 ### 集計式の結論 製品使用回数・有効成分総使用回数の両方とも `COUNT(DISTINCT SprayEvent.id)` に統一され、シンプルな実装になった。
Collaborator

追加の確認事項です。

PesticideIngredientLimit は現在 pesticide + ingredient_name + crop_name 単位で保持する設計ですが、仕様では「同一成分・同一作物なら製品が異なっても上限値は共通」としています。この前提自体は問題ありませんが、データモデル上はその一致を保証していないため、手動修正や取得揺れで同じ ingredient_name + crop_name に複数の max_total_uses が混在すると、ingredient_usage.max_total_uses の返却値が再び不定になります。

実装前に、少なくとも保存時バリデーションとして「同一 ingredient_name + crop_name の既存値と異なる max_total_uses を保存しようとしたらエラーにする」か、別テーブルへ正規化して値の一意性を担保する、のどちらかを仕様で固定しておくと安全です。

追加の確認事項です。 `PesticideIngredientLimit` は現在 `pesticide + ingredient_name + crop_name` 単位で保持する設計ですが、仕様では「同一成分・同一作物なら製品が異なっても上限値は共通」としています。この前提自体は問題ありませんが、データモデル上はその一致を保証していないため、手動修正や取得揺れで同じ `ingredient_name + crop_name` に複数の `max_total_uses` が混在すると、`ingredient_usage.max_total_uses` の返却値が再び不定になります。 実装前に、少なくとも保存時バリデーションとして「同一 `ingredient_name + crop_name` の既存値と異なる `max_total_uses` を保存しようとしたらエラーにする」か、別テーブルへ正規化して値の一意性を担保する、のどちらかを仕様で固定しておくと安全です。
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: akira/keinasystem#18