2007-03-30 04:36:52 +00:00
|
|
|
class Project < ActiveRecord::Base
|
2011-01-06 23:01:17 +01:00
|
|
|
has_many :todos, :dependent => :delete_all
|
2007-03-30 04:36:52 +00:00
|
|
|
has_many :notes, :dependent => :delete_all, :order => "created_at DESC"
|
2010-04-04 18:20:07 +02:00
|
|
|
has_many :recurring_todos
|
2009-04-18 23:50:12 +02:00
|
|
|
|
2008-06-17 01:13:25 -04:00
|
|
|
belongs_to :default_context, :class_name => "Context", :foreign_key => "default_context_id"
|
2007-03-30 04:36:52 +00:00
|
|
|
belongs_to :user
|
2008-11-29 15:35:17 +01:00
|
|
|
|
|
|
|
|
named_scope :active, :conditions => { :state => 'active' }
|
|
|
|
|
named_scope :hidden, :conditions => { :state => 'hidden' }
|
|
|
|
|
named_scope :completed, :conditions => { :state => 'completed'}
|
2011-05-03 11:43:02 +02:00
|
|
|
named_scope :uncompleted, :conditions => ["NOT(state = ?)", 'completed']
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2010-10-31 21:27:13 +08:00
|
|
|
validates_presence_of :name
|
|
|
|
|
validates_length_of :name, :maximum => 255
|
2010-11-09 15:51:21 +01:00
|
|
|
validates_uniqueness_of :name, :scope => "user_id"
|
2007-03-30 04:36:52 +00:00
|
|
|
|
2011-09-13 11:15:14 +02:00
|
|
|
acts_as_list :scope => 'user_id = #{user_id} AND state = \'#{state}\'', :top_of_list => 0
|
|
|
|
|
|
2011-05-16 15:42:47 +08:00
|
|
|
include AASM
|
|
|
|
|
aasm_column :state
|
|
|
|
|
aasm_initial_state :active
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2007-03-30 04:36:52 +00:00
|
|
|
extend NamePartFinder
|
2009-04-18 23:50:12 +02:00
|
|
|
#include Tracks::TodoList
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2011-05-16 15:42:47 +08:00
|
|
|
aasm_state :active
|
|
|
|
|
aasm_state :hidden, :enter => :hide_todos, :exit => :unhide_todos
|
2011-06-10 14:28:42 +02:00
|
|
|
aasm_state :completed, :enter => :set_completed_at_date, :exit => :clear_completed_at_date
|
2007-03-30 04:36:52 +00:00
|
|
|
|
2011-05-16 15:42:47 +08:00
|
|
|
aasm_event :activate do
|
|
|
|
|
transitions :to => :active, :from => [:active, :hidden, :completed]
|
2007-03-30 04:36:52 +00:00
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2011-05-16 15:42:47 +08:00
|
|
|
aasm_event :hide do
|
2007-03-30 04:36:52 +00:00
|
|
|
transitions :to => :hidden, :from => [:active, :completed]
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2011-05-16 15:42:47 +08:00
|
|
|
aasm_event :complete do
|
2007-03-30 04:36:52 +00:00
|
|
|
transitions :to => :completed, :from => [:active, :hidden]
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2007-03-30 04:36:52 +00:00
|
|
|
attr_protected :user
|
|
|
|
|
attr_accessor :cached_note_count
|
|
|
|
|
|
|
|
|
|
def self.null_object
|
|
|
|
|
NullProject.new
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2007-03-30 04:36:52 +00:00
|
|
|
def self.feed_options(user)
|
|
|
|
|
{
|
2010-11-09 10:47:09 +01:00
|
|
|
:title => I18n.t('models.project.feed_title'),
|
|
|
|
|
:description => I18n.t('models.project.feed_description', :username => user.display_name)
|
2007-03-30 04:36:52 +00:00
|
|
|
}
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2007-03-30 04:36:52 +00:00
|
|
|
def hide_todos
|
|
|
|
|
todos.each do |t|
|
|
|
|
|
unless t.completed? || t.deferred?
|
|
|
|
|
t.hide!
|
|
|
|
|
t.save
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2007-03-30 04:36:52 +00:00
|
|
|
def unhide_todos
|
|
|
|
|
todos.each do |t|
|
|
|
|
|
if t.project_hidden?
|
|
|
|
|
t.unhide!
|
|
|
|
|
t.save
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2011-06-10 14:28:42 +02:00
|
|
|
def set_completed_at_date
|
|
|
|
|
self.completed_at = Time.zone.now
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2011-06-10 14:28:42 +02:00
|
|
|
def clear_completed_at_date
|
|
|
|
|
self.completed_at = nil
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2007-03-30 04:36:52 +00:00
|
|
|
def note_count
|
2011-06-12 05:38:45 +02:00
|
|
|
# TODO: test this for eager and not eager loading!!!
|
|
|
|
|
return 0 if notes.size == 0
|
2007-03-30 04:36:52 +00:00
|
|
|
cached_note_count || notes.count
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2007-03-30 04:36:52 +00:00
|
|
|
alias_method :original_default_context, :default_context
|
|
|
|
|
|
|
|
|
|
def default_context
|
|
|
|
|
original_default_context.nil? ? Context.null_object : original_default_context
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2007-03-30 04:36:52 +00:00
|
|
|
# would prefer to call this method state=(), but that causes an endless loop
|
|
|
|
|
# as a result of acts_as_state_machine calling state=() to update the attribute
|
|
|
|
|
def transition_to(candidate_state)
|
|
|
|
|
case candidate_state.to_sym
|
2011-05-16 15:42:47 +08:00
|
|
|
when aasm_current_state
|
2007-03-30 04:36:52 +00:00
|
|
|
return
|
|
|
|
|
when :hidden
|
|
|
|
|
hide!
|
|
|
|
|
when :active
|
|
|
|
|
activate!
|
|
|
|
|
when :completed
|
|
|
|
|
complete!
|
|
|
|
|
end
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2011-09-16 15:07:58 -04:00
|
|
|
def needs_review?(current_user)
|
2011-10-08 00:23:31 -04:00
|
|
|
return false if self.completed?
|
2011-09-16 23:34:09 -04:00
|
|
|
return true if last_reviewed.nil?
|
2011-09-28 13:54:50 +02:00
|
|
|
return (active? && (last_reviewed < current_user.time - current_user.prefs.review_period.days))
|
2011-09-16 23:34:09 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def blocked?
|
|
|
|
|
## mutually exclusive for stalled and blocked
|
2011-09-28 13:54:50 +02:00
|
|
|
# blocked is uncompleted project with deferred or pending todos, but no next actions
|
2011-09-28 15:34:15 +02:00
|
|
|
return false if self.completed?
|
|
|
|
|
return !self.todos.deferred_or_blocked.empty? && self.todos.not_deferred_or_blocked.empty?
|
2011-09-16 23:34:09 -04:00
|
|
|
end
|
2011-09-28 13:54:50 +02:00
|
|
|
|
2011-09-16 23:34:09 -04:00
|
|
|
def stalled?
|
2011-09-28 13:54:50 +02:00
|
|
|
# stalled is active/hidden project with no active todos
|
|
|
|
|
return false if self.completed?
|
2011-09-28 15:34:15 +02:00
|
|
|
return self.todos.deferred_or_blocked.empty? && self.todos.not_deferred_or_blocked.empty?
|
2011-09-16 15:07:58 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
2007-10-03 03:05:34 +00:00
|
|
|
def name=(value)
|
|
|
|
|
self[:name] = value.gsub(/\s{2,}/, " ").strip
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2007-11-04 23:06:46 +00:00
|
|
|
def new_record_before_save?
|
|
|
|
|
@new_record_before_save
|
2011-09-13 11:15:14 +02:00
|
|
|
end
|
|
|
|
|
|
2007-03-30 04:36:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
class NullProject
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2007-03-30 04:36:52 +00:00
|
|
|
def hidden?
|
|
|
|
|
false
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2007-03-30 04:36:52 +00:00
|
|
|
def nil?
|
|
|
|
|
true
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2007-03-30 04:36:52 +00:00
|
|
|
def id
|
|
|
|
|
nil
|
|
|
|
|
end
|
2011-09-13 11:15:14 +02:00
|
|
|
|
2009-04-19 00:18:12 +02:00
|
|
|
end
|