From 40b0de1d51091c5d28a93a206c0640b8d17eb54c Mon Sep 17 00:00:00 2001 From: gamer147 Date: Tue, 26 May 2026 13:42:59 -0400 Subject: [PATCH] refactor(bootstrap): migrate practice opponents to seed file Move /practice/info handling out of GlobalsImporter into a dedicated PracticeOpponentImporter that reads a normalized JSON seed file generated by data_dumps/extract/extract-practice-opponents.ps1. --- .../practice-info-2026-05-23.json | 1 - .../Data/seeds/practice-opponents.json | 1367 +++++++++++++++++ SVSim.Bootstrap/Importers/GlobalsImporter.cs | 47 - .../Importers/PracticeOpponentImporter.cs | 54 + .../Models/Seed/PracticeOpponentSeed.cs | 18 + SVSim.Bootstrap/Program.cs | 7 +- .../PracticeOpponentImporterTests.cs | 41 + .../Infrastructure/SVSimTestFactory.cs | 5 + SVSim.UnitTests/SVSim.UnitTests.csproj | 5 + 9 files changed, 1495 insertions(+), 50 deletions(-) delete mode 100644 SVSim.Bootstrap/Data/prod-captures/practice-info-2026-05-23.json create mode 100644 SVSim.Bootstrap/Data/seeds/practice-opponents.json create mode 100644 SVSim.Bootstrap/Importers/PracticeOpponentImporter.cs create mode 100644 SVSim.Bootstrap/Models/Seed/PracticeOpponentSeed.cs create mode 100644 SVSim.UnitTests/Importers/PracticeOpponentImporterTests.cs diff --git a/SVSim.Bootstrap/Data/prod-captures/practice-info-2026-05-23.json b/SVSim.Bootstrap/Data/prod-captures/practice-info-2026-05-23.json deleted file mode 100644 index 142f338..0000000 --- a/SVSim.Bootstrap/Data/prod-captures/practice-info-2026-05-23.json +++ /dev/null @@ -1 +0,0 @@ -{"data_headers":{"sid":"2aee92feffc2d16d494e5b45926a3f991779588133","short_udid":411054851,"viewer_id":906243102,"servertime":1779588133,"result_code":1},"data":[{"null":1,"practice_id":"1106","text_id":"Practice_1106","class_id":"1","chara_id":"1","degree_id":"-1","ai_deck_level":"106","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"1","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"1001","text_id":"Practice_1001","class_id":"1","chara_id":"1","degree_id":"400001","ai_deck_level":"0","ai_logic_level":"0","ai_max_life":"10","battle3dfield_id":"1","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"1002","text_id":"Practice_1002","class_id":"1","chara_id":"1","degree_id":"400002","ai_deck_level":"2","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"1","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"1003","text_id":"Practice_1003","class_id":"1","chara_id":"1","degree_id":"400003","ai_deck_level":"3","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"1","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"1004","text_id":"Practice_1004","class_id":"1","chara_id":"1","degree_id":"400003","ai_deck_level":"5","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"1","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"1005","text_id":"Practice_1005","class_id":"1","chara_id":"1","degree_id":"400004","ai_deck_level":"4","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"1","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"1006","text_id":"Practice_1006","class_id":"1","chara_id":"1","degree_id":"400004","ai_deck_level":"6","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"1","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"1007","text_id":"Practice_1007","class_id":"1","chara_id":"1","degree_id":"400004","ai_deck_level":"7","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"1","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"1101","text_id":"Practice_1101","class_id":"1","chara_id":"1","degree_id":"-1","ai_deck_level":"101","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"1","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"1102","text_id":"Practice_1102","class_id":"1","chara_id":"1","degree_id":"-1","ai_deck_level":"102","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"1","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"1103","text_id":"Practice_1103","class_id":"1","chara_id":"1","degree_id":"-1","ai_deck_level":"103","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"1","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"1104","text_id":"Practice_1104","class_id":"1","chara_id":"1","degree_id":"-1","ai_deck_level":"104","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"1","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"1105","text_id":"Practice_1105","class_id":"1","chara_id":"1","degree_id":"-1","ai_deck_level":"105","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"1","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"2106","text_id":"Practice_2106","class_id":"2","chara_id":"2","degree_id":"-1","ai_deck_level":"106","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"4","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"2001","text_id":"Practice_2001","class_id":"2","chara_id":"2","degree_id":"400001","ai_deck_level":"0","ai_logic_level":"0","ai_max_life":"10","battle3dfield_id":"4","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"2002","text_id":"Practice_2002","class_id":"2","chara_id":"2","degree_id":"400002","ai_deck_level":"2","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"4","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"2003","text_id":"Practice_2003","class_id":"2","chara_id":"2","degree_id":"400003","ai_deck_level":"3","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"4","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"2004","text_id":"Practice_2004","class_id":"2","chara_id":"2","degree_id":"400003","ai_deck_level":"5","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"4","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"2005","text_id":"Practice_2005","class_id":"2","chara_id":"2","degree_id":"400004","ai_deck_level":"4","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"4","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"2006","text_id":"Practice_2006","class_id":"2","chara_id":"2","degree_id":"400004","ai_deck_level":"6","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"4","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"2007","text_id":"Practice_2007","class_id":"2","chara_id":"2","degree_id":"400004","ai_deck_level":"7","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"4","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"2101","text_id":"Practice_2101","class_id":"2","chara_id":"2","degree_id":"-1","ai_deck_level":"101","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"4","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"2102","text_id":"Practice_2102","class_id":"2","chara_id":"2","degree_id":"-1","ai_deck_level":"102","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"4","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"2103","text_id":"Practice_2103","class_id":"2","chara_id":"2","degree_id":"-1","ai_deck_level":"103","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"4","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"2104","text_id":"Practice_2104","class_id":"2","chara_id":"2","degree_id":"-1","ai_deck_level":"104","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"4","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"2105","text_id":"Practice_2105","class_id":"2","chara_id":"2","degree_id":"-1","ai_deck_level":"105","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"4","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"3106","text_id":"Practice_3106","class_id":"3","chara_id":"3","degree_id":"-1","ai_deck_level":"106","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"7","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"3001","text_id":"Practice_3001","class_id":"3","chara_id":"3","degree_id":"400001","ai_deck_level":"0","ai_logic_level":"0","ai_max_life":"10","battle3dfield_id":"7","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"3002","text_id":"Practice_3002","class_id":"3","chara_id":"3","degree_id":"400002","ai_deck_level":"2","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"7","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"3003","text_id":"Practice_3003","class_id":"3","chara_id":"3","degree_id":"400003","ai_deck_level":"3","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"7","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"3004","text_id":"Practice_3004","class_id":"3","chara_id":"3","degree_id":"400003","ai_deck_level":"5","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"7","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"3005","text_id":"Practice_3005","class_id":"3","chara_id":"3","degree_id":"400004","ai_deck_level":"4","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"7","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"3006","text_id":"Practice_3006","class_id":"3","chara_id":"3","degree_id":"400004","ai_deck_level":"6","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"7","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"3007","text_id":"Practice_3007","class_id":"3","chara_id":"3","degree_id":"400004","ai_deck_level":"7","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"7","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"3101","text_id":"Practice_3101","class_id":"3","chara_id":"3","degree_id":"-1","ai_deck_level":"101","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"7","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"3102","text_id":"Practice_3102","class_id":"3","chara_id":"3","degree_id":"-1","ai_deck_level":"102","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"7","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"3103","text_id":"Practice_3103","class_id":"3","chara_id":"3","degree_id":"-1","ai_deck_level":"103","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"7","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"3104","text_id":"Practice_3104","class_id":"3","chara_id":"3","degree_id":"-1","ai_deck_level":"104","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"7","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"3105","text_id":"Practice_3105","class_id":"3","chara_id":"3","degree_id":"-1","ai_deck_level":"105","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"7","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"4107","text_id":"Practice_4107","class_id":"4","chara_id":"4","degree_id":"-1","ai_deck_level":"107","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"3","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"4001","text_id":"Practice_4001","class_id":"4","chara_id":"4","degree_id":"400001","ai_deck_level":"0","ai_logic_level":"0","ai_max_life":"10","battle3dfield_id":"3","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"4002","text_id":"Practice_4002","class_id":"4","chara_id":"4","degree_id":"400002","ai_deck_level":"2","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"3","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"4003","text_id":"Practice_4003","class_id":"4","chara_id":"4","degree_id":"400003","ai_deck_level":"3","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"3","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"4004","text_id":"Practice_4004","class_id":"4","chara_id":"4","degree_id":"400003","ai_deck_level":"5","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"3","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"4005","text_id":"Practice_4005","class_id":"4","chara_id":"4","degree_id":"400004","ai_deck_level":"4","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"3","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"4006","text_id":"Practice_4006","class_id":"4","chara_id":"4","degree_id":"400004","ai_deck_level":"6","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"3","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"4007","text_id":"Practice_4007","class_id":"4","chara_id":"4","degree_id":"400004","ai_deck_level":"7","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"3","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"4101","text_id":"Practice_4101","class_id":"4","chara_id":"4","degree_id":"-1","ai_deck_level":"101","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"3","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"4102","text_id":"Practice_4102","class_id":"4","chara_id":"4","degree_id":"-1","ai_deck_level":"102","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"3","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"4103","text_id":"Practice_4103","class_id":"4","chara_id":"4","degree_id":"-1","ai_deck_level":"103","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"3","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"4104","text_id":"Practice_4104","class_id":"4","chara_id":"4","degree_id":"-1","ai_deck_level":"104","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"3","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"4105","text_id":"Practice_4105","class_id":"4","chara_id":"4","degree_id":"-1","ai_deck_level":"105","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"3","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"4106","text_id":"Practice_4106","class_id":"4","chara_id":"4","degree_id":"-1","ai_deck_level":"106","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"3","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"5105","text_id":"Practice_5105","class_id":"5","chara_id":"5","degree_id":"-1","ai_deck_level":"105","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"6","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"5001","text_id":"Practice_5001","class_id":"5","chara_id":"5","degree_id":"400001","ai_deck_level":"0","ai_logic_level":"0","ai_max_life":"10","battle3dfield_id":"6","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"5002","text_id":"Practice_5002","class_id":"5","chara_id":"5","degree_id":"400002","ai_deck_level":"2","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"6","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"5003","text_id":"Practice_5003","class_id":"5","chara_id":"5","degree_id":"400003","ai_deck_level":"3","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"6","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"5004","text_id":"Practice_5004","class_id":"5","chara_id":"5","degree_id":"400003","ai_deck_level":"5","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"6","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"5005","text_id":"Practice_5005","class_id":"5","chara_id":"5","degree_id":"400004","ai_deck_level":"4","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"6","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"5006","text_id":"Practice_5006","class_id":"5","chara_id":"5","degree_id":"400004","ai_deck_level":"6","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"6","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"5007","text_id":"Practice_5007","class_id":"5","chara_id":"5","degree_id":"400004","ai_deck_level":"7","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"6","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"5101","text_id":"Practice_5101","class_id":"5","chara_id":"5","degree_id":"-1","ai_deck_level":"101","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"6","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"5102","text_id":"Practice_5102","class_id":"5","chara_id":"5","degree_id":"-1","ai_deck_level":"102","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"6","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"5103","text_id":"Practice_5103","class_id":"5","chara_id":"5","degree_id":"-1","ai_deck_level":"103","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"6","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"5104","text_id":"Practice_5104","class_id":"5","chara_id":"5","degree_id":"-1","ai_deck_level":"104","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"6","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6109","text_id":"Practice_6109","class_id":"6","chara_id":"6","degree_id":"-1","ai_deck_level":"109","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6001","text_id":"Practice_6001","class_id":"6","chara_id":"6","degree_id":"400001","ai_deck_level":"0","ai_logic_level":"0","ai_max_life":"10","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6002","text_id":"Practice_6002","class_id":"6","chara_id":"6","degree_id":"400002","ai_deck_level":"2","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6003","text_id":"Practice_6003","class_id":"6","chara_id":"6","degree_id":"400003","ai_deck_level":"3","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6004","text_id":"Practice_6004","class_id":"6","chara_id":"6","degree_id":"400003","ai_deck_level":"5","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6005","text_id":"Practice_6005","class_id":"6","chara_id":"6","degree_id":"400004","ai_deck_level":"4","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6006","text_id":"Practice_6006","class_id":"6","chara_id":"6","degree_id":"400004","ai_deck_level":"6","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6007","text_id":"Practice_6007","class_id":"6","chara_id":"6","degree_id":"400004","ai_deck_level":"7","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6101","text_id":"Practice_6101","class_id":"6","chara_id":"6","degree_id":"-1","ai_deck_level":"101","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6102","text_id":"Practice_6102","class_id":"6","chara_id":"6","degree_id":"-1","ai_deck_level":"102","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6103","text_id":"Practice_6103","class_id":"6","chara_id":"6","degree_id":"-1","ai_deck_level":"103","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6104","text_id":"Practice_6104","class_id":"6","chara_id":"6","degree_id":"-1","ai_deck_level":"104","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6105","text_id":"Practice_6105","class_id":"6","chara_id":"6","degree_id":"-1","ai_deck_level":"105","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6106","text_id":"Practice_6106","class_id":"6","chara_id":"6","degree_id":"-1","ai_deck_level":"106","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6107","text_id":"Practice_6107","class_id":"6","chara_id":"6","degree_id":"-1","ai_deck_level":"107","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"6108","text_id":"Practice_6108","class_id":"6","chara_id":"6","degree_id":"-1","ai_deck_level":"108","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"2","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"7105","text_id":"Practice_7105","class_id":"7","chara_id":"7","degree_id":"-1","ai_deck_level":"105","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"5","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"7001","text_id":"Practice_7001","class_id":"7","chara_id":"7","degree_id":"400001","ai_deck_level":"0","ai_logic_level":"0","ai_max_life":"10","battle3dfield_id":"5","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"7002","text_id":"Practice_7002","class_id":"7","chara_id":"7","degree_id":"400002","ai_deck_level":"2","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"5","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"7003","text_id":"Practice_7003","class_id":"7","chara_id":"7","degree_id":"400003","ai_deck_level":"3","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"5","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"7004","text_id":"Practice_7004","class_id":"7","chara_id":"7","degree_id":"400003","ai_deck_level":"5","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"5","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"7005","text_id":"Practice_7005","class_id":"7","chara_id":"7","degree_id":"400004","ai_deck_level":"4","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"5","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"7006","text_id":"Practice_7006","class_id":"7","chara_id":"7","degree_id":"400004","ai_deck_level":"6","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"5","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"7007","text_id":"Practice_7007","class_id":"7","chara_id":"7","degree_id":"400004","ai_deck_level":"7","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"5","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"7101","text_id":"Practice_7101","class_id":"7","chara_id":"7","degree_id":"-1","ai_deck_level":"101","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"5","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"7102","text_id":"Practice_7102","class_id":"7","chara_id":"7","degree_id":"-1","ai_deck_level":"102","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"5","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"7103","text_id":"Practice_7103","class_id":"7","chara_id":"7","degree_id":"-1","ai_deck_level":"103","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"5","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"7104","text_id":"Practice_7104","class_id":"7","chara_id":"7","degree_id":"-1","ai_deck_level":"104","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"5","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"8107","text_id":"Practice_8107","class_id":"8","chara_id":"8","degree_id":"-1","ai_deck_level":"107","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"18","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"8001","text_id":"Practice_8001","class_id":"8","chara_id":"8","degree_id":"400001","ai_deck_level":"0","ai_logic_level":"0","ai_max_life":"20","battle3dfield_id":"18","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"8002","text_id":"Practice_8002","class_id":"8","chara_id":"8","degree_id":"400002","ai_deck_level":"2","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"18","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"8003","text_id":"Practice_8003","class_id":"8","chara_id":"8","degree_id":"400003","ai_deck_level":"3","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"18","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"8005","text_id":"Practice_8005","class_id":"8","chara_id":"8","degree_id":"400004","ai_deck_level":"4","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"18","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"8006","text_id":"Practice_8006","class_id":"8","chara_id":"8","degree_id":"400004","ai_deck_level":"6","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"18","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"8101","text_id":"Practice_8101","class_id":"8","chara_id":"8","degree_id":"-1","ai_deck_level":"101","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"18","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"8102","text_id":"Practice_8102","class_id":"8","chara_id":"8","degree_id":"-1","ai_deck_level":"102","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"18","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"8103","text_id":"Practice_8103","class_id":"8","chara_id":"8","degree_id":"-1","ai_deck_level":"103","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"18","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"8104","text_id":"Practice_8104","class_id":"8","chara_id":"8","degree_id":"-1","ai_deck_level":"104","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"18","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"8105","text_id":"Practice_8105","class_id":"8","chara_id":"8","degree_id":"-1","ai_deck_level":"105","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"18","is_campaign_practice":false,"is_maintenance":false},{"null":1,"practice_id":"8106","text_id":"Practice_8106","class_id":"8","chara_id":"8","degree_id":"-1","ai_deck_level":"106","ai_logic_level":"2","ai_max_life":"20","battle3dfield_id":"18","is_campaign_practice":false,"is_maintenance":false}]} diff --git a/SVSim.Bootstrap/Data/seeds/practice-opponents.json b/SVSim.Bootstrap/Data/seeds/practice-opponents.json new file mode 100644 index 0000000..158f146 --- /dev/null +++ b/SVSim.Bootstrap/Data/seeds/practice-opponents.json @@ -0,0 +1,1367 @@ +[ + { + "practice_id": 1106, + "text_id": "Practice_1106", + "class_id": 1, + "chara_id": 1, + "degree_id": -1, + "ai_deck_level": 106, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "1", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 1001, + "text_id": "Practice_1001", + "class_id": 1, + "chara_id": 1, + "degree_id": 400001, + "ai_deck_level": 0, + "ai_logic_level": 0, + "ai_max_life": 10, + "battle3dfield_id": "1", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 1002, + "text_id": "Practice_1002", + "class_id": 1, + "chara_id": 1, + "degree_id": 400002, + "ai_deck_level": 2, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "1", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 1003, + "text_id": "Practice_1003", + "class_id": 1, + "chara_id": 1, + "degree_id": 400003, + "ai_deck_level": 3, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "1", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 1004, + "text_id": "Practice_1004", + "class_id": 1, + "chara_id": 1, + "degree_id": 400003, + "ai_deck_level": 5, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "1", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 1005, + "text_id": "Practice_1005", + "class_id": 1, + "chara_id": 1, + "degree_id": 400004, + "ai_deck_level": 4, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "1", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 1006, + "text_id": "Practice_1006", + "class_id": 1, + "chara_id": 1, + "degree_id": 400004, + "ai_deck_level": 6, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "1", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 1007, + "text_id": "Practice_1007", + "class_id": 1, + "chara_id": 1, + "degree_id": 400004, + "ai_deck_level": 7, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "1", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 1101, + "text_id": "Practice_1101", + "class_id": 1, + "chara_id": 1, + "degree_id": -1, + "ai_deck_level": 101, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "1", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 1102, + "text_id": "Practice_1102", + "class_id": 1, + "chara_id": 1, + "degree_id": -1, + "ai_deck_level": 102, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "1", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 1103, + "text_id": "Practice_1103", + "class_id": 1, + "chara_id": 1, + "degree_id": -1, + "ai_deck_level": 103, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "1", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 1104, + "text_id": "Practice_1104", + "class_id": 1, + "chara_id": 1, + "degree_id": -1, + "ai_deck_level": 104, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "1", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 1105, + "text_id": "Practice_1105", + "class_id": 1, + "chara_id": 1, + "degree_id": -1, + "ai_deck_level": 105, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "1", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 2106, + "text_id": "Practice_2106", + "class_id": 2, + "chara_id": 2, + "degree_id": -1, + "ai_deck_level": 106, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "4", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 2001, + "text_id": "Practice_2001", + "class_id": 2, + "chara_id": 2, + "degree_id": 400001, + "ai_deck_level": 0, + "ai_logic_level": 0, + "ai_max_life": 10, + "battle3dfield_id": "4", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 2002, + "text_id": "Practice_2002", + "class_id": 2, + "chara_id": 2, + "degree_id": 400002, + "ai_deck_level": 2, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "4", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 2003, + "text_id": "Practice_2003", + "class_id": 2, + "chara_id": 2, + "degree_id": 400003, + "ai_deck_level": 3, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "4", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 2004, + "text_id": "Practice_2004", + "class_id": 2, + "chara_id": 2, + "degree_id": 400003, + "ai_deck_level": 5, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "4", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 2005, + "text_id": "Practice_2005", + "class_id": 2, + "chara_id": 2, + "degree_id": 400004, + "ai_deck_level": 4, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "4", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 2006, + "text_id": "Practice_2006", + "class_id": 2, + "chara_id": 2, + "degree_id": 400004, + "ai_deck_level": 6, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "4", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 2007, + "text_id": "Practice_2007", + "class_id": 2, + "chara_id": 2, + "degree_id": 400004, + "ai_deck_level": 7, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "4", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 2101, + "text_id": "Practice_2101", + "class_id": 2, + "chara_id": 2, + "degree_id": -1, + "ai_deck_level": 101, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "4", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 2102, + "text_id": "Practice_2102", + "class_id": 2, + "chara_id": 2, + "degree_id": -1, + "ai_deck_level": 102, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "4", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 2103, + "text_id": "Practice_2103", + "class_id": 2, + "chara_id": 2, + "degree_id": -1, + "ai_deck_level": 103, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "4", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 2104, + "text_id": "Practice_2104", + "class_id": 2, + "chara_id": 2, + "degree_id": -1, + "ai_deck_level": 104, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "4", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 2105, + "text_id": "Practice_2105", + "class_id": 2, + "chara_id": 2, + "degree_id": -1, + "ai_deck_level": 105, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "4", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 3106, + "text_id": "Practice_3106", + "class_id": 3, + "chara_id": 3, + "degree_id": -1, + "ai_deck_level": 106, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "7", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 3001, + "text_id": "Practice_3001", + "class_id": 3, + "chara_id": 3, + "degree_id": 400001, + "ai_deck_level": 0, + "ai_logic_level": 0, + "ai_max_life": 10, + "battle3dfield_id": "7", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 3002, + "text_id": "Practice_3002", + "class_id": 3, + "chara_id": 3, + "degree_id": 400002, + "ai_deck_level": 2, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "7", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 3003, + "text_id": "Practice_3003", + "class_id": 3, + "chara_id": 3, + "degree_id": 400003, + "ai_deck_level": 3, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "7", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 3004, + "text_id": "Practice_3004", + "class_id": 3, + "chara_id": 3, + "degree_id": 400003, + "ai_deck_level": 5, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "7", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 3005, + "text_id": "Practice_3005", + "class_id": 3, + "chara_id": 3, + "degree_id": 400004, + "ai_deck_level": 4, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "7", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 3006, + "text_id": "Practice_3006", + "class_id": 3, + "chara_id": 3, + "degree_id": 400004, + "ai_deck_level": 6, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "7", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 3007, + "text_id": "Practice_3007", + "class_id": 3, + "chara_id": 3, + "degree_id": 400004, + "ai_deck_level": 7, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "7", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 3101, + "text_id": "Practice_3101", + "class_id": 3, + "chara_id": 3, + "degree_id": -1, + "ai_deck_level": 101, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "7", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 3102, + "text_id": "Practice_3102", + "class_id": 3, + "chara_id": 3, + "degree_id": -1, + "ai_deck_level": 102, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "7", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 3103, + "text_id": "Practice_3103", + "class_id": 3, + "chara_id": 3, + "degree_id": -1, + "ai_deck_level": 103, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "7", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 3104, + "text_id": "Practice_3104", + "class_id": 3, + "chara_id": 3, + "degree_id": -1, + "ai_deck_level": 104, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "7", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 3105, + "text_id": "Practice_3105", + "class_id": 3, + "chara_id": 3, + "degree_id": -1, + "ai_deck_level": 105, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "7", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 4107, + "text_id": "Practice_4107", + "class_id": 4, + "chara_id": 4, + "degree_id": -1, + "ai_deck_level": 107, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "3", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 4001, + "text_id": "Practice_4001", + "class_id": 4, + "chara_id": 4, + "degree_id": 400001, + "ai_deck_level": 0, + "ai_logic_level": 0, + "ai_max_life": 10, + "battle3dfield_id": "3", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 4002, + "text_id": "Practice_4002", + "class_id": 4, + "chara_id": 4, + "degree_id": 400002, + "ai_deck_level": 2, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "3", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 4003, + "text_id": "Practice_4003", + "class_id": 4, + "chara_id": 4, + "degree_id": 400003, + "ai_deck_level": 3, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "3", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 4004, + "text_id": "Practice_4004", + "class_id": 4, + "chara_id": 4, + "degree_id": 400003, + "ai_deck_level": 5, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "3", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 4005, + "text_id": "Practice_4005", + "class_id": 4, + "chara_id": 4, + "degree_id": 400004, + "ai_deck_level": 4, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "3", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 4006, + "text_id": "Practice_4006", + "class_id": 4, + "chara_id": 4, + "degree_id": 400004, + "ai_deck_level": 6, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "3", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 4007, + "text_id": "Practice_4007", + "class_id": 4, + "chara_id": 4, + "degree_id": 400004, + "ai_deck_level": 7, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "3", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 4101, + "text_id": "Practice_4101", + "class_id": 4, + "chara_id": 4, + "degree_id": -1, + "ai_deck_level": 101, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "3", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 4102, + "text_id": "Practice_4102", + "class_id": 4, + "chara_id": 4, + "degree_id": -1, + "ai_deck_level": 102, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "3", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 4103, + "text_id": "Practice_4103", + "class_id": 4, + "chara_id": 4, + "degree_id": -1, + "ai_deck_level": 103, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "3", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 4104, + "text_id": "Practice_4104", + "class_id": 4, + "chara_id": 4, + "degree_id": -1, + "ai_deck_level": 104, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "3", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 4105, + "text_id": "Practice_4105", + "class_id": 4, + "chara_id": 4, + "degree_id": -1, + "ai_deck_level": 105, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "3", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 4106, + "text_id": "Practice_4106", + "class_id": 4, + "chara_id": 4, + "degree_id": -1, + "ai_deck_level": 106, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "3", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 5105, + "text_id": "Practice_5105", + "class_id": 5, + "chara_id": 5, + "degree_id": -1, + "ai_deck_level": 105, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "6", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 5001, + "text_id": "Practice_5001", + "class_id": 5, + "chara_id": 5, + "degree_id": 400001, + "ai_deck_level": 0, + "ai_logic_level": 0, + "ai_max_life": 10, + "battle3dfield_id": "6", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 5002, + "text_id": "Practice_5002", + "class_id": 5, + "chara_id": 5, + "degree_id": 400002, + "ai_deck_level": 2, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "6", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 5003, + "text_id": "Practice_5003", + "class_id": 5, + "chara_id": 5, + "degree_id": 400003, + "ai_deck_level": 3, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "6", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 5004, + "text_id": "Practice_5004", + "class_id": 5, + "chara_id": 5, + "degree_id": 400003, + "ai_deck_level": 5, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "6", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 5005, + "text_id": "Practice_5005", + "class_id": 5, + "chara_id": 5, + "degree_id": 400004, + "ai_deck_level": 4, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "6", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 5006, + "text_id": "Practice_5006", + "class_id": 5, + "chara_id": 5, + "degree_id": 400004, + "ai_deck_level": 6, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "6", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 5007, + "text_id": "Practice_5007", + "class_id": 5, + "chara_id": 5, + "degree_id": 400004, + "ai_deck_level": 7, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "6", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 5101, + "text_id": "Practice_5101", + "class_id": 5, + "chara_id": 5, + "degree_id": -1, + "ai_deck_level": 101, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "6", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 5102, + "text_id": "Practice_5102", + "class_id": 5, + "chara_id": 5, + "degree_id": -1, + "ai_deck_level": 102, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "6", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 5103, + "text_id": "Practice_5103", + "class_id": 5, + "chara_id": 5, + "degree_id": -1, + "ai_deck_level": 103, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "6", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 5104, + "text_id": "Practice_5104", + "class_id": 5, + "chara_id": 5, + "degree_id": -1, + "ai_deck_level": 104, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "6", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6109, + "text_id": "Practice_6109", + "class_id": 6, + "chara_id": 6, + "degree_id": -1, + "ai_deck_level": 109, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6001, + "text_id": "Practice_6001", + "class_id": 6, + "chara_id": 6, + "degree_id": 400001, + "ai_deck_level": 0, + "ai_logic_level": 0, + "ai_max_life": 10, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6002, + "text_id": "Practice_6002", + "class_id": 6, + "chara_id": 6, + "degree_id": 400002, + "ai_deck_level": 2, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6003, + "text_id": "Practice_6003", + "class_id": 6, + "chara_id": 6, + "degree_id": 400003, + "ai_deck_level": 3, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6004, + "text_id": "Practice_6004", + "class_id": 6, + "chara_id": 6, + "degree_id": 400003, + "ai_deck_level": 5, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6005, + "text_id": "Practice_6005", + "class_id": 6, + "chara_id": 6, + "degree_id": 400004, + "ai_deck_level": 4, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6006, + "text_id": "Practice_6006", + "class_id": 6, + "chara_id": 6, + "degree_id": 400004, + "ai_deck_level": 6, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6007, + "text_id": "Practice_6007", + "class_id": 6, + "chara_id": 6, + "degree_id": 400004, + "ai_deck_level": 7, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6101, + "text_id": "Practice_6101", + "class_id": 6, + "chara_id": 6, + "degree_id": -1, + "ai_deck_level": 101, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6102, + "text_id": "Practice_6102", + "class_id": 6, + "chara_id": 6, + "degree_id": -1, + "ai_deck_level": 102, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6103, + "text_id": "Practice_6103", + "class_id": 6, + "chara_id": 6, + "degree_id": -1, + "ai_deck_level": 103, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6104, + "text_id": "Practice_6104", + "class_id": 6, + "chara_id": 6, + "degree_id": -1, + "ai_deck_level": 104, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6105, + "text_id": "Practice_6105", + "class_id": 6, + "chara_id": 6, + "degree_id": -1, + "ai_deck_level": 105, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6106, + "text_id": "Practice_6106", + "class_id": 6, + "chara_id": 6, + "degree_id": -1, + "ai_deck_level": 106, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6107, + "text_id": "Practice_6107", + "class_id": 6, + "chara_id": 6, + "degree_id": -1, + "ai_deck_level": 107, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 6108, + "text_id": "Practice_6108", + "class_id": 6, + "chara_id": 6, + "degree_id": -1, + "ai_deck_level": 108, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "2", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 7105, + "text_id": "Practice_7105", + "class_id": 7, + "chara_id": 7, + "degree_id": -1, + "ai_deck_level": 105, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "5", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 7001, + "text_id": "Practice_7001", + "class_id": 7, + "chara_id": 7, + "degree_id": 400001, + "ai_deck_level": 0, + "ai_logic_level": 0, + "ai_max_life": 10, + "battle3dfield_id": "5", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 7002, + "text_id": "Practice_7002", + "class_id": 7, + "chara_id": 7, + "degree_id": 400002, + "ai_deck_level": 2, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "5", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 7003, + "text_id": "Practice_7003", + "class_id": 7, + "chara_id": 7, + "degree_id": 400003, + "ai_deck_level": 3, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "5", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 7004, + "text_id": "Practice_7004", + "class_id": 7, + "chara_id": 7, + "degree_id": 400003, + "ai_deck_level": 5, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "5", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 7005, + "text_id": "Practice_7005", + "class_id": 7, + "chara_id": 7, + "degree_id": 400004, + "ai_deck_level": 4, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "5", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 7006, + "text_id": "Practice_7006", + "class_id": 7, + "chara_id": 7, + "degree_id": 400004, + "ai_deck_level": 6, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "5", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 7007, + "text_id": "Practice_7007", + "class_id": 7, + "chara_id": 7, + "degree_id": 400004, + "ai_deck_level": 7, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "5", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 7101, + "text_id": "Practice_7101", + "class_id": 7, + "chara_id": 7, + "degree_id": -1, + "ai_deck_level": 101, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "5", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 7102, + "text_id": "Practice_7102", + "class_id": 7, + "chara_id": 7, + "degree_id": -1, + "ai_deck_level": 102, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "5", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 7103, + "text_id": "Practice_7103", + "class_id": 7, + "chara_id": 7, + "degree_id": -1, + "ai_deck_level": 103, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "5", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 7104, + "text_id": "Practice_7104", + "class_id": 7, + "chara_id": 7, + "degree_id": -1, + "ai_deck_level": 104, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "5", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 8107, + "text_id": "Practice_8107", + "class_id": 8, + "chara_id": 8, + "degree_id": -1, + "ai_deck_level": 107, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "18", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 8001, + "text_id": "Practice_8001", + "class_id": 8, + "chara_id": 8, + "degree_id": 400001, + "ai_deck_level": 0, + "ai_logic_level": 0, + "ai_max_life": 20, + "battle3dfield_id": "18", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 8002, + "text_id": "Practice_8002", + "class_id": 8, + "chara_id": 8, + "degree_id": 400002, + "ai_deck_level": 2, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "18", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 8003, + "text_id": "Practice_8003", + "class_id": 8, + "chara_id": 8, + "degree_id": 400003, + "ai_deck_level": 3, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "18", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 8005, + "text_id": "Practice_8005", + "class_id": 8, + "chara_id": 8, + "degree_id": 400004, + "ai_deck_level": 4, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "18", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 8006, + "text_id": "Practice_8006", + "class_id": 8, + "chara_id": 8, + "degree_id": 400004, + "ai_deck_level": 6, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "18", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 8101, + "text_id": "Practice_8101", + "class_id": 8, + "chara_id": 8, + "degree_id": -1, + "ai_deck_level": 101, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "18", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 8102, + "text_id": "Practice_8102", + "class_id": 8, + "chara_id": 8, + "degree_id": -1, + "ai_deck_level": 102, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "18", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 8103, + "text_id": "Practice_8103", + "class_id": 8, + "chara_id": 8, + "degree_id": -1, + "ai_deck_level": 103, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "18", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 8104, + "text_id": "Practice_8104", + "class_id": 8, + "chara_id": 8, + "degree_id": -1, + "ai_deck_level": 104, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "18", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 8105, + "text_id": "Practice_8105", + "class_id": 8, + "chara_id": 8, + "degree_id": -1, + "ai_deck_level": 105, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "18", + "is_maintenance": false, + "is_campaign_practice": false + }, + { + "practice_id": 8106, + "text_id": "Practice_8106", + "class_id": 8, + "chara_id": 8, + "degree_id": -1, + "ai_deck_level": 106, + "ai_logic_level": 2, + "ai_max_life": 20, + "battle3dfield_id": "18", + "is_maintenance": false, + "is_campaign_practice": false + } +] diff --git a/SVSim.Bootstrap/Importers/GlobalsImporter.cs b/SVSim.Bootstrap/Importers/GlobalsImporter.cs index d18f1e9..8a17b5a 100644 --- a/SVSim.Bootstrap/Importers/GlobalsImporter.cs +++ b/SVSim.Bootstrap/Importers/GlobalsImporter.cs @@ -31,7 +31,6 @@ public class GlobalsImporter JsonElement? mypageIndex = LoadCapture(capturesDir, "mypage-index"); JsonElement? deckInfo = LoadCapture(capturesDir, "deck-info"); JsonElement? paymentItemList = LoadCapture(capturesDir, "payment-item-list"); - JsonElement? practiceInfo = LoadCapture(capturesDir, "practice-info"); JsonElement? packInfo = LoadCapture(capturesDir, "pack-info"); JsonElement? basicPuzzleInfo = LoadCapture(capturesDir, "basic-puzzle-info"); JsonElement? basicPuzzleMission = LoadCapture(capturesDir, "basic-puzzle-mission"); @@ -75,11 +74,6 @@ public class GlobalsImporter total += await ImportPaymentItems(context, paymentItemList.Value); } - if (practiceInfo.HasValue) - { - total += await ImportPracticeOpponents(context, practiceInfo.Value); - } - if (packInfo.HasValue) { total += await ImportPacks(context, packInfo.Value); @@ -879,47 +873,6 @@ public class GlobalsImporter return created + updated; } - // ---------- Practice Opponents ---------- - - /// - /// Capture is the full /practice/info envelope; data is a JSON ARRAY (not an object, - /// unlike most endpoints). Each row is one AI opponent row keyed on practice_id. Prod sends - /// numeric fields as strings — GetInt tolerates both. Rows present in the DB but missing - /// from the capture are LEFT INTACT (consistent with the rest of GlobalsImporter; partial - /// captures shouldn't silently delete entries). - /// - private async Task ImportPracticeOpponents(SVSimDbContext context, JsonElement practiceData) - { - if (practiceData.ValueKind != JsonValueKind.Array) return 0; - - var existing = await context.PracticeOpponents.ToDictionaryAsync(e => e.Id); - int created = 0, updated = 0; - - foreach (var row in practiceData.EnumerateArray()) - { - int practiceId = GetInt(row, "practice_id"); - if (practiceId == 0) continue; // malformed row - - var entry = existing.TryGetValue(practiceId, out var ex) ? ex : new PracticeOpponentEntry { Id = practiceId }; - entry.TextId = GetString(row, "text_id"); - entry.ClassId = GetInt(row, "class_id"); - entry.CharaId = GetInt(row, "chara_id"); - entry.DegreeId = GetInt(row, "degree_id"); - entry.AiDeckLevel = GetInt(row, "ai_deck_level"); - entry.AiLogicLevel = GetInt(row, "ai_logic_level"); - entry.AiMaxLife = GetInt(row, "ai_max_life"); - entry.Battle3dFieldId = GetString(row, "battle3dfield_id", "1"); - entry.IsMaintenance = GetBool(row, "is_maintenance"); - entry.IsCampaignPractice = GetBool(row, "is_campaign_practice"); - - if (ex is null) { context.PracticeOpponents.Add(entry); created++; } - else updated++; - } - - Console.WriteLine($"[GlobalsImporter] PracticeOpponents: +{created}/~{updated}"); - return created + updated; - } - // ---------- Basic Puzzle Groups + Puzzles ---------- /// diff --git a/SVSim.Bootstrap/Importers/PracticeOpponentImporter.cs b/SVSim.Bootstrap/Importers/PracticeOpponentImporter.cs new file mode 100644 index 0000000..81e6a1f --- /dev/null +++ b/SVSim.Bootstrap/Importers/PracticeOpponentImporter.cs @@ -0,0 +1,54 @@ +using Microsoft.EntityFrameworkCore; +using SVSim.Bootstrap.Models.Seed; +using SVSim.Database; +using SVSim.Database.Models; + +namespace SVSim.Bootstrap.Importers; + +/// +/// Idempotent upsert of practice opponents from seeds/practice-opponents.json. +/// Rows missing from the seed are LEFT INTACT (consistent with the previous import behavior; +/// a partial seed shouldn't silently delete entries). +/// +public class PracticeOpponentImporter +{ + public async Task ImportAsync(SVSimDbContext context, string seedDir) + { + string path = Path.Combine(seedDir, "practice-opponents.json"); + var seed = SeedLoader.LoadList(path); + if (seed.Count == 0) + { + Console.WriteLine("[PracticeOpponentImporter] No seed rows; skipping."); + return 0; + } + + var existing = await context.PracticeOpponents.ToDictionaryAsync(e => e.Id); + int created = 0, updated = 0; + + foreach (var s in seed) + { + if (s.PracticeId == 0) continue; + + var entry = existing.TryGetValue(s.PracticeId, out var ex) + ? ex : new PracticeOpponentEntry { Id = s.PracticeId }; + + entry.TextId = s.TextId; + entry.ClassId = s.ClassId; + entry.CharaId = s.CharaId; + entry.DegreeId = s.DegreeId; + entry.AiDeckLevel = s.AiDeckLevel; + entry.AiLogicLevel = s.AiLogicLevel; + entry.AiMaxLife = s.AiMaxLife; + entry.Battle3dFieldId = s.Battle3dFieldId; + entry.IsMaintenance = s.IsMaintenance; + entry.IsCampaignPractice = s.IsCampaignPractice; + + if (ex is null) { context.PracticeOpponents.Add(entry); created++; } + else updated++; + } + + await context.SaveChangesAsync(); + Console.WriteLine($"[PracticeOpponentImporter] +{created}/~{updated}"); + return created + updated; + } +} diff --git a/SVSim.Bootstrap/Models/Seed/PracticeOpponentSeed.cs b/SVSim.Bootstrap/Models/Seed/PracticeOpponentSeed.cs new file mode 100644 index 0000000..06f9959 --- /dev/null +++ b/SVSim.Bootstrap/Models/Seed/PracticeOpponentSeed.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; + +namespace SVSim.Bootstrap.Models.Seed; + +public sealed class PracticeOpponentSeed +{ + [JsonPropertyName("practice_id")] public int PracticeId { get; set; } + [JsonPropertyName("text_id")] public string TextId { get; set; } = ""; + [JsonPropertyName("class_id")] public int ClassId { get; set; } + [JsonPropertyName("chara_id")] public int CharaId { get; set; } + [JsonPropertyName("degree_id")] public int DegreeId { get; set; } + [JsonPropertyName("ai_deck_level")] public int AiDeckLevel { get; set; } + [JsonPropertyName("ai_logic_level")] public int AiLogicLevel { get; set; } + [JsonPropertyName("ai_max_life")] public int AiMaxLife { get; set; } + [JsonPropertyName("battle3dfield_id")] public string Battle3dFieldId { get; set; } = "1"; + [JsonPropertyName("is_maintenance")] public bool IsMaintenance { get; set; } + [JsonPropertyName("is_campaign_practice")] public bool IsCampaignPractice { get; set; } +} diff --git a/SVSim.Bootstrap/Program.cs b/SVSim.Bootstrap/Program.cs index e6f2487..6e2dd7a 100644 --- a/SVSim.Bootstrap/Program.cs +++ b/SVSim.Bootstrap/Program.cs @@ -76,6 +76,7 @@ public static class Program if (!opts.SkipGlobals) { await new GlobalsImporter().ImportAllAsync(context, opts.CapturesDir); + await new PracticeOpponentImporter().ImportAsync(context, opts.SeedDir); // BuildDeck pipeline: series CSV → catalog JSON → package CSV. Catalog must run after // series CSV (FK on products → series) and before package CSV (so the catalog-side @@ -152,6 +153,7 @@ public static class Program string refDir = referenceDataDir ?? shippedDataDir; string shippedStoryDir = Path.Combine(shippedDataDir, "story"); string storyDir = storyDataDir ?? shippedStoryDir; + string shippedSeedDir = Path.Combine(shippedDataDir, "seeds"); string connStr = connection ?? Environment.GetEnvironmentVariable("NPGSQL_CONNECTION") @@ -159,7 +161,7 @@ public static class Program return new BootstrapOptions( cardsFile, capturesDir, refDir, connStr, skipReference, skipCards, skipGlobals, - skipStory, storyDir); + skipStory, storyDir, shippedSeedDir); } private static string NextArg(string[] args, ref int i) @@ -204,5 +206,6 @@ public static class Program bool SkipCards, bool SkipGlobals, bool SkipStory, - string StoryDataDir); + string StoryDataDir, + string SeedDir); } diff --git a/SVSim.UnitTests/Importers/PracticeOpponentImporterTests.cs b/SVSim.UnitTests/Importers/PracticeOpponentImporterTests.cs new file mode 100644 index 0000000..3f1ed68 --- /dev/null +++ b/SVSim.UnitTests/Importers/PracticeOpponentImporterTests.cs @@ -0,0 +1,41 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using SVSim.Bootstrap.Importers; +using SVSim.Database; +using SVSim.UnitTests.Infrastructure; + +namespace SVSim.UnitTests.Importers; + +public class PracticeOpponentImporterTests +{ + private static string SeedDir => Path.Combine(AppContext.BaseDirectory, "Data", "seeds"); + + [Test] + public async Task Imports_opponents_from_seed_file() + { + using var factory = new SVSimTestFactory(); + using var scope = factory.Services.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + + await new PracticeOpponentImporter().ImportAsync(db, SeedDir); + + var opponents = await db.PracticeOpponents.OrderBy(p => p.Id).ToListAsync(); + Assert.That(opponents.Count, Is.GreaterThan(0), "seed file must contain opponents"); + Assert.That(opponents.All(o => o.ClassId >= 0), Is.True); + } + + [Test] + public async Task Is_idempotent_on_rerun() + { + using var factory = new SVSimTestFactory(); + using var scope = factory.Services.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + + await new PracticeOpponentImporter().ImportAsync(db, SeedDir); + int before = await db.PracticeOpponents.CountAsync(); + await new PracticeOpponentImporter().ImportAsync(db, SeedDir); + int after = await db.PracticeOpponents.CountAsync(); + + Assert.That(after, Is.EqualTo(before)); + } +} diff --git a/SVSim.UnitTests/Infrastructure/SVSimTestFactory.cs b/SVSim.UnitTests/Infrastructure/SVSimTestFactory.cs index c91c3d6..899883a 100644 --- a/SVSim.UnitTests/Infrastructure/SVSimTestFactory.cs +++ b/SVSim.UnitTests/Infrastructure/SVSimTestFactory.cs @@ -185,9 +185,14 @@ internal sealed class SVSimTestFactory : WebApplicationFactory public async Task SeedGlobalsAsync(string? capturesDir = null) { capturesDir ??= Path.Combine(AppContext.BaseDirectory, "Data", "prod-captures"); + string seedDir = Path.Combine(AppContext.BaseDirectory, "Data", "seeds"); using var scope = Services.CreateScope(); var ctx = scope.ServiceProvider.GetRequiredService(); await new GlobalsImporter().ImportAllAsync(ctx, capturesDir); + // Per-importer seed pipeline runs alongside GlobalsImporter during the migration. + // Wired here so SeedGlobalsAsync callers (e.g. PracticeControllerTests) still see + // practice-opponent rows after the corresponding block was lifted out of GlobalsImporter. + await new PracticeOpponentImporter().ImportAsync(ctx, seedDir); } /// Convenience: bake the X-Test-Viewer-Id header into a fresh client. diff --git a/SVSim.UnitTests/SVSim.UnitTests.csproj b/SVSim.UnitTests/SVSim.UnitTests.csproj index 4b5cbc4..244ede9 100644 --- a/SVSim.UnitTests/SVSim.UnitTests.csproj +++ b/SVSim.UnitTests/SVSim.UnitTests.csproj @@ -42,6 +42,11 @@ PreserveNewest + + + PreserveNewest +