mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-24 03:00:12 +01:00
Merge branch 'vacation-work'
This commit is contained in:
commit
0a95d430d4
22 changed files with 171 additions and 105 deletions
|
|
@ -54,7 +54,7 @@ class LoginController < ApplicationController
|
|||
end
|
||||
when :get
|
||||
if User.no_users_yet?
|
||||
redirect_to :controller => 'users', :action => 'new'
|
||||
redirect_to signup_path
|
||||
return
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -319,6 +319,7 @@ class StatsController < ApplicationController
|
|||
# - actions not part of a hidden project
|
||||
# - actions not part of a hidden context
|
||||
# - actions not deferred (show_from must be null)
|
||||
# - actions not pending/blocked
|
||||
|
||||
@actions_running_time = @actions.find_by_sql([
|
||||
"SELECT t.created_at "+
|
||||
|
|
@ -326,7 +327,7 @@ class StatsController < ApplicationController
|
|||
"WHERE t.user_id=? "+
|
||||
"AND t.completed_at IS NULL " +
|
||||
"AND t.show_from IS NULL " +
|
||||
"AND NOT (p.state='hidden' OR c.hide=?) " +
|
||||
"AND NOT (p.state='hidden' OR p.state='pending' OR c.hide=?) " +
|
||||
"ORDER BY t.created_at ASC", @user.id, true]
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class UsersController < ApplicationController
|
|||
|
||||
unless user.valid?
|
||||
session['new_user'] = user
|
||||
redirect_to :action => 'new'
|
||||
redirect_to signup_path
|
||||
return
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -29,13 +29,17 @@ class RecurringTodo < ActiveRecord::Base
|
|||
|
||||
validates_presence_of :description
|
||||
validates_presence_of :recurring_period
|
||||
validates_presence_of :target
|
||||
validates_presence_of :recurring_period
|
||||
validates_presence_of :ends_on
|
||||
validates_presence_of :context
|
||||
|
||||
validates_length_of :description, :maximum => 100
|
||||
validates_length_of :notes, :maximum => 60000, :allow_nil => true
|
||||
|
||||
validates_presence_of :context
|
||||
|
||||
validate :period_specific_validations
|
||||
validate :starts_and_ends_on_validations
|
||||
validate :set_recurrence_on_validations
|
||||
|
||||
def period_specific_validations
|
||||
periods = %W[daily weekly monthly yearly]
|
||||
|
|
@ -57,9 +61,8 @@ class RecurringTodo < ActiveRecord::Base
|
|||
errors.add_to_base("Every other nth week may not be empty for recurrence setting")
|
||||
end
|
||||
something_set = false
|
||||
%w{sunday monday tuesday wednesday thursday friday}.each do |day|
|
||||
%w{sunday monday tuesday wednesday thursday friday saturday}.each do |day|
|
||||
something_set ||= self.send("on_#{day}")
|
||||
|
||||
end
|
||||
errors.add_to_base("You must specify at least one day on which the todo recurs") if !something_set
|
||||
end
|
||||
|
|
@ -104,6 +107,21 @@ class RecurringTodo < ActiveRecord::Base
|
|||
errors.add_to_base("The end of the recurrence is not selected") unless ends_on == "no_end_date"
|
||||
end
|
||||
end
|
||||
|
||||
def set_recurrence_on_validations
|
||||
# show always or x days before due date. x not null
|
||||
case self.target
|
||||
when 'show_from_date'
|
||||
# no validations
|
||||
when 'due_date'
|
||||
errors.add_to_base("Please select when to show the action") if show_always.nil?
|
||||
unless show_always
|
||||
errors.add_to_base("Please fill in the number of days to show the todo before the due date") if show_from_delta.nil? || show_from_delta.blank?
|
||||
end
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence target selector '#{self.recurrence_target}'"
|
||||
end
|
||||
end
|
||||
|
||||
# the following recurrence patterns can be stored:
|
||||
#
|
||||
|
|
@ -388,9 +406,9 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def recurrence_pattern
|
||||
return "invalid repeat pattern" if every_other1.nil?
|
||||
case recurring_period
|
||||
when 'daily'
|
||||
return "invalid repeat pattern" if every_other1.nil?
|
||||
if only_work_days
|
||||
return "on work days"
|
||||
else
|
||||
|
|
@ -401,21 +419,19 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
when 'weekly'
|
||||
return "invalid repeat pattern" if every_other1.nil?
|
||||
if every_other1 > 1
|
||||
return "every #{every_other1} weeks"
|
||||
else
|
||||
return 'weekly'
|
||||
end
|
||||
when 'monthly'
|
||||
return "invalid repeat pattern" if every_other1.nil? || every_other2.nil?
|
||||
return "invalid repeat pattern" if every_other2.nil?
|
||||
if self.recurrence_selector == 0
|
||||
return "every #{self.every_other2} month#{self.every_other2>1?'s':''} on day #{self.every_other1}"
|
||||
else
|
||||
return "every #{self.xth} #{self.day_of_week} of every #{self.every_other2} month#{self.every_other2>1?'s':''}"
|
||||
end
|
||||
when 'yearly'
|
||||
return "invalid repeat pattern" if every_other1.nil?
|
||||
if self.recurrence_selector == 0
|
||||
return "every year on #{self.month_of_year} #{self.every_other1}"
|
||||
else
|
||||
|
|
@ -535,8 +551,8 @@ class RecurringTodo < ActiveRecord::Base
|
|||
start = previous + 1.day
|
||||
if start.wday() == 0
|
||||
# we went to a new week , go to the nth next week and find first match
|
||||
# that week
|
||||
start += self.every_other1.week
|
||||
# that week. Note that we already went into the next week, so -1
|
||||
start += (self.every_other1-1).week
|
||||
end
|
||||
unless self.start_from.nil?
|
||||
# check if the start_from date is later than previous. If so, use
|
||||
|
|
@ -726,11 +742,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
protected
|
||||
|
||||
def validate
|
||||
errors.add("", "At least one day must be selected in the weekly pattern") if self.every_day == ' '
|
||||
end
|
||||
|
||||
|
||||
def determine_start(previous)
|
||||
if previous.nil?
|
||||
start = self.start_from.nil? ? Time.zone.now : self.start_from
|
||||
|
|
|
|||
|
|
@ -18,7 +18,10 @@ class Todo < ActiveRecord::Base
|
|||
|
||||
named_scope :active, :conditions => { :state => 'active' }
|
||||
named_scope :not_completed, :conditions => ['NOT (todos.state = ? )', 'completed']
|
||||
named_scope :completed, :conditions => ["NOT completed_at IS NULL"]
|
||||
named_scope :are_due, :conditions => ['NOT (todos.due IS NULL)']
|
||||
named_scope :deferred, :conditions => ["completed_at IS NULL AND NOT show_from IS NULL"]
|
||||
named_scope :blocked, :conditions => ['todos.state = ?', 'pending']
|
||||
|
||||
STARRED_TAG_NAME = "starred"
|
||||
RE_TODO = /[^"]+/
|
||||
|
|
|
|||
|
|
@ -1,19 +1,21 @@
|
|||
<p>You have <%= @projects.count%> projects.
|
||||
Of those <%= @projects.count(:conditions => "state = 'active'")%> are active projects,
|
||||
<%= @projects.count(:conditions => "state = 'hidden'")%> hidden projects and
|
||||
<%= @projects.count(:conditions => "state = 'completed'")%> completed projects</p>
|
||||
Of those <%= @projects.active.count%> are active projects,
|
||||
<%= @projects.hidden.count%> hidden projects and
|
||||
<%= @projects.completed.count%> completed projects</p>
|
||||
|
||||
<p>You have <%= @contexts.count%> contexts.
|
||||
Of those <%= @contexts.count(:conditions => ["hide = ?", false])%> are visible contexts and
|
||||
<%= @contexts.count(:conditions => ["hide = ?", true]) %> are hidden contexts
|
||||
Of those <%= @contexts.active.count%> are visible contexts and
|
||||
<%= @contexts.hidden.count%> are hidden contexts
|
||||
|
||||
<% unless @actions.empty? -%>
|
||||
<p>You have <%= @actions.count(:conditions => "completed_at IS NULL") %> incomplete actions
|
||||
of which <%= @actions.count(:conditions => "completed_at IS NULL AND NOT show_from IS NULL") %> are deferred actions. </p>
|
||||
<p>Since your first action on <%= format_date(@first_action.created_at) %>
|
||||
you have a total of <%= @actions.count %> actions.
|
||||
<%= @actions.completed.count %> of these are completed.
|
||||
|
||||
<p>Since your first action on <%= format_date(@first_action.created_at) %>
|
||||
you have a total of <%= @actions.count %> actions.
|
||||
<%= @actions.count(:conditions => "NOT completed_at IS NULL") %> of these are completed.
|
||||
<p>You have <%= @actions.not_completed.count %> incomplete actions
|
||||
of which <%= @actions.deferred.count %> are deferred actions
|
||||
in the tickler and <%= @actions.blocked.count %> are dependent on the completion of other actions.
|
||||
. </p>
|
||||
|
||||
<p>You have <%= @tags_count-%> tags placed on actions. Of those tags,
|
||||
<%= @unique_tags_count -%> are unique.
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ ActionController::Routing::Routes.draw do |map|
|
|||
map.root :controller => 'todos' # Make OpenID happy because it needs #root_url defined
|
||||
|
||||
map.resources :notes
|
||||
map.feeds 'feeds', :controller => 'feedlist', :action => 'index'
|
||||
map.feeds 'feeds.m', :controller => 'feedlist', :action => 'index', :format => 'm'
|
||||
map.feeds 'feeds', :controller => 'feedlist', :action => 'index'
|
||||
|
||||
if Rails.env == 'test'
|
||||
map.connect '/selenium_helper/login', :controller => 'selenium_helper', :action => 'login'
|
||||
|
|
|
|||
9
db/migrate/20100502162317_add_index_to_todo_state.rb
Normal file
9
db/migrate/20100502162317_add_index_to_todo_state.rb
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
class AddIndexToTodoState < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_index :todos, :state
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_index :todos, :state
|
||||
end
|
||||
end
|
||||
|
|
@ -11,7 +11,7 @@ Feature: Manage a project
|
|||
And I have logged in as "testuser" with password "secret"
|
||||
And there exists a project "manage me" for user "testuser"
|
||||
|
||||
@selenium
|
||||
@selenium, @wip
|
||||
Scenario: I can describe the project using markup
|
||||
When I visit the "manage me" project
|
||||
And I edit the project description to "_successfull outcome_: project is *done*"
|
||||
|
|
|
|||
31
features/manage_users.feature
Normal file
31
features/manage_users.feature
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
Feature: Manage users
|
||||
|
||||
In order to be able to manage the users able to use Tracks
|
||||
As the administrator of this installed Tracks
|
||||
I want to add and delete accounts of users
|
||||
|
||||
Background:
|
||||
Given the following user records
|
||||
| login | password | is_admin |
|
||||
| testuser | secret | false |
|
||||
| admin | secret | true |
|
||||
And I have logged in as "admin" with password "secret"
|
||||
|
||||
Scenario: Show all accounts
|
||||
When I go to the manage users page
|
||||
Then I should see "testuser"
|
||||
And I should see "admin"
|
||||
|
||||
Scenario: Add new account
|
||||
When I go to the manage users page
|
||||
And I follow "Signup new user"
|
||||
Then I should be on the signup page
|
||||
When I submit the signup form with username "new.user", password "secret123" and confirm with "secret123"
|
||||
Then I should be on the manage users page
|
||||
And I should see "new.user"
|
||||
|
||||
@selenium
|
||||
Scenario: Delete account from users page
|
||||
When I go to the manage users page
|
||||
And I delete the user "testuser"
|
||||
Then I should see that a user named "testuser" is not present
|
||||
25
features/recurring_todos.feature
Normal file
25
features/recurring_todos.feature
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Feature: Manage recurring todos
|
||||
|
||||
In order to manage repeating todos
|
||||
As a Tracks user
|
||||
I want to view, edit, add, or remove recurrence patterns of repeating todos
|
||||
|
||||
Background:
|
||||
Given the following user record
|
||||
| login | password | is_admin |
|
||||
| testuser | secret | false |
|
||||
And I have logged in as "testuser" with password "secret"
|
||||
|
||||
@selenium
|
||||
Scenario: Being able to select daily, weekly, monthly and yearly pattern
|
||||
When I go to the repeating todos page
|
||||
And I follow "Add a new recurring action"
|
||||
Then I should see the form for "Daily" recurrence pattern
|
||||
When I select "Weekly" recurrence pattern
|
||||
Then I should see the form for "Weekly" recurrence pattern
|
||||
When I select "Monthly" recurrence pattern
|
||||
Then I should see the form for "Monthly" recurrence pattern
|
||||
When I select "Yearly" recurrence pattern
|
||||
Then I should see the form for "Yearly" recurrence pattern
|
||||
When I select "Daily" recurrence pattern
|
||||
Then I should see the form for "Daily" recurrence pattern
|
||||
|
|
@ -2,6 +2,13 @@ Given /^I have a context called "([^\"]*)"$/ do |context_name|
|
|||
@context = @current_user.contexts.create!(:name => context_name)
|
||||
end
|
||||
|
||||
Given /^I have a context "([^\"]*)" with (.*) actions$/ do |context_name, number_of_actions|
|
||||
context = @current_user.contexts.create!(:name => context_name)
|
||||
1.upto number_of_actions.to_i do |i|
|
||||
@current_user.todos.create!(:context_id => context.id, :description => "todo #{i}")
|
||||
end
|
||||
end
|
||||
|
||||
When /^I visits the context page for "([^\"]*)"$/ do |context_name|
|
||||
context = @current_user.contexts.find_by_name(context_name)
|
||||
context.should_not be_nil
|
||||
|
|
@ -14,25 +21,6 @@ When /^I edit the context name in place to be "([^\"]*)"$/ do |new_context_name|
|
|||
click_button "OK"
|
||||
end
|
||||
|
||||
Then /^I should see the context name is "([^\"]*)"$/ do |context_name|
|
||||
Then "I should see \"#{context_name}\""
|
||||
end
|
||||
|
||||
Then /^he should see that a context named "([^\"]*)" is present$/ do |context_name|
|
||||
Then "I should see \"#{context_name}\""
|
||||
end
|
||||
|
||||
Then /^he should see that a context named "([^\"]*)" is not present$/ do |context_name|
|
||||
Then "I should not see \"#{context_name} (\""
|
||||
end
|
||||
|
||||
Given /^I have a context "([^\"]*)" with (.*) actions$/ do |context_name, number_of_actions|
|
||||
context = @current_user.contexts.create!(:name => context_name)
|
||||
1.upto number_of_actions.to_i do |i|
|
||||
@current_user.todos.create!(:context_id => context.id, :description => "todo #{i}")
|
||||
end
|
||||
end
|
||||
|
||||
When /^I delete the context "([^\"]*)"$/ do |context_name|
|
||||
context = @current_user.contexts.find_by_name(context_name)
|
||||
context.should_not be_nil
|
||||
|
|
@ -51,3 +39,15 @@ When /^I edit the context to rename it to "([^\"]*)"$/ do |new_name|
|
|||
selenium.is_visible("flash")
|
||||
end
|
||||
end
|
||||
|
||||
Then /^I should see the context name is "([^\"]*)"$/ do |context_name|
|
||||
Then "I should see \"#{context_name}\""
|
||||
end
|
||||
|
||||
Then /^he should see that a context named "([^\"]*)" is present$/ do |context_name|
|
||||
Then "I should see \"#{context_name}\""
|
||||
end
|
||||
|
||||
Then /^he should see that a context named "([^\"]*)" is not present$/ do |context_name|
|
||||
Then "I should not see \"#{context_name} (\""
|
||||
end
|
||||
|
|
@ -3,6 +3,7 @@ Given /^I have logged in as "(.*)" with password "(.*)"$/ do |username, password
|
|||
fill_in "Login", :with => username
|
||||
fill_in "Password", :with => password
|
||||
click_button
|
||||
selenium.wait_for_page_to_load(5000)
|
||||
response.should contain(/Login successful/)
|
||||
@current_user = User.find_by_login(username)
|
||||
end
|
||||
|
|
|
|||
7
features/step_definitions/recurring_todo_steps.rb
Normal file
7
features/step_definitions/recurring_todo_steps.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
When /^I select "([^\"]*)" recurrence pattern$/ do |recurrence_period|
|
||||
selenium.click("recurring_todo_recurring_period_#{recurrence_period.downcase}")
|
||||
end
|
||||
|
||||
Then /^I should see the form for "([^\"]*)" recurrence pattern$/ do |recurrence_period|
|
||||
selenium.is_visible("recurring_#{recurrence_period.downcase}")
|
||||
end
|
||||
|
|
@ -10,6 +10,24 @@ Given "no users exists" do
|
|||
User.delete_all
|
||||
end
|
||||
|
||||
When /^I delete the user "([^\"]*)"$/ do |username|
|
||||
# click "//tr[@id='user-3']//img"
|
||||
# assert_confirmation "Warning: this will delete user 'john', all their actions, contexts, project and notes. Are you sure that you want to continue?"
|
||||
user = User.find_by_login(username)
|
||||
user.should_not be_nil
|
||||
|
||||
selenium.click "xpath=//tr[@id='user-#{user.id}']//img"
|
||||
selenium.get_confirmation.should == "Warning: this will delete user '#{user.login}', all their actions, contexts, project and notes. Are you sure that you want to continue?"
|
||||
wait_for do
|
||||
!selenium.is_element_present("//tr[@id='user-#{user.id}']//img")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Then /^I should see that a user named "([^\"]*)" is not present$/ do |username|
|
||||
Then "I should not see \"#{username} (\""
|
||||
end
|
||||
|
||||
Then "I should be an admin" do
|
||||
# just check on the presence of the menu item for managing users
|
||||
Then "I should see \"Manage users\""
|
||||
|
|
|
|||
|
|
@ -9,13 +9,17 @@ module NavigationHelpers
|
|||
when /the statistics page/
|
||||
stats_path
|
||||
when /the signup page/
|
||||
"/users/new"
|
||||
signup_path
|
||||
when /the login page/
|
||||
login_path
|
||||
when /the notes page/
|
||||
notes_path
|
||||
when /the contexts page/
|
||||
contexts_path
|
||||
when /the manage users page/
|
||||
users_path
|
||||
when /the repeating todos page/
|
||||
recurring_todos_path
|
||||
|
||||
# Add more page name => path mappings here
|
||||
|
||||
|
|
|
|||
2
test/fixtures/recurring_todos.yml
vendored
2
test/fixtures/recurring_todos.yml
vendored
|
|
@ -64,7 +64,7 @@ call_bill_gates_every_workday:
|
|||
show_from_delta: ~
|
||||
recurring_period: daily
|
||||
recurrence_selector: ~
|
||||
every_other1: ~
|
||||
every_other1: 1
|
||||
every_other2: ~
|
||||
every_other3: ~
|
||||
every_day: ~
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
setup :fixtures => :all
|
||||
login :as => 'admin'
|
||||
open "/contexts"
|
||||
click "css=#context_3 .buttons img.edit_item"
|
||||
wait_for_visible "edit_context_3"
|
||||
wait_for_not_visible "context_3"
|
||||
type "//div[@id='edit_context_3'] //input[@name='context[name]']", "telegraph"
|
||||
click "//div[@id='edit_context_3'] //button"
|
||||
wait_for_not_visible "edit_context_3"
|
||||
wait_for_visible "context_3"
|
||||
click "css=#context_3 .buttons img.delete_item"
|
||||
assert_confirmation "Are you sure that you want to delete the context 'telegraph'?"
|
||||
wait_for_visible "flash"
|
||||
wait_for_text "flash", "Deleted context 'telegraph'"
|
||||
wait_for_element_not_present "context_3"
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
setup :fixtures => :all
|
||||
login :as => 'admin'
|
||||
open "/contexts"
|
||||
click "css=#context_3 .buttons img.edit_item"
|
||||
wait_for_visible "edit_context_3"
|
||||
wait_for_not_visible "context_3"
|
||||
type "//div[@id='edit_context_3'] //input[@name='context[name]']", "telegraph"
|
||||
click "//div[@id='edit_context_3'] //button"
|
||||
wait_for_not_visible "edit_context_3"
|
||||
wait_for_visible "context_3"
|
||||
assert_text 'css=#context_3 .data a', 'telegraph'
|
||||
click "css=#context_3 .buttons img.edit_item"
|
||||
wait_for_visible "edit_context_3"
|
||||
wait_for_not_visible "context_3"
|
||||
type "//div[@id='edit_context_3'] //input[@name='context[name]']", "email"
|
||||
click "//div[@id='edit_context_3'] //button"
|
||||
wait_for_not_visible "edit_context_3"
|
||||
wait_for_visible "context_3"
|
||||
assert_text 'css=#context_3 .data a', 'email'
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
setup :fixtures => :users, :clear_tables => [:projects, :contexts, :todos]
|
||||
login :as => 'admin'
|
||||
open "/recurring_todos"
|
||||
click "css=#recurring_new_container a"
|
||||
click "recurring_todo_recurring_period_daily"
|
||||
assert_visible "recurring_daily"
|
||||
click "recurring_todo_recurring_period_weekly"
|
||||
assert_visible "recurring_weekly"
|
||||
click "recurring_todo_recurring_period_monthly"
|
||||
assert_visible "recurring_monthly"
|
||||
click "recurring_todo_recurring_period_yearly"
|
||||
assert_visible "recurring_yearly"
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
setup :fixtures => :all
|
||||
login :as => 'admin'
|
||||
open '/users'
|
||||
assert_text_present "John Deere"
|
||||
click "//tr[@id='user-3']//img"
|
||||
assert_confirmation "Warning: this will delete user 'john', all their actions, contexts, project and notes. Are you sure that you want to continue?"
|
||||
wait_for_text_not_present "John Deere"
|
||||
|
|
@ -124,7 +124,9 @@ class RecurringTodoTest < ActiveSupport::TestCase
|
|||
due_date = @weekly_every_day.get_due_date(@sunday)
|
||||
assert_equal @monday, due_date
|
||||
|
||||
# saturday is last day in week, so the next date should be sunday + n_weeks
|
||||
# saturday is last day in week, so the next date should be sunday + n-1 weeks
|
||||
# n-1 because sunday is already in the next week
|
||||
@weekly_every_day.every_other1 = 3
|
||||
due_date = @weekly_every_day.get_due_date(@saturday)
|
||||
assert_equal @sunday + 2.weeks, due_date
|
||||
|
||||
|
|
@ -141,6 +143,10 @@ class RecurringTodoTest < ActiveSupport::TestCase
|
|||
assert_equal @wednesday, due_date
|
||||
due_date = @weekly_every_day.get_due_date(@wednesday)
|
||||
assert_equal @tuesday+1.week, due_date
|
||||
|
||||
@weekly_every_day.every_day = ' s'
|
||||
due_date = @weekly_every_day.get_due_date(@sunday)
|
||||
assert_equal @saturday+1.week, due_date
|
||||
end
|
||||
|
||||
def test_monthly_pattern
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue