1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-07-19 05:09:38 +02:00
Maybe/app/models/period.rb

171 lines
4.2 KiB
Ruby
Raw Normal View History

class Period
include ActiveModel::Validations, Comparable
2025-03-03 12:47:20 -05:00
class InvalidKeyError < StandardError; end
2025-03-03 12:47:20 -05:00
attr_reader :key, :start_date, :end_date
validates :start_date, :end_date, presence: true, if: -> { PERIODS[key].nil? }
validates :key, presence: true, if: -> { start_date.nil? || end_date.nil? }
validate :must_be_valid_date_range
PERIODS = {
"last_day" => {
2025-05-07 22:19:09 -04:00
date_range: -> { [ 1.day.ago.to_date, Date.current ] },
label_short: "1D",
label: "Last Day",
comparison_label: "vs. yesterday"
},
"current_week" => {
2025-05-07 22:19:09 -04:00
date_range: -> { [ Date.current.beginning_of_week, Date.current ] },
label_short: "WTD",
label: "Current Week",
comparison_label: "vs. start of week"
},
"last_7_days" => {
2025-05-07 22:19:09 -04:00
date_range: -> { [ 7.days.ago.to_date, Date.current ] },
label_short: "7D",
label: "Last 7 Days",
comparison_label: "vs. last week"
},
"current_month" => {
2025-05-07 22:19:09 -04:00
date_range: -> { [ Date.current.beginning_of_month, Date.current ] },
label_short: "MTD",
label: "Current Month",
comparison_label: "vs. start of month"
},
"last_30_days" => {
2025-05-07 22:19:09 -04:00
date_range: -> { [ 30.days.ago.to_date, Date.current ] },
label_short: "30D",
label: "Last 30 Days",
comparison_label: "vs. last month"
},
"last_90_days" => {
2025-05-07 22:19:09 -04:00
date_range: -> { [ 90.days.ago.to_date, Date.current ] },
label_short: "90D",
label: "Last 90 Days",
comparison_label: "vs. last quarter"
},
"current_year" => {
2025-05-07 22:19:09 -04:00
date_range: -> { [ Date.current.beginning_of_year, Date.current ] },
label_short: "YTD",
label: "Current Year",
comparison_label: "vs. start of year"
},
"last_365_days" => {
2025-05-07 22:19:09 -04:00
date_range: -> { [ 365.days.ago.to_date, Date.current ] },
label_short: "365D",
label: "Last 365 Days",
comparison_label: "vs. 1 year ago"
},
"last_5_years" => {
2025-05-07 22:19:09 -04:00
date_range: -> { [ 5.years.ago.to_date, Date.current ] },
label_short: "5Y",
label: "Last 5 Years",
comparison_label: "vs. 5 years ago"
}
}
class << self
2025-03-03 12:47:20 -05:00
def from_key(key)
unless PERIODS.key?(key)
raise InvalidKeyError, "Invalid period key: #{key}"
end
2025-05-07 22:19:09 -04:00
start_date, end_date = PERIODS[key].fetch(:date_range).call
2025-03-03 12:47:20 -05:00
new(key: key, start_date: start_date, end_date: end_date)
end
2025-03-03 12:47:20 -05:00
def custom(start_date:, end_date:)
new(start_date: start_date, end_date: end_date)
end
def all
PERIODS.map { |key, period| from_key(key) }
end
def as_options
all.map { |period| [ period.label_short, period.key ] }
end
end
PERIODS.each do |key, period|
define_singleton_method(key) do
2025-03-03 12:47:20 -05:00
from_key(key)
end
end
2025-03-03 12:47:20 -05:00
def initialize(start_date: nil, end_date: nil, key: nil, date_format: "%b %d, %Y")
@key = key
@start_date = start_date
@end_date = end_date
@date_format = date_format
validate!
end
def <=>(other)
[ start_date, end_date ] <=> [ other.start_date, other.end_date ]
end
def date_range
start_date..end_date
end
def days
(end_date - start_date).to_i + 1
end
def within?(other)
start_date >= other.start_date && end_date <= other.end_date
end
def interval
if days > 366
"1 week"
else
"1 day"
end
end
def label
2025-03-03 12:47:20 -05:00
if key_metadata
key_metadata.fetch(:label)
else
"Custom Period"
end
end
def label_short
2025-03-03 12:47:20 -05:00
if key_metadata
key_metadata.fetch(:label_short)
else
2025-03-03 12:47:20 -05:00
"Custom"
end
end
def comparison_label
2025-03-03 12:47:20 -05:00
if key_metadata
key_metadata.fetch(:comparison_label)
else
"#{start_date.strftime(@date_format)} to #{end_date.strftime(@date_format)}"
end
end
private
2025-03-03 12:47:20 -05:00
def key_metadata
@key_metadata ||= PERIODS[key]
end
def must_be_valid_date_range
return if start_date.nil? || end_date.nil?
unless start_date.is_a?(Date) && end_date.is_a?(Date)
Personal finance AI (v1) (#2022) * AI sidebar * Add chat and message models with associations * Implement AI chat functionality with sidebar and messaging system - Add chat and messages controllers - Create chat and message views - Implement chat-related routes - Add message broadcasting and user interactions - Update application layout to support chat sidebar - Enhance user model with initials method * Refactor AI sidebar with enhanced chat menu and interactions - Update sidebar layout with dynamic width and improved responsiveness - Add new chat menu Stimulus controller for toggling between chat and chat list views - Improve chat list display with recent chats and empty state - Extract AI avatar to a partial for reusability - Enhance message display and interaction styling - Add more contextual buttons and interaction hints * Improve chat scroll behavior and message styling - Refactor chat scroll functionality with Stimulus controller - Optimize message scrolling in chat views - Update message styling for better visual hierarchy - Enhance chat container layout with flex and auto-scroll - Simplify message rendering across different chat views * Extract AI avatar to a shared partial for consistent styling - Refactor AI avatar rendering across chat views - Replace hardcoded avatar markup with a reusable partial - Simplify avatar display in chats and messages views * Update sidebar controller to handle right panel width dynamically - Add conditional width class for right sidebar panel - Ensure consistent sidebar toggle behavior for both left and right panels - Use specific width class for right panel (w-[375px]) * Refactor chat form and AI greeting with flexible partials - Extract message form to a reusable partial with dynamic context support - Create flexible AI greeting partial for consistent welcome messages - Simplify chat and sidebar views by leveraging new partials - Add support for different form scenarios (chat, new chat, sidebar) - Improve code modularity and reduce duplication * Add chat clearing functionality with dynamic menu options - Implement clear chat action in ChatsController - Add clear chat route to support clearing messages - Update AI sidebar with dropdown menu for chat actions - Preserve system message when clearing chat - Enhance chat interaction with new menu options * Add frontmatter to project structure documentation - Create initial frontmatter for structure.mdc file - Include description and configuration options - Prepare for potential dynamic documentation rendering * Update general project rules with additional guidelines - Add rule for using `Current.family` instead of `current_family` - Include new guidelines for testing, API routes, and solution approach - Expand project-specific rules for more consistent development practices * Add OpenAI gem and AI-friendly data representations - Add `ruby-openai` gem for AI integration - Implement `to_ai_readable_hash` methods in BalanceSheet and IncomeStatement - Include Promptable module in both models - Add savings rate calculation method in IncomeStatement - Prepare financial models for AI-powered insights and interactions * Enhance AI Financial Assistant with Advanced Querying and Debugging Capabilities - Implement comprehensive AI financial query system with function-based interactions - Add detailed debug logging for AI responses and function calls - Extend BalanceSheet and IncomeStatement models with AI-friendly methods - Create robust error handling and fallback mechanisms for AI queries - Update chat and message views to support debug mode and enhanced rendering - Add AI query routes and initial test coverage for financial assistant * Refactor AI sidebar and chat layout with improved structure and comments - Remove inline AI chat from application layout - Enhance AI sidebar with more semantic HTML structure - Add descriptive comments to clarify different sections of chat view - Improve flex layout and scrolling behavior in chat messages container - Optimize message rendering with more explicit class names and structure * Add Markdown rendering support for AI chat messages - Implement `markdown` helper method in ApplicationHelper using Redcarpet - Update message view to render AI messages with Markdown formatting - Add comprehensive Markdown rendering options (tables, code blocks, links) - Enhance AI Financial Assistant prompt to encourage Markdown usage - Remove commented Markdown CSS in Tailwind application stylesheet * Missing comma * Enhance AI response processing with chat history context * Improve AI debug logging with payload size limits and internal message flag * Enhance AI chat interaction with improved thinking indicator and scrolling behavior * Add AI consent and enable/disable functionality for AI chat * Upgrade Biome and refactor JavaScript template literals - Update @biomejs/biome to latest version with caret (^) notation - Refactor AI query and chat controllers to use template literals - Standardize npm scripts formatting in package.json * Add beta testing usage note to AI consent modal * Update test fixtures and configurations for AI chat functionality - Add family association to chat fixtures and tests - Set consistent password digest for test users - Enable AI for test users - Add OpenAI access token for test environment - Update chat and user model tests to include family context * Simplify data model and get tests passing * Remove structure.mdc from version control * Integrate AI chat styles into existing prose pattern * Match Figma design spec, implement Turbo frames and actions for chats controller * AI rules refresh * Consolidate Stimulus controllers, thinking state, controllers, and views * Naming, domain alignment * Reset migrations * Improve data model to support tool calls and message types * Tool calling tests and fixtures * Tool call implementation and test * Get assistant test working again * Test updates * Process tool calls within provider * Chat UI back to working state again * Remove stale code * Tests passing * Update openai class naming to avoid conflicts * Reconfigure test env * Rebuild gemfile * Fix naming conflicts for ChatResponse * Message styles * Use OpenAI conversation state management * Assistant function base implementation * Add back thinking messages, clean up error handling for chat * Fix sync error when security price has bad data from provider * Add balance sheet function to assistant * Add better function calling error visibility * Add income statement function * Simplify and clean up "thinking" interactions with Turbo frames * Remove stale data definitions from functions * Ensure VCR fixtures working with latest code * basic stream implementation * Get streaming working * Make AI sidebar wider when left sidebar is collapsed * Get tests working with streaming responses * Centralize provider error handling * Provider data boundaries --------- Co-authored-by: Josh Pigford <josh@joshpigford.com>
2025-03-28 13:08:22 -04:00
errors.add(:start_date, "must be a valid date, got #{start_date.inspect}")
errors.add(:end_date, "must be a valid date, got #{end_date.inspect}")
return
end
errors.add(:start_date, "must be before end date") if start_date > end_date
end
end