simple_pvr 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/public/js/app.js CHANGED
@@ -1,12 +1,19 @@
1
1
  'use strict';
2
2
 
3
- angular.module('simplePvr', ['simplePvrServices']).
4
- directive('pvrAutocomplete', function() {
5
- return function(scope, element, attrs) {
6
- element.typeahead({
7
- source: scope.autocomplete
8
- });
9
- }
3
+ angular.module('simplePvr', ['simplePvrServices', 'simplePvrFilters']).
4
+ directive('titleSearch', function() {
5
+ return {
6
+ templateUrl: '/app/templates/titleSearch.html',
7
+ restrict: 'E',
8
+ replace: true,
9
+ controller: SearchProgrammesCtrl,
10
+ link: function(scope, element, attributes, controller) {
11
+ element.find('input').typeahead({
12
+ source: scope.autocomplete,
13
+ updater: scope.updater
14
+ });
15
+ }
16
+ };
10
17
  }).
11
18
  directive('navbarItem', function($location) {
12
19
  return {
@@ -29,73 +36,6 @@ directive('navbarItem', function($location) {
29
36
  }
30
37
  };
31
38
  }).
32
- filter('chunk', function() {
33
- function chunkArray(array, chunkSize) {
34
- var result = [];
35
- var currentChunk = [];
36
- for (var i=0; i<array.length; i++) {
37
- currentChunk.push(array[i]);
38
- if (currentChunk.length == chunkSize) {
39
- result.push(currentChunk);
40
- currentChunk = [];
41
- }
42
- }
43
- if (currentChunk.length > 0) {
44
- result.push(currentChunk);
45
- }
46
- return result;
47
- }
48
-
49
- function defineHashKeys(array) {
50
- for (var i=0; i<array.length; i++) {
51
- array[i].$$hashKey = i;
52
- }
53
- }
54
-
55
- return function(array, chunkSize) {
56
- if (!(array instanceof Array)) return array;
57
- if (!chunkSize) return array;
58
- var result = chunkArray(array, chunkSize);
59
- defineHashKeys(result);
60
- return result;
61
- }
62
- }).
63
- filter('formatEpisode', function() {
64
- return function(episodeNum) {
65
- return episodeNum ? episodeNum.replace(' .', '').replace('. ', '') : "";
66
- }
67
- }).
68
- filter('filteredWeekdays', function() {
69
- return function(schedule) {
70
- if (!schedule.filter_by_weekday) {
71
- return '';
72
- }
73
-
74
- var selectedWeekdays = [];
75
- if (schedule.monday) selectedWeekdays.push('Mondays');
76
- if (schedule.tuesday) selectedWeekdays.push('Tuesdays');
77
- if (schedule.wednesday) selectedWeekdays.push('Wednesdays');
78
- if (schedule.thursday) selectedWeekdays.push('Thursdays');
79
- if (schedule.friday) selectedWeekdays.push('Fridays');
80
- if (schedule.saturday) selectedWeekdays.push('Saturdays');
81
- if (schedule.sunday) selectedWeekdays.push('Sundays');
82
-
83
- var days = '';
84
- if (selectedWeekdays.length == 0) {
85
- return ''
86
- } else if (selectedWeekdays.length == 1) {
87
- days = selectedWeekdays[0];
88
- } else if (selectedWeekdays.length == 2) {
89
- days = selectedWeekdays[0] + ' and ' + selectedWeekdays[1];
90
- } else {
91
- for (var i=0; i<selectedWeekdays.length - 1; i++) {
92
- days += selectedWeekdays[i] + ', ';
93
- }
94
- days += 'and ' + selectedWeekdays[selectedWeekdays.length - 1];
95
- }
96
- return '(' + days + ')';
97
- }
98
- }).
99
39
  config(function($routeProvider, $locationProvider) {
100
40
  $locationProvider.html5Mode(true).hashPrefix('');
101
41
 
@@ -137,10 +137,16 @@ function SearchProgrammesCtrl($scope, $http, $location) {
137
137
  $scope.autocomplete = function(query, process) {
138
138
  $http.get('/api/programmes/title_search', {params: {query: query}}).success(process);
139
139
  }
140
+
141
+ $scope.updater = function(item) {
142
+ $scope.$apply(function() {
143
+ $scope.title = item;
144
+ });
145
+ return item;
146
+ }
140
147
 
141
148
  $scope.search = function() {
142
- var query = $("#programme-search-query").val();
143
- $location.path('/search').search({query: query});
149
+ $location.path('/search').search({query: $scope.title});
144
150
  }
145
151
  }
146
152
 
@@ -0,0 +1,101 @@
1
+ 'use strict';
2
+
3
+ angular.module('simplePvrFilters', []).
4
+ filter('chunk', function() {
5
+ function chunkArray(array, chunkSize) {
6
+ var result = [];
7
+ var currentChunk = [];
8
+ for (var i=0; i<array.length; i++) {
9
+ currentChunk.push(array[i]);
10
+ if (currentChunk.length == chunkSize) {
11
+ result.push(currentChunk);
12
+ currentChunk = [];
13
+ }
14
+ }
15
+ if (currentChunk.length > 0) {
16
+ result.push(currentChunk);
17
+ }
18
+ return result;
19
+ }
20
+
21
+ function defineHashKeys(array) {
22
+ for (var i=0; i<array.length; i++) {
23
+ array[i].$$hashKey = i;
24
+ }
25
+ }
26
+
27
+ return function(array, chunkSize) {
28
+ if (!(array instanceof Array)) return array;
29
+ if (!chunkSize) return array;
30
+ var result = chunkArray(array, chunkSize);
31
+ defineHashKeys(result);
32
+ return result;
33
+ }
34
+ }).
35
+ filter('formatEpisode', function() {
36
+ return function(episodeNum) {
37
+ return episodeNum ? episodeNum.replace(' .', '').replace('. ', '') : '';
38
+ }
39
+ }).
40
+ filter('filteredWeekdays', function() {
41
+ return function(schedule) {
42
+ if (!schedule.filter_by_weekday) {
43
+ return '';
44
+ }
45
+
46
+ var selectedWeekdays = [];
47
+ if (schedule.monday) selectedWeekdays.push('Mondays');
48
+ if (schedule.tuesday) selectedWeekdays.push('Tuesdays');
49
+ if (schedule.wednesday) selectedWeekdays.push('Wednesdays');
50
+ if (schedule.thursday) selectedWeekdays.push('Thursdays');
51
+ if (schedule.friday) selectedWeekdays.push('Fridays');
52
+ if (schedule.saturday) selectedWeekdays.push('Saturdays');
53
+ if (schedule.sunday) selectedWeekdays.push('Sundays');
54
+
55
+ var days = '';
56
+ if (selectedWeekdays.length == 0) {
57
+ return ''
58
+ } else if (selectedWeekdays.length == 1) {
59
+ days = selectedWeekdays[0];
60
+ } else if (selectedWeekdays.length == 2) {
61
+ days = selectedWeekdays[0] + ' and ' + selectedWeekdays[1];
62
+ } else {
63
+ for (var i=0; i<selectedWeekdays.length - 1; i++) {
64
+ days += selectedWeekdays[i] + ', ';
65
+ }
66
+ days += 'and ' + selectedWeekdays[selectedWeekdays.length - 1];
67
+ }
68
+ return '(' + days + ')';
69
+ }
70
+ }).
71
+ filter('timeOfDay', function() {
72
+ return function(schedule) {
73
+ if (!schedule.filter_by_time_of_day) {
74
+ return '';
75
+ }
76
+ if (schedule.from_time_of_day && schedule.to_time_of_day) {
77
+ return '(between ' + schedule.from_time_of_day + ' and ' + schedule.to_time_of_day + ')';
78
+ }
79
+ if (schedule.from_time_of_day) {
80
+ return '(after ' + schedule.from_time_of_day + ')';
81
+ }
82
+ if (schedule.to_time_of_day) {
83
+ return '(before ' + schedule.to_time_of_day + ')';
84
+ }
85
+ }
86
+ }).
87
+ filter('startEarlyEndLate', function() {
88
+ return function(schedule) {
89
+ var sentences = [];
90
+ if (schedule.custom_start_early_minutes) {
91
+ sentences.push('starts ' + schedule.custom_start_early_minutes + ' minutes early');
92
+ }
93
+ if (schedule.custom_end_late_minutes) {
94
+ sentences.push('ends ' + schedule.custom_end_late_minutes + ' minutes late');
95
+ }
96
+ if (sentences.length > 0) {
97
+ return '(' + sentences.join(', ') + ')';
98
+ }
99
+ return '';
100
+ }
101
+ });
@@ -13,7 +13,7 @@
13
13
  <p>
14
14
  <button class="btn btn-primary" type="button" ng-click="recordOnThisChannel()">Record on this channel</button>
15
15
  <button class="btn btn-primary" type="button" ng-click="recordOnAnyChannel()">Record on any channel</button>
16
- <button class="btn btn-primary" type="button" ng-click="recordJustThisProgramme()">Record just this programme</button>
16
+ <button ng-hide="programme.is_outdated" class="btn btn-primary" type="button" ng-click="recordJustThisProgramme()">Record just this programme</button>
17
17
  </p>
18
18
  </div>
19
19
  </div>
@@ -1,5 +1,5 @@
1
- <div class="row">
2
- <form class="form-horizontal">
1
+ <div class="row" ng-show="schedule">
2
+ <form class="form-horizontal" name="scheduleForm">
3
3
  <div class="control-group">
4
4
  <label class="control-label" for="showName">Name of show</label>
5
5
  <div class="controls">
@@ -14,65 +14,105 @@
14
14
  </select>
15
15
  </div>
16
16
  </div>
17
+
18
+ <div class="control-group" ng-class="{error: scheduleForm.startEarly.$invalid}">
19
+ <label class="control-label" for="startEarly">Start early</label>
20
+ <div class="controls">
21
+ <div class="input-append">
22
+ <input type="text" class="input-mini" style="text-align: right" ng-model="schedule.custom_start_early_minutes" ng-pattern="/^[0-9]*$/" name="startEarly" id="startEarly" placeholder="2">
23
+ <span class="add-on">minutes</span>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ <div class="control-group" ng-class="{error: scheduleForm.endLate.$invalid}">
28
+ <label class="control-label" for="endLate">End late</label>
29
+ <div class="controls">
30
+ <div class="input-append">
31
+ <input type="text" class="input-mini" style="text-align: right" ng-model="schedule.custom_end_late_minutes" ng-pattern="/^[0-9]*$/" name="endLate" id="endLate" placeholder="5">
32
+ <span class="add-on">minutes</span>
33
+ </div>
34
+ </div>
35
+ </div>
36
+
37
+ <div class="control-group">
38
+ <div class="controls">
39
+ <label class="checkbox" for="filterByTimeOfDay">
40
+ <input type="checkbox" id="filterByTimeOfDay" ng-model="schedule.filter_by_time_of_day">Filter by time of day
41
+ </label>
42
+ </div>
43
+ </div>
44
+ <div class="control-group" ng-show="schedule.filter_by_time_of_day" ng-class="{error: scheduleForm.startTime.$invalid}">
45
+ <label class="control-label" for="startTime">From</label>
46
+ <div class="controls">
47
+ <input type="text" class="input-mini" style="text-align: right" ng-model="schedule.from_time_of_day" ng-pattern="/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/" name="startTime" id="startTime" placeholder="00:00">
48
+ </div>
49
+ </div>
50
+ <div class="control-group" ng-show="schedule.filter_by_time_of_day">
51
+ <label class="control-label" for="endTime">To</label>
52
+ <div class="controls">
53
+ <input type="text" class="input-mini" style="text-align: right" ng-model="schedule.to_time_of_day" ng-pattern="/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/" id="endTime" placeholder="00:00">
54
+ </div>
55
+ </div>
56
+
17
57
  <div class="control-group">
18
58
  <div class="controls">
19
59
  <label class="checkbox" for="filterByWeekday">
20
- <input type="checkbox" ng-model="schedule.filter_by_weekday">Filter by weekday
60
+ <input type="checkbox" id="filterByWeekday" ng-model="schedule.filter_by_weekday">Filter by weekday
21
61
  </label>
22
62
  </div>
23
63
  </div>
24
64
  <div class="control-group" ng-show="schedule.filter_by_weekday">
25
65
  <div class="controls">
26
66
  <label class="checkbox" for="recordMondays">
27
- <input type="checkbox" ng-model="schedule.monday">Record Mondays
67
+ <input type="checkbox" id="recordMondays" ng-model="schedule.monday">Record Mondays
28
68
  </label>
29
69
  </div>
30
70
  </div>
31
71
  <div class="control-group" ng-show="schedule.filter_by_weekday">
32
72
  <div class="controls">
33
73
  <label class="checkbox" for="recordTuesdays">
34
- <input type="checkbox" ng-model="schedule.tuesday">Record Tuesdays
74
+ <input type="checkbox" id="recordTuesdays" ng-model="schedule.tuesday">Record Tuesdays
35
75
  </label>
36
76
  </div>
37
77
  </div>
38
78
  <div class="control-group" ng-show="schedule.filter_by_weekday">
39
79
  <div class="controls">
40
80
  <label class="checkbox" for="recordWednesdays">
41
- <input type="checkbox" ng-model="schedule.wednesday">Record Wednesdays
81
+ <input type="checkbox" id="recordWednesdays" ng-model="schedule.wednesday">Record Wednesdays
42
82
  </label>
43
83
  </div>
44
84
  </div>
45
85
  <div class="control-group" ng-show="schedule.filter_by_weekday">
46
86
  <div class="controls">
47
87
  <label class="checkbox" for="recordThursdays">
48
- <input type="checkbox" ng-model="schedule.thursday">Record Thursdays
88
+ <input type="checkbox" id="recordThursdays" ng-model="schedule.thursday">Record Thursdays
49
89
  </label>
50
90
  </div>
51
91
  </div>
52
92
  <div class="control-group" ng-show="schedule.filter_by_weekday">
53
93
  <div class="controls">
54
94
  <label class="checkbox" for="recordFridays">
55
- <input type="checkbox" ng-model="schedule.friday">Record Fridays
95
+ <input type="checkbox" id="recordFridays" ng-model="schedule.friday">Record Fridays
56
96
  </label>
57
97
  </div>
58
98
  </div>
59
99
  <div class="control-group" ng-show="schedule.filter_by_weekday">
60
100
  <div class="controls">
61
101
  <label class="checkbox" for="recordSaturdays">
62
- <input type="checkbox" ng-model="schedule.saturday">Record Saturdays
102
+ <input type="checkbox" id="recordSaturdays" ng-model="schedule.saturday">Record Saturdays
63
103
  </label>
64
104
  </div>
65
105
  </div>
66
106
  <div class="control-group" ng-show="schedule.filter_by_weekday">
67
107
  <div class="controls">
68
108
  <label class="checkbox" for="recordSundays">
69
- <input type="checkbox" ng-model="schedule.sunday">Record Sundays
109
+ <input type="checkbox" id="recordSundays" ng-model="schedule.sunday">Record Sundays
70
110
  </label>
71
111
  </div>
72
112
  </div>
73
113
  <div class="control-group">
74
114
  <div class="controls">
75
- <button type="submit" class="btn btn-primary" ng-click="update()">Update</button>
115
+ <button type="submit" class="btn btn-primary" ng-class="{disabled: scheduleForm.$invalid}" ng-click="update()" ng-disabled="scheduleForm.$invalid">Update</button>
76
116
  <a ng-href="/schedules" class="btn">Cancel</a>
77
117
  </div>
78
118
  </div>
@@ -6,7 +6,7 @@
6
6
  <p><span ng-show="schedule.is_exception">Exception:</span> {{schedule.title}}
7
7
  <span ng-show="schedule.channel">on {{schedule.channel.name}}</span>
8
8
  <span ng-show="schedule.start_time">{{schedule.start_time | date:'fullDate'}} at {{schedule.start_time | date:'HH:mm'}}</span>
9
- {{schedule | filteredWeekdays}}
9
+ {{schedule | filteredWeekdays}} {{schedule | timeOfDay}} {{schedule | startEarlyEndLate}}
10
10
  </p>
11
11
  <p>
12
12
  <a ng-href="/schedules/{{schedule.id}}" class="btn">Edit</a>
@@ -0,0 +1,4 @@
1
+ <form class="navbar-form pull-right" ng-submit="search()">
2
+ <input class="span2" name="programme-search-query" type="text" ng-model="title" placeholder="Search programmes...">
3
+ <button type="submit" class="btn">Search</button>
4
+ </form>
data/simple_pvr.gemspec CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |gem|
22
22
  gem.add_dependency 'data_mapper', '~> 1.2'
23
23
  gem.add_dependency 'dm-sqlite-adapter', '~> 1.2'
24
24
  gem.add_dependency 'sinatra', '~> 1.3'
25
+ gem.add_dependency 'puma', '~> 1.6'
25
26
 
26
27
  gem.add_development_dependency 'rake', '>= 10.0.0'
27
28
  gem.add_development_dependency 'rspec', '~> 2.11'
@@ -15,6 +15,17 @@ describe SimplePvr::Model::Programme do
15
15
  @dr_1 = Channel.add('DR 1', 23000000, 1098)
16
16
  @dr_2 = Channel.add('DR 2', 24000000, 1099)
17
17
  end
18
+
19
+ it 'knows its end time' do
20
+ programme = Programme.new(start_time: Time.local(2012, 7, 17, 20, 30), duration: 50.minutes)
21
+
22
+ programme.end_time.should == Time.local(2012, 7, 17, 21, 20)
23
+ end
24
+
25
+ it 'knows when it is outdated' do
26
+ Programme.new(start_time: 10.minutes.ago, duration: 9.minutes).should be_outdated
27
+ Programme.new(start_time: 10.minutes.ago, duration: 11.minutes).should_not be_outdated
28
+ end
18
29
 
19
30
  it 'can insert programmes' do
20
31
  3.times { Programme.add(@dr_1, 'Title', 'Subtitle', 'Description', Time.local(2012, 7, 17, 20, 30), 50.minutes, ' .4/12. ') }
@@ -13,7 +13,7 @@ describe SimplePvr::Model::Schedule do
13
13
  end
14
14
 
15
15
  it 'can save a schedule with a title' do
16
- Schedule.add_specification(:title => 'Sports')
16
+ Schedule.add_specification(title: 'Sports')
17
17
 
18
18
  schedules = Schedule.all
19
19
  schedules.length.should == 1
@@ -23,7 +23,7 @@ describe SimplePvr::Model::Schedule do
23
23
  end
24
24
 
25
25
  it 'can save a schedule with a title and a channel' do
26
- Schedule.add_specification(:title => 'Sports', :channel => @dr_1)
26
+ Schedule.add_specification(title: 'Sports', channel: @dr_1)
27
27
 
28
28
  schedules = Schedule.all
29
29
  schedules.length.should == 1
@@ -42,6 +42,37 @@ describe SimplePvr::Model::Schedule do
42
42
  schedules[0].title.should == 'Sports'
43
43
  schedules[0].channel.name.should == 'DR 1'
44
44
  schedules[0].start_time.should == start_time
45
+ end
46
+
47
+ it 'starts 2 minutes early and 5 minutes late by default' do
48
+ schedule = Schedule.new
49
+
50
+ schedule.custom_start_early_minutes.should be_nil
51
+ schedule.custom_end_late_minutes.should be_nil
52
+ schedule.start_early_minutes.should == 2
53
+ schedule.end_late_minutes.should == 5
54
+ end
55
+
56
+ it 'can have custom start early and end late intervals' do
57
+ schedule = Schedule.new(custom_start_early_minutes: 5, custom_end_late_minutes: 10)
58
+
59
+ schedule.custom_start_early_minutes.should == 5
60
+ schedule.custom_end_late_minutes.should == 10
61
+ schedule.start_early_minutes.should == 5
62
+ schedule.end_late_minutes.should == 10
63
+ end
64
+
65
+ it 'can clean up schedules that are out of date' do
66
+ Schedule.add_specification(title: 'Old Sports News', start_time: 60.minutes.ago, end_time: 10.minutes.ago)
67
+ Schedule.add_specification(title: 'Current Sports News', start_time: 5.minutes.ago, end_time: 10.minutes.from_now)
68
+ Schedule.add_specification(title: 'Upcoming Sports News', start_time: 1.hour.from_now, end_time: 2.hours.from_now)
69
+ Schedule.add_specification(title: 'Great movies')
45
70
 
71
+ Schedule.cleanup
72
+ remaining_names = Schedule.all.collect {|s| s.title }
73
+ remaining_names.should include('Current Sports News')
74
+ remaining_names.should include('Upcoming Sports News')
75
+ remaining_names.should include('Great movies')
76
+ remaining_names.should_not include('Old Sports News')
46
77
  end
47
78
  end