農薬散布管理機能の実装 #18
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
現在の状態(なぜOpenか)
未着手。仕様確定済み、実装待ち。
次にすること(Next Action)
農薬登録情報提供システムのAPI/スクレイピング方式を調査し、農薬マスタ設計を確定する
ブロック要因
農林水産省 農薬登録情報提供システムの利用可能なAPIまたはスクレイピング方式の詳細確認が必要
概要
散布した農薬を記録・管理し、農薬使用基準(製品ごと・有効成分ごとの使用回数制限)への適合確認と、特別栽培認証用の成分数集計を行う機能を追加する。
背景・目的
背景・目的
農薬は製品ごと・有効成分ごとに1シーズンの最大使用回数が定められている(農薬取締法)。複数の製品に同じ有効成分が含まれる場合、その有効成分の総使用回数として合算カウントする必要がある。また特別栽培認証用に「節減対象農薬の使用成分数」の報告義務がある。
散布対象(複数選択可)
使用回数カウントのルール
例)A剤(上限3回)+B剤(上限3回)、成分α総上限3回 → A剤2回+B剤1回まで
カウント対象外農薬(節減対象外)
特別栽培向け成分数集計
完了条件
関連
参考:使用回数カウント方法(croplifejapan.org/leaf14.pdf) / 福井県特別栽培カウントしない農薬リスト(pref.fukui.lg.jp/080.pdf) / 関連:apps/fertilizer(同様のマスタ+記録パターン)
農薬登録情報提供システム 調査結果
結論:公開APIなし、スクレイピングは技術的に可能
サイト構成
sch-app.azurewebsites.net)JSESSIONIDクッキー + CSRFトークン(フォームに埋め込み)検索〜詳細取得のフロー
詳細ページで取得できるデータ(スミチオン乳剤 登録番号4962 の例)
基本情報テーブル:
有効成分テーブル:
適用表(作物×病害虫ごとの使用条件):
→ 「本剤の使用回数」と「○○を含む農薬の総使用回数(有効成分上限)」の両方が取れる!
→ 使用時期別制限(種もみ・育苗箱・本田)もテキストとして取れる(正規表現でパース必要)
実装方針
推奨: Pythonスクレイパー(requests + BeautifulSoup)
このスクレイパーを Django management command または Windmill ワークフローとして実装する。
注意点・制約
ブロック解除:技術的には実装可能。次は apps/pesticide の設計に進める。
ドキュメントレビュー結果を共有します。実装前に以下の4点は仕様で閉じておいた方がよいです。
PesticideIngredient.max_total_usesの粒度だと、作物別の総使用回数上限を正しく持てません。document/18_マスタードキュメント_農薬散布管理編.mdでは成分に単一のmax_total_usesを持たせていますが、取得元は適用表の各行で、実際には作物名 × 成分名ごとに値が変わりえます。今の設計だと、後から取得した別作物の値で上書きされ、使用回数チェックが誤判定になります。PesticideIngredientLimitのような作物別テーブルに切り出すか、少なくともcrop_nameを含む形にしないと危ないです。農水省の作物名と内部
Cropの対応ルールが未定義で、上限照合が壊れる可能性が高いです。ドキュメントでは
crop_nameを農水省表記の文字列で保存し、集計APIはcrop_id指定で返す想定です。一方で既存システムの作物名は自由名で、資料中でも「米」「水稲」「稲」が混在しています。このままだとcrop_id=水稲に対してPesticideProductLimit.crop_name=稲が引けず、max_usesが空になるはずです。正規化テーブルか別名マッピング仕様が必要です。散布イベントが対象の“記号”しか持たないため、後日のマスタ変更で過去実績の集計結果が動いてしまいます。
現設計では、
groupはtarget_groupの文字列、field/groupの作物判定は年度の作付け計画から後追い解決です。既存実装ではField.group_nameは可変属性なので、グループ名変更や作付け変更が起きると、過去の散布記録の対象圃場や作物が再解釈されて、使用回数集計が retroactive に変わります。保存時に対象圃場を確定保存する中間テーブルか、少なくともcrop_snapshot/variety_snapshotの保持が必要です。有効成分集計の式に
is_active=True条件がなく、「その他成分」を誤って数える仕様になっています。is_active=Falseの成分も保持する設計なのに、総使用回数集計と特別栽培向け成分数集計の式にその除外条件がありません。仕様どおりならis_active=Trueを必須条件に入れるべきです。大枠の方向性はかなり整理されていて、特に「超過でも保存は止めない」「スクレイピング対象項目を明示している」あたりは実装しやすいです。上の4点だけ先に詰めれば、後戻りはかなり減ると思います。
仕様更新:作物別集計の明確化(農薬取締法対応)
背景
農薬取締法上、使用回数は作物単位で管理する義務がある。圃場グループ内に複数作物が混在する場合の回数カウントの扱いを仕様に明記した。
変更内容
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 「使用回数集計の仕組み」セクションを更新済み。
再レビュー結果です。変更の方向性はかなり良くなっていましたが、実装前に以下の3点は揃えておいた方が安全です。
同一イベント内で同じ有効成分を含む複数製品を使った場合、総使用回数を過少計上します。
ドキュメントでは「同一有効成分を含む複数製品は合算カウント」と定義していますが、集計式が
COUNT(DISTINCT SprayEvent.id)になっています。これだと 1 回の散布でMEP剤AとMEP剤Bを同時使用したケースが 2 回ではなく 1 回になります。1イベント=1回は製品単位には合っても、有効成分の「複数製品合算」とは衝突しています。SprayEventResolvedFieldを正源にしたはずなのに、設計判断がまだ旧仕様のままで矛盾しています。集計の正源は
SprayEventResolvedField.crop_name_snapshotに統一されていますが、設計判断にはまだ「作付け計画(Plan)と照合」と残っています。さらに削除したはずのcrop_snapshot/variety_snapshotも保持対象として書かれています。実装者がここを読むと旧設計に引っ張られます。製品使用回数も、同一イベント内の重複明細をどう扱うかが未定義で、式とモデルが噛み合っていません。
集計式は
COUNT(DISTINCT SprayEvent.id)ですが、明細モデルにはevent + pesticideの一意制約がありません。つまり同じ農薬を同一イベントに 2 行入れられる設計なのに、集計では 1 回に潰れます。仕様として「同一イベント内で同一農薬は1回しか登録できない」を明記して一意制約を持たせるか、重複明細の意味を定義した方が安全です。大筋ではかなり良くなっていて、特に「作物単位での法的管理」と「圃場ごとの正源を
SprayEventResolvedFieldに寄せた」方向は明快でした。上の3点だけ揃えると、実装時の解釈ぶれがかなり減ると思います。仕様修正:集計式の正確化・矛盾解消(Findings 3点対応)
1. 有効成分総使用回数の過少計上を修正
問題:
COUNT(DISTINCT SprayEvent.id)だと、1イベント内でMEP剤A+MEP剤Bを同時使用した場合でもMEP成分は1回になる。修正内容: 有効成分総使用回数の集計式を「製品」単位のカウントに変更。
1イベント内でMEP剤A+MEP剤Bを同時使用した場合 = MEP成分は2回カウント。
2. SprayEventPesticide に unique_together を追加
問題: event + pesticide の一意制約がなく、同一イベント内で同じ農薬の重複行が作れる設計なのに、集計では1回に潰れる矛盾。
修正内容:
unique_together = ['event', 'pesticide']を追加。3. 設計判断に残った旧仕様の記述を削除
問題: 設計判断に「作仕け計画(Plan)と照合」や削除済みの
crop_snapshot/variety_snapshotが残存していた。修正内容: 設計判断の記述を下記に更新。
ドキュメント更新先
document/18 「使用回数集計の仕組み」・「SprayEventPesticideモデル」・「設計判断」セクションを更新済み。
再レビュー結果の追記です。残っている論点は以下の2点です。
有効成分総使用回数の式は、同一作物の圃場が複数あるイベントで過大計上になります。
SprayEventResolvedFieldを圃場ごとに保存しつつ、有効成分集計をCOUNT(SprayEventPesticide)にしているため、水稲3筆を含む1イベントで MEP剤A を1回使っただけでも、crop_name_snapshot='水稲'との結合で3行に膨らみ、MEPが1回ではなく3回と数えられる恐れがあります。今のルールは「同一イベントの農薬が水稲に +1」と読めるので、ここはCOUNT(DISTINCT SprayEventPesticide.id)相当にする必要があります。ingredient_usage.max_total_usesの決め方が未定義で、複数製品をまたぐと上限表示が不定になります。PesticideIngredientLimitはpesticide + ingredient_name + crop_name単位で保持される一方、使用回数チェックAPIは成分ごとに単一のmax_total_usesを返す想定です。同じ年度・作物で同じ成分を含む複数製品を使った場合、どの製品由来の上限を返すのかが仕様にありません。最小値を採るのか、全件表示するのか、同一成分・同一作物では上限一致を前提にするのかを決めておかないと、実装者ごとに挙動が割れます。全体としてはかなり締まってきていて、前回までの大きな矛盾はだいぶ解消されています。残る論点はこの2点で、どちらも集計実装に直結するため、ここだけ先に固定すると安心です。
仕様修正:有効成分総使用回数のカウント式を元に戻す
背景
実務確認により、次の2点が明確になった。
変更内容
COUNT(DISTINCT SprayEvent.id)に戻す(製品使用回数と同じ式)PesticideIngredientLimitの最小値ロジックを削除(同一成分・同一作物なら製品間で上限値は共通)max_total_uses_ruleフィールドをAPIレスポンスから削除集計式の結論
製品使用回数・有効成分総使用回数の両方とも
COUNT(DISTINCT SprayEvent.id)に統一され、シンプルな実装になった。追加の確認事項です。
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を保存しようとしたらエラーにする」か、別テーブルへ正規化して値の一意性を担保する、のどちらかを仕様で固定しておくと安全です。