dates 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,46 @@
1
+ Dates keeps track of things you two have been meaning to do, and learns what's been fun and what hasn't along several different dimensions (adventure, romantic, intellectual, athletic).
2
+
3
+ ## Usage
4
+
5
+ $ bundle exec bin/dates.rb --help
6
+ dates 1.0.0-beta, max & daphne.
7
+ Usage: dates.rb [command] [options] <ideas-file>
8
+
9
+ To see help for a specific command, use the --help switch on that command.
10
+
11
+ Commands:
12
+ choose Choose a good date idea for right now! [default command]
13
+ list Parse and list the contents of the date ideas file.
14
+
15
+ Global options:
16
+ --version, -v: Print version and exit
17
+ --help, -h: Show this message
18
+
19
+ To run it, the simplest way is just to provide it with an ideas file and nothing else:
20
+
21
+ $ bundle exec bin/dates.rb ideas.rb
22
+ watch _serenity_: (intellectual: 5, athletic: 0, adventure: 2, romantic: 4)
23
+
24
+ ### Commands
25
+
26
+ The `choose` command picks a date, and it takes two options:
27
+
28
+ $ bundle exec bin/dates.rb choose --help
29
+ dates 1.0.0-beta, max & daphne.
30
+ Options for `choose` command:
31
+ --weather, -w <f>: Current weather score [0-9] (default: 5.0)
32
+ --time, -t <f>: Time commitment score [0-9] (default: 5.0)
33
+
34
+ The `list` command prints out the contents of an ideas file:
35
+
36
+ $ bundle exec bin/dates.rb list --help
37
+ dates 1.0.0-beta, max & daphne.
38
+ Options for `list` command:
39
+ --incomplete, -i: Only the incomplete dates
40
+ --complete, -c: Only the completed dates (with feedback)
41
+
42
+ ## Tests
43
+
44
+ Dates uses RSpec 2 (core and expectations) for tests. To run them, just call:
45
+
46
+ bundle exec rspec test/spec.rb -c -f d
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/dates'
4
+
5
+ SUB_COMMANDS = %w(list choose)
6
+ VERSION_STR = "dates 1.0.0-beta, max & daphne."
7
+ global_opts = Trollop::options do
8
+ version VERSION_STR
9
+ banner <<-EOS
10
+ #{VERSION_STR}
11
+ Usage: dates.rb [command] [options] <ideas-file>
12
+
13
+ To see help for a specific command, use the --help switch on that command.
14
+
15
+ Commands:
16
+ choose Choose a good date idea for right now! [default command]
17
+ list Parse and list the contents of the date ideas file.
18
+
19
+ Global options:
20
+ EOS
21
+
22
+ stop_on SUB_COMMANDS
23
+ end
24
+
25
+ cmd = ARGV.shift if ARGV.length > 1
26
+
27
+ cmd_opts = case cmd
28
+ when "list"
29
+ Trollop::options do
30
+ version VERSION_STR
31
+ banner "#{VERSION_STR}\nOptions for `list` command:"
32
+ opt :incomplete, "Only the incomplete dates", :short => :i
33
+ opt :complete, "Only the completed dates (with feedback)", :short => :c
34
+ end
35
+ else # `choose` is default command: can be called without explicit specification
36
+ Trollop::options do
37
+ version VERSION_STR
38
+ banner "#{VERSION_STR}\nOptions for `choose` command:"
39
+ opt :weather, "Current weather score [0-9]", :short => :w, :default => 5.0
40
+ opt :time, "Time commitment score [0-9]", :short => :t, :default => 5.0
41
+ end
42
+ end
43
+
44
+ if ARGV.length < 1
45
+ Trollop::die "Ideas file required"
46
+ exit 1
47
+ end
48
+
49
+ ideas = eval(File.read(ARGV.first))
50
+
51
+ if "list" == cmd
52
+ if cmd_opts[:complete] and cmd_opts[:incomplete]
53
+ Trollop::die "-i and -c are mutually exclusive options for `list`"
54
+ else
55
+ puts ideas.list(cmd_opts)
56
+ end
57
+ elsif "choose" == cmd
58
+ puts ideas.choose(cmd_opts[:weather], cmd_opts[:time])
59
+ else
60
+ puts ideas.choose(cmd_opts[:weather], cmd_opts[:time])
61
+ end
@@ -0,0 +1,70 @@
1
+ class DateIdea
2
+
3
+ def initialize(name = "")
4
+ @name = name.to_s
5
+ @attributes = {}
6
+ @requirements = {}
7
+ @outcome = nil
8
+ end
9
+
10
+ def kind(args)
11
+ args.each do |field, value|
12
+ @attributes[field] = value
13
+ end
14
+ end
15
+
16
+ def requires(args)
17
+ args.each do |field, value|
18
+ @requirements[field] = value
19
+ end
20
+ end
21
+
22
+ def outcome(score = nil)
23
+ if score.nil?
24
+ @outcome
25
+ else
26
+ @outcome = score
27
+ end
28
+ end
29
+
30
+ def outcome_safe
31
+ return @outcome unless @outcome.nil?
32
+ -1
33
+ end
34
+
35
+ def attributes
36
+ @attributes
37
+ end
38
+
39
+ def attribute(name)
40
+ @attributes[name]
41
+ end
42
+
43
+ def requirements
44
+ @requirements
45
+ end
46
+
47
+ def name
48
+ @name
49
+ end
50
+
51
+ def score
52
+ @outcome
53
+ end
54
+
55
+ def stub?
56
+ @attributes.empty?
57
+ end
58
+
59
+ def complete?
60
+ not @outcome.nil?
61
+ end
62
+
63
+ def to_s
64
+ attr_str = @attributes.map { |k,v| "#{k}: #{v}" }.join(", ")
65
+ date_str = "#{@name}: (#{attr_str})"
66
+ date_str = date_str + ", outcome: #{@outcome}" unless @outcome.nil?
67
+ "#{date_str}\n"
68
+ end
69
+
70
+ end
@@ -0,0 +1,96 @@
1
+ class DateIdeas
2
+
3
+ ATTRIBUTES = %w(adventure romantic athletic intellectual)
4
+
5
+ def initialize(&block)
6
+ @date_ideas = []
7
+ instance_eval &block
8
+ end
9
+
10
+ def ideas
11
+ @date_ideas
12
+ end
13
+
14
+ def date(name, &block)
15
+ date = DateIdea.new(name)
16
+ if block
17
+ date.instance_eval(&block)
18
+ end
19
+ @date_ideas << date
20
+ end
21
+
22
+ def list(opts)
23
+ if opts[:complete]
24
+ render complete
25
+ elsif opts[:incomplete]
26
+ render incomplete
27
+ else
28
+ render
29
+ end
30
+ end
31
+
32
+ def render(ideas = nil)
33
+ if nil == ideas
34
+ ideas = @date_ideas
35
+ end
36
+ str = "Date ideas:\n"
37
+ ideas.each do |f|
38
+ if not f.complete?
39
+ str << " #{f.to_s}".blue
40
+ elsif f.complete? and f.score >= 6.0
41
+ str << " #{f.to_s}".green
42
+ elsif f.complete? and f.score < 6.0
43
+ str << " #{f.to_s}".red
44
+ else
45
+ raise Exception("We should never reach this.")
46
+ end
47
+ end
48
+ str
49
+ end
50
+
51
+ def to_s
52
+ render
53
+ end
54
+
55
+ def complete
56
+ @date_ideas.reject { |idea| idea.stub? or not idea.complete? }
57
+ end
58
+
59
+ def incomplete
60
+ @date_ideas.reject { |idea| idea.stub? or idea.complete? }
61
+ end
62
+
63
+ def extract_features(idea_list)
64
+ keys = idea_list.reduce([]) { |acc, idea|
65
+ acc << idea.attributes.keys
66
+ }.flatten.uniq
67
+ features = idea_list.map { |idea|
68
+ attributes = idea.attributes.map { |key, val|
69
+ [keys.index(key), val]
70
+ }.sort_by {|k| k[0]}.map { |e| e[1] }
71
+ [attributes, idea.outcome_safe]
72
+ }
73
+ [keys, features]
74
+ end
75
+
76
+ def fitted_model
77
+ dimensions, examples = complete_features
78
+ terms = examples.map { |examples|
79
+ examples[0] + examples[0].permutation(2).to_a.map { |term| term.reduce(1) {|acc,k| acc*k } }
80
+ }
81
+ end
82
+
83
+ def complete_features
84
+ extract_features(complete)
85
+ end
86
+
87
+ def incomplete_features
88
+ extract_features(incomplete)
89
+ end
90
+
91
+ def choose(weather, time)
92
+ fitted_model
93
+ incomplete.first
94
+ end
95
+
96
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+
3
+ require 'colorize'
4
+ require 'statsample'
5
+ require 'trollop'
6
+ require 'date_ideas'
7
+ require 'date_idea'
8
+
9
+ $LOAD_PATH.shift
@@ -0,0 +1,3 @@
1
+ module Dates
2
+ VERSION = "1.0.1"
3
+ end
@@ -0,0 +1,76 @@
1
+ require File.dirname(__FILE__)+ '/../lib/dates'
2
+
3
+ ideas = DateIdeas.new do
4
+
5
+ date "watch primer in north" do
6
+ kind :adventure => 2, :romantic => 4, :intellectual => 6, :athletic => 0
7
+ requires :weather => 0, :hours => 2
8
+ outcome 20
9
+ end
10
+
11
+ date "very close to primer" do
12
+ kind :adventure => 2, :romantic => 4, :intellectual => 6, :athletic => 1
13
+ requires :weather => 0, :hours => 2
14
+ end
15
+
16
+ date "very far from primer" do
17
+ kind :adventure => 10, :romantic => 10, :intellectual => 0, :athletic => 10
18
+ requires :weather => 0, :hours => 2
19
+ end
20
+
21
+ date "escher in the DiVE" do
22
+ kind :adventure => 4, :romantic => 3, :intellectual => 4, :athletic => 0
23
+ requires :weather => 0, :hours => 2
24
+ end
25
+
26
+ date "mushroom hunting" do
27
+ kind :adventure => 6, :romantic => 3, :intellectual => 5, :athletic => 3
28
+ requires :weather => 5, :hours => 2
29
+ end
30
+
31
+ date "duke observatory" do
32
+ kind :adventure => 100, :romantic => 7, :intellectual => 5, :athletic => 1
33
+ requires :weather => 8, :hours => 4
34
+ end
35
+
36
+ end
37
+
38
+ valid_attributes = [:adventure, :romantic, :intellectual, :athletic]
39
+
40
+ describe Date do
41
+
42
+ describe "interprets an ideas file" do
43
+ it "correctly identifies incomplete and completed dates" do
44
+ ideas.incomplete.length.should == 5
45
+ ideas.complete.length.should == 1
46
+ ideas.complete.first.name == "watch primer in north"
47
+ end
48
+
49
+ it "correctly loads the attributes and requirements for each date" do
50
+ ideas.ideas.map { |idea|
51
+ if idea.stub?
52
+ idea.attributes.empty?.should == true
53
+ else
54
+ idea.attributes.length.should == valid_attributes.length
55
+ idea.attributes.map { |name, val|
56
+ valid_attributes.member?(name).should == true
57
+ }
58
+ end
59
+ }
60
+ end
61
+ end
62
+
63
+ describe "list command" do
64
+ it "lists the contents of a dates file correctly" do
65
+ ideas.ideas.length.should == 6
66
+ end
67
+ end
68
+
69
+ describe "choose command" do
70
+ it "chooses a date from the incomplete dates list" do
71
+ choosed = ideas.choose(2, 2)
72
+ ideas.incomplete.member?(choosed).should == true
73
+ end
74
+ end
75
+
76
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dates
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Max Hodak, Daphne Ezer
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-15 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: statsample
16
+ requirement: &70326830794380 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70326830794380
25
+ - !ruby/object:Gem::Dependency
26
+ name: colorize
27
+ requirement: &70326830793940 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70326830793940
36
+ - !ruby/object:Gem::Dependency
37
+ name: trollop
38
+ requirement: &70326830793520 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70326830793520
47
+ description: Dates keeps track of things you two have been meaning to do, and learns
48
+ to make good suggestions as you teach it.
49
+ email: maxhodak@gmail.com
50
+ executables:
51
+ - dates
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - README.mdown
56
+ - bin/dates
57
+ - lib/date_idea.rb
58
+ - lib/date_ideas.rb
59
+ - lib/dates.rb
60
+ - lib/version.rb
61
+ - test/spec.rb
62
+ homepage: http://www.github.com/maxhodak/dates
63
+ licenses: []
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 1.8.6
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: Dates keeps track of things you two have been meaning to do.
86
+ test_files: []
87
+ has_rdoc: