From deb228a55ed9fed640c5c88409b9c8be89006518 Mon Sep 17 00:00:00 2001 From: mwjdaws <130329097+mwjdaws@users.noreply.github.com> Date: Sun, 20 Jul 2025 16:18:25 -0400 Subject: [PATCH] Integrate investment analytics app --- .env.local.example | 4 ++++ .../investment_analytics/fmp_provider.rb | 4 ++-- app/models/provider/registry.rb | 9 +++++++- .../investment_analytics_providers.rb | 2 ++ config/routes.rb | 8 ------- test/models/provider/fmp_provider_test.rb | 22 +++++++++++++++++++ 6 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 test/models/provider/fmp_provider_test.rb diff --git a/.env.local.example b/.env.local.example index d393f623..8de08546 100644 --- a/.env.local.example +++ b/.env.local.example @@ -3,3 +3,7 @@ SELF_HOSTED=false # Enable Synth market data (careful, this will use your API credits) SYNTH_API_KEY=yourapikeyhere + +# Enable the Investment Analytics app and configure FMP API access +ENABLE_INVESTMENT_ANALYTICS_APP=false +FMP_API_KEY=yourfmpapikeyhere diff --git a/app/apps/investment_analytics/services/investment_analytics/fmp_provider.rb b/app/apps/investment_analytics/services/investment_analytics/fmp_provider.rb index 7945aa93..40862c65 100644 --- a/app/apps/investment_analytics/services/investment_analytics/fmp_provider.rb +++ b/app/apps/investment_analytics/services/investment_analytics/fmp_provider.rb @@ -1,8 +1,8 @@ # app/apps/investment_analytics/services/investment_analytics/fmp_provider.rb module InvestmentAnalytics - class FmpProvider - include Provider::Base + require Rails.root.join("app", "models", "provider") + class FmpProvider < ::Provider BASE_URL = "https://financialmodelingprep.com/api/v3" diff --git a/app/models/provider/registry.rb b/app/models/provider/registry.rb index c2d37db0..986fa6fc 100644 --- a/app/models/provider/registry.rb +++ b/app/models/provider/registry.rb @@ -8,10 +8,17 @@ class Provider::Registry validates :concept, inclusion: { in: CONCEPTS } class << self + @@additional_providers = {} + def for_concept(concept) new(concept.to_sym) end + def register_provider(name, provider_class) + @@additional_providers[name.to_sym] = provider_class + define_singleton_method(name) { provider_class.new } + end + def get_provider(name) send(name) rescue NoMethodError @@ -98,7 +105,7 @@ class Provider::Registry when :llm %i[openai] else - %i[synth plaid_us plaid_eu github openai] + %i[synth plaid_us plaid_eu github openai] + self.class.class_variable_get(:@@additional_providers).keys end end end diff --git a/config/initializers/investment_analytics_providers.rb b/config/initializers/investment_analytics_providers.rb index 43981c8b..a30a295a 100644 --- a/config/initializers/investment_analytics_providers.rb +++ b/config/initializers/investment_analytics_providers.rb @@ -2,6 +2,8 @@ # Ensure the InvestmentAnalytics module is loaded require_relative '../../app/apps/investment_analytics/investment_analytics' +require Rails.root.join('app', 'models', 'provider') +require Rails.root.join('app', 'models', 'provider', 'registry') # Ensure the FmpProvider class is loaded require_relative '../../app/apps/investment_analytics/services/investment_analytics/fmp_provider' diff --git a/config/routes.rb b/config/routes.rb index 74eadf17..4c641a91 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -262,14 +262,6 @@ Rails.application.routes.draw do get "privacy", to: redirect("https://maybefinance.com/privacy") get "terms", to: redirect("https://maybefinance.com/tos") - namespace :investment_analytics do - resources :dashboards, only: [:index] do - collection do - get :portfolio_summary - get :dividend_forecast - end - end - end # Defines the root path route ("/") root "pages#dashboard" diff --git a/test/models/provider/fmp_provider_test.rb b/test/models/provider/fmp_provider_test.rb new file mode 100644 index 00000000..a70eb494 --- /dev/null +++ b/test/models/provider/fmp_provider_test.rb @@ -0,0 +1,22 @@ +require "test_helper" +require "ostruct" + +class InvestmentAnalytics::FmpProviderTest < ActiveSupport::TestCase + setup do + @provider = InvestmentAnalytics::FmpProvider.new(api_key: "test") + end + + test "quote returns parsed data" do + response = OpenStruct.new(success?: true, body: [{ price: 10 }.to_json]) + HTTParty.expects(:get).returns(response) + + result = @provider.quote("AAPL") + assert_equal({"price" => 10}, result) + end + + test "error responses raise provider error" do + response = OpenStruct.new(success?: false, code: 404, body: "not found") + assert_raises(Provider::Error) { @provider.send(:handle_response, response) } + end +end +