Skip to content

Port All_Filtered.json generator#420

Open
D1firehail wants to merge 3 commits into
WFCD:masterfrom
D1firehail:AllFilteredGenerator
Open

Port All_Filtered.json generator#420
D1firehail wants to merge 3 commits into
WFCD:masterfrom
D1firehail:AllFilteredGenerator

Conversation

@D1firehail
Copy link
Copy Markdown
Contributor

@D1firehail D1firehail commented Apr 12, 2026

What did you fix?

Ported code for generating All_Filtered.json from google apps script to C# cross-platform CLI tool.
Enables transition to the API host performing conversion, instead of having to periodically fetch from google drive.
(Does not do the same for Warframe.market data)


Reproduction steps

Building:

  1. (Make sure .NET 10 SDK is installed)
  2. Open terminal in AllFilteredGenerator folder
  3. run dotnet publish
  4. (Optional) move produced binary to location of your choice

Running:

  1. Execute binary via CLI.
    • First argument is path to All.json file of warframe-items repo (or file dump of corresponding web API with english language specified. I think it should be able to handle either)
    • Second argument is desired output path

Evidence/screenshot/link to line

Compare with live version from https://api.warframestat.us/wfinfo/filtered_items, Only difference should be time, errors in live version or updates yet to be processed by live version.

Considerations

  • Does this contain a new dependency? [No]
  • Does this introduce opinionated data formatting or manual data entry? [No]
  • Does this pr include updated data files in a separate commit that can be reverted for a clean code-only pr? [No]
  • Have I run the linter? [Yes]
  • Is is a bug fix, feature request, or enhancement? [Enhancement/Maintenance]

Summary by CodeRabbit

  • New Features

    • Added a command-line tool that parses game data to produce a compact JSON export with primes, relics grouped by era, vaulted status, computed rarities/ducats, merged independent components, and an errors+timestampped report.
  • Chores

    • Added publish profiles for Windows and Linux and registered the new project in the solution for build/publish workflows.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 56f99e9c-5a3e-412c-ae52-dafcf361a201

📥 Commits

Reviewing files that changed from the base of the PR and between 7ab0694 and a36e6db.

📒 Files selected for processing (4)
  • AllFilteredGenerator/JsonExtensions.cs
  • AllFilteredGenerator/Program.cs
  • AllFilteredGenerator/Properties/PublishProfiles/linux.pubxml
  • AllFilteredGenerator/Properties/PublishProfiles/windows.pubxml
✅ Files skipped from review due to trivial changes (2)
  • AllFilteredGenerator/Properties/PublishProfiles/windows.pubxml
  • AllFilteredGenerator/Properties/PublishProfiles/linux.pubxml
👮 Files not reviewed due to content moderation or server errors (2)
  • AllFilteredGenerator/JsonExtensions.cs
  • AllFilteredGenerator/Program.cs

📝 Walkthrough

Walkthrough

A new executable project, AllFilteredGenerator, was added. It parses an All.json array to extract Prime equipment and Relic data, applies parsing/synchronization logic across domain models, and emits a consolidated JSON output with errors, timestamp, relic groupings, equipment, and ignored items.

Changes

Cohort / File(s) Summary
Project Configuration
AllFilteredGenerator/AllFilteredGenerator.csproj, AllFilteredGenerator/Properties/PublishProfiles/linux.pubxml, AllFilteredGenerator/Properties/PublishProfiles/windows.pubxml
New .NET 10.0 executable project with ImplicitUsings/Nullable, PublishAot & InvariantGlobalization enabled; includes publish profiles for linux-x64 and win-x64 Release.
Domain Models
AllFilteredGenerator/IndependentPrimeComponent.cs, AllFilteredGenerator/ItemRelicInfo.cs, AllFilteredGenerator/PrimeComponent.cs, AllFilteredGenerator/PrimeEquipment.cs, AllFilteredGenerator/Relic.cs
Added classes modeling primes, independent components, relics, and item-relic info. Provide TryParse factories, synchronization methods (vault/drops), grouping by era, and JSON serialization helpers.
JSON Utilities
AllFilteredGenerator/JsonExtensions.cs
New JsonObject extension methods for safe retrieval (array/object/string/int/double) and GetRarity logic that interprets chance values and normalizes rarity.
Entry Point
AllFilteredGenerator/Program.cs
New CLI Main that reads input All.json, parses primes and relics, syncs relics/forma/independent parts, groups relics by era, and writes final JSON (errors, timestamp, relics, eqmt, ignored_items).
Solution
WFInfo.sln
Added the AllFilteredGenerator project to the solution with Debug/Release and platform configurations.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI (Program.Main)
    participant Parser as JSON Parser
    participant Prime as PrimeEquipment / Components
    participant Relic as Relic / Forma sources
    participant Sync as Synchronizers
    participant Output as File Output

    CLI->>Parser: Read All.json (JsonArray)
    Parser->>Prime: Parse Prime entries (TryParse)
    Prime->>Prime: Parse PrimeComponent & IndependentPrimeComponent
    Parser->>Relic: Parse Relic entries (TryParse)
    Relic->>Relic: Extract Forma drops (ItemRelicInfo)

    CLI->>Sync: SyncRelicsWithParts(primes, relics, errors)
    Sync->>Prime: Match parts -> relics, update part.Vaulted & prime.Vaulted
    Sync->>Relic: Populate DropsByRarity

    CLI->>Sync: SyncRelicsWithForma(formaInfo, relics, errors)
    Sync->>Relic: Match forma sources, add "Forma Blueprint" entries

    CLI->>Relic: DivideByEra(relics, errors)
    Relic->>Relic: Group relics by EraName and validate counts

    CLI->>Output: Build final JsonObject (errors, timestamp, relics, eqmt, ignored_items)
    Output->>Output: Serialize & write file
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped through JSON, parsing each part,

Primes and relics stitched into art.
Vaults aligned, drops sorted neat,
Output ready—what a fruitful feat!
🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Port All_Filtered.json generator' accurately and concisely summarizes the main change: porting the All_Filtered.json generator from Google Apps Script to C# CLI.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (7)
AllFilteredGenerator/PrimeComponent.cs (1)

25-62: TryParse always returns non-null despite nullable return type.

The method signature indicates it can return null (PrimeComponent?), but the implementation always returns a valid instance. Either:

  1. Remove the nullable annotation if the method should always succeed, or
  2. Return null when critical data is missing (e.g., when both ducats and primeSellingPrice are missing)
Option 2: Return null when ducat value is missing
             else
             {
-                savedDucatCount = 0;
-                errors.Add(objectName + " " + componentName + " IS MISSING ducats/primeSellingPrice");
+                errors.Add(objectName + " " + componentName + " IS MISSING ducats/primeSellingPrice");
+                return null;
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AllFilteredGenerator/PrimeComponent.cs` around lines 25 - 62, TryParse
currently always constructs and returns a PrimeComponent despite its nullable
signature; either remove the nullable or return null when critical ducat data is
absent—preferably implement Option 2: detect missing ducat/primeSellingPrice
(when parsedDucats and parsedPrimeSellingPrice are both null), log the error
(errors.Add) and return null instead of creating a PrimeComponent; update the
TryParse method so the branch where you set savedDucatCount to 0 and call
errors.Add yields an immediate return null, leaving PrimeComponent(...)
construction only in the success path.
AllFilteredGenerator/ItemRelicInfo.cs (1)

67-71: Redundant else block.

The else { continue; } is unnecessary since the loop naturally continues to the next iteration.

Proposed fix
                         result.Add(new ItemRelicInfo(eraName, nameInEra, rarity));

                     }
-                    else
-                    {
-                        continue;
-                    }

                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AllFilteredGenerator/ItemRelicInfo.cs` around lines 67 - 71, In the loop
inside ItemRelicInfo (remove the redundant else { continue; } block): delete the
else branch so the loop simply proceeds naturally when the if condition fails;
keep the code path for the if branch intact and ensure subsequent statements
remain at the same level (no further changes to method or variable names
required).
AllFilteredGenerator/Program.cs (2)

41-44: Inconsistent error handling: throws exception vs Environment.Exit.

Missing arguments and missing file trigger Environment.Exit(1), but invalid JSON structure throws an exception. Consider using a consistent approach.

Proposed fix for consistency
             if (json is null || json.GetValueKind() is not JsonValueKind.Array)
             {
-                throw new ArgumentException("All.json not a json array");
+                Console.WriteLine("All.json is not a valid JSON array");
+                Environment.Exit(1);
+                return; // Unreachable but satisfies compiler
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AllFilteredGenerator/Program.cs` around lines 41 - 44, The code currently
throws an ArgumentException when the parsed JSON is null or not an array (in the
block checking json is null or json.GetValueKind() is not JsonValueKind.Array),
which is inconsistent with other error handling that calls Environment.Exit(1)
for missing args/file; change this to use the same exit pattern: log or write a
clear error message (matching other messages for missing args/file) and call
Environment.Exit(1) instead of throwing, ensuring the message identifies the
file/condition and locating the change in the JSON validation block (the if
checking json is null || json.GetValueKind() is not JsonValueKind.Array).

177-189: Remove commented-out dead code.

Commented-out code without explanation adds noise. If this is kept for reference, consider adding a comment explaining why it's preserved, or remove it entirely since version control maintains history.

Proposed cleanup
                 else
                 {
-                    // old code, doesn't handle 2X Forma Blueprint
-                    //var category = elemObj.GetStringProperty("category");
-                    //if (category is not null && category.Equals("Misc", StringComparison.InvariantCultureIgnoreCase) && name.Equals("Forma"))
-                    //{
-                    //    var formaSources = ItemRelicInfo.TryParseDrops(elemObj);
-                    //    if (formaSources.Count > 0)
-                    //    {
-                    //        formaRelicInfo.AddRange(formaSources);
-                    //    }
-                    //}
+                    // Items that don't match Prime or Relic criteria are skipped
                 }

Or simply remove the else block entirely since it does nothing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AllFilteredGenerator/Program.cs` around lines 177 - 189, Remove the dead
commented-out code inside the else block that references
elemObj.GetStringProperty("category"), name.Equals("Forma"),
ItemRelicInfo.TryParseDrops and formaRelicInfo — either delete the entire empty
else block or replace it with a brief explanatory comment if you intentionally
preserved it for historical reasons; ensure no commented legacy logic remains in
Program.cs so only active code or a clear rationale comment remains.
AllFilteredGenerator/IndependentPrimeComponent.cs (1)

21-28: Method name TryParse is misleading since it never returns null.

Unlike PrimeComponent.TryParse which can return null, this method always returns a valid IndependentPrimeComponent. Consider renaming to Parse or changing the return type to IndependentPrimeComponent? if there are conditions under which parsing should fail.

Proposed rename
-        public static IndependentPrimeComponent TryParse(string componentName, JsonObject componentObj)
+        public static IndependentPrimeComponent Parse(string componentName, JsonObject componentObj)

Also update the call site in PrimeEquipment.cs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AllFilteredGenerator/IndependentPrimeComponent.cs` around lines 21 - 28, The
method IndependentPrimeComponent.TryParse always returns a non-null
IndependentPrimeComponent, so rename TryParse to Parse (change method name to
Parse in the IndependentPrimeComponent class) and update all call sites (notably
the usage in PrimeEquipment.cs) to call IndependentPrimeComponent.Parse; ensure
the method signature and any XML comments are updated accordingly and run a
solution-wide rename to catch any other references.
AllFilteredGenerator/PrimeEquipment.cs (1)

29-45: Pre-index prime names before syncing independent parts.

This loop does a full primes.FirstOrDefault(...) per independent part. Building one case-insensitive map once will reduce repeated scans and simplify the method.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AllFilteredGenerator/PrimeEquipment.cs` around lines 29 - 45, In
SyncIndependentParts, avoid repeated linear scans by prebuilding a
case-insensitive lookup (e.g., Dictionary<string, PrimeEquipment> using
StringComparer.InvariantCultureIgnoreCase) mapping prime.Name to the
PrimeEquipment, then replace primes.FirstOrDefault(...) with a
dictionary.TryGetValue on that map; when a match is found preserve the existing
logic (set part.Vaulted = matchingItem.Vaulted and prime.Vaulted |=
matchingItem.Vaulted) and when not found add the error as before.
AllFilteredGenerator/Relic.cs (1)

26-36: Use a keyed relic index for both sync passes.

Both methods repeatedly scan relics with FirstOrDefault. A shared dictionary keyed by (EraName, NameInEra) (case-insensitive) will reduce lookup overhead and centralize matching logic.

Also applies to: 63-68

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AllFilteredGenerator/Relic.cs` around lines 26 - 36, Replace repeated
FirstOrDefault scans over the relics list with a single case-insensitive lookup
dictionary built once at the start of SyncRelicsWithParts: create a Dictionary
keyed by a combined key (e.g. $"{EraName}|{NameInEra}".ToLowerInvariant()) from
the relics collection and use it to find matches for part.DroppingRelics instead
of relics.FirstOrDefault; apply the same dictionary-based lookup to the other
sync pass referenced around lines 63-68 so both SyncRelicsWithParts and the
counterpart use the shared keyed index for matchingRelic lookups to centralize
matching logic and improve performance.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@AllFilteredGenerator/JsonExtensions.cs`:
- Around line 81-92: The threshold check is currently done against a rounded int
(chancePercent) which loses precision; update the logic in JsonExtensions.cs
(the block that computes chancePercent from chance.Value and checks
rarity.Equals("Uncommon", ...)) to perform the > 11 comparison against the
unrounded value instead—i.e., compute the multiplier (100 when chance < 1,
otherwise 1), test if chance.Value * multiplier > 11 for the "Uncommon" branch,
and only then round/assign chancePercent for downstream use so values like 11.2
are correctly handled.
- Around line 39-67: The parsing in GetIntProperty and GetDoubleProperty uses
culture-sensitive TryParse overloads; update both to use the IFormatProvider
overloads with CultureInfo.InvariantCulture (and appropriate NumberStyles:
NumberStyles.Integer for GetIntProperty and NumberStyles.Float |
NumberStyles.AllowThousands for GetDoubleProperty) when calling int.TryParse and
double.TryParse on propNode.ToString(), so JSON numeric strings parse
deterministically regardless of host locale.

In `@AllFilteredGenerator/Program.cs`:
- Around line 32-36: The error message in the Program.cs file's missing-file
check prints the wrong variable; update the Console.WriteLine inside the if
(!File.Exists(allJsonPath)) block to reference allJsonPath (and include a space
after the colon) instead of savePath so the user sees the actual missing file
path; locate the if block that checks File.Exists(allJsonPath) and change the
string concatenation to use allJsonPath in the Console.WriteLine call.

In `@AllFilteredGenerator/Properties/PublishProfiles/linux.pubxml`:
- Around line 7-10: Update the publish profile so its TargetFramework matches
the project target (change the TargetFramework element in the linux.pubxml from
net8.0 to net10.0); locate the linux.pubxml publish profile and modify the
<TargetFramework> element (and any related PublishDir paths that embed net8.0,
e.g., the <PublishDir> value) so they consistently reference net10.0 to avoid
publish/runtime mismatches when building AllFilteredGenerator.

In `@AllFilteredGenerator/Properties/PublishProfiles/windows.pubxml`:
- Around line 7-10: The windows publish profile's TargetFramework element
currently reads net8.0 but the project targets net10.0; update the
<TargetFramework> value in
AllFilteredGenerator/Properties/PublishProfiles/windows.pubxml (the
TargetFramework XML element in that file) to net10.0 so the publish profile
matches the project target, and verify related publish paths
(PublishDir/PublishProtocol/_TargetId) remain correct for the win-x64 publish.

---

Nitpick comments:
In `@AllFilteredGenerator/IndependentPrimeComponent.cs`:
- Around line 21-28: The method IndependentPrimeComponent.TryParse always
returns a non-null IndependentPrimeComponent, so rename TryParse to Parse
(change method name to Parse in the IndependentPrimeComponent class) and update
all call sites (notably the usage in PrimeEquipment.cs) to call
IndependentPrimeComponent.Parse; ensure the method signature and any XML
comments are updated accordingly and run a solution-wide rename to catch any
other references.

In `@AllFilteredGenerator/ItemRelicInfo.cs`:
- Around line 67-71: In the loop inside ItemRelicInfo (remove the redundant else
{ continue; } block): delete the else branch so the loop simply proceeds
naturally when the if condition fails; keep the code path for the if branch
intact and ensure subsequent statements remain at the same level (no further
changes to method or variable names required).

In `@AllFilteredGenerator/PrimeComponent.cs`:
- Around line 25-62: TryParse currently always constructs and returns a
PrimeComponent despite its nullable signature; either remove the nullable or
return null when critical ducat data is absent—preferably implement Option 2:
detect missing ducat/primeSellingPrice (when parsedDucats and
parsedPrimeSellingPrice are both null), log the error (errors.Add) and return
null instead of creating a PrimeComponent; update the TryParse method so the
branch where you set savedDucatCount to 0 and call errors.Add yields an
immediate return null, leaving PrimeComponent(...) construction only in the
success path.

In `@AllFilteredGenerator/PrimeEquipment.cs`:
- Around line 29-45: In SyncIndependentParts, avoid repeated linear scans by
prebuilding a case-insensitive lookup (e.g., Dictionary<string, PrimeEquipment>
using StringComparer.InvariantCultureIgnoreCase) mapping prime.Name to the
PrimeEquipment, then replace primes.FirstOrDefault(...) with a
dictionary.TryGetValue on that map; when a match is found preserve the existing
logic (set part.Vaulted = matchingItem.Vaulted and prime.Vaulted |=
matchingItem.Vaulted) and when not found add the error as before.

In `@AllFilteredGenerator/Program.cs`:
- Around line 41-44: The code currently throws an ArgumentException when the
parsed JSON is null or not an array (in the block checking json is null or
json.GetValueKind() is not JsonValueKind.Array), which is inconsistent with
other error handling that calls Environment.Exit(1) for missing args/file;
change this to use the same exit pattern: log or write a clear error message
(matching other messages for missing args/file) and call Environment.Exit(1)
instead of throwing, ensuring the message identifies the file/condition and
locating the change in the JSON validation block (the if checking json is null
|| json.GetValueKind() is not JsonValueKind.Array).
- Around line 177-189: Remove the dead commented-out code inside the else block
that references elemObj.GetStringProperty("category"), name.Equals("Forma"),
ItemRelicInfo.TryParseDrops and formaRelicInfo — either delete the entire empty
else block or replace it with a brief explanatory comment if you intentionally
preserved it for historical reasons; ensure no commented legacy logic remains in
Program.cs so only active code or a clear rationale comment remains.

In `@AllFilteredGenerator/Relic.cs`:
- Around line 26-36: Replace repeated FirstOrDefault scans over the relics list
with a single case-insensitive lookup dictionary built once at the start of
SyncRelicsWithParts: create a Dictionary keyed by a combined key (e.g.
$"{EraName}|{NameInEra}".ToLowerInvariant()) from the relics collection and use
it to find matches for part.DroppingRelics instead of relics.FirstOrDefault;
apply the same dictionary-based lookup to the other sync pass referenced around
lines 63-68 so both SyncRelicsWithParts and the counterpart use the shared keyed
index for matchingRelic lookups to centralize matching logic and improve
performance.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 90860bac-4f4f-439f-a992-ff797673f04b

📥 Commits

Reviewing files that changed from the base of the PR and between b167a2c and 7ab0694.

📒 Files selected for processing (11)
  • AllFilteredGenerator/AllFilteredGenerator.csproj
  • AllFilteredGenerator/IndependentPrimeComponent.cs
  • AllFilteredGenerator/ItemRelicInfo.cs
  • AllFilteredGenerator/JsonExtensions.cs
  • AllFilteredGenerator/PrimeComponent.cs
  • AllFilteredGenerator/PrimeEquipment.cs
  • AllFilteredGenerator/Program.cs
  • AllFilteredGenerator/Properties/PublishProfiles/linux.pubxml
  • AllFilteredGenerator/Properties/PublishProfiles/windows.pubxml
  • AllFilteredGenerator/Relic.cs
  • WFInfo.sln

Comment thread AllFilteredGenerator/JsonExtensions.cs
Comment thread AllFilteredGenerator/JsonExtensions.cs
Comment thread AllFilteredGenerator/Program.cs
Comment thread AllFilteredGenerator/Properties/PublishProfiles/linux.pubxml Outdated
Comment thread AllFilteredGenerator/Properties/PublishProfiles/windows.pubxml Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant