Commit 6cdc409cefb859ae3f893cda12dd1f535972e52e

Authored by AntonioTerceiro
1 parent 68b5c1da

ActionItem26: displaying as calendar

and several other things


git-svn-id: https://svn.colivre.coop.br/svn/noosfero/trunk@1886 3f533792-8f58-4932-b0fe-aaf55b0a4547
app/controllers/public/search_controller.rb
@@ -16,7 +16,7 @@ class SearchController < ApplicationController @@ -16,7 +16,7 @@ class SearchController < ApplicationController
16 @search_in = SEARCH_IN 16 @search_in = SEARCH_IN
17 @searching = {} 17 @searching = {}
18 @search_in.each do |key, name| 18 @search_in.each do |key, name|
19 - @searching[key] = params[:find_in].nil? || params[:find_in].empty? || params[:find_in].include?(key.to_s) 19 + @searching[key] = (params[:asset].blank? && (params[:find_in].nil? || params[:find_in].empty? || params[:find_in].include?(key.to_s))) || (params[:asset] == key.to_s)
20 end 20 end
21 end 21 end
22 22
@@ -91,6 +91,33 @@ class SearchController < ApplicationController @@ -91,6 +91,33 @@ class SearchController < ApplicationController
91 end 91 end
92 end 92 end
93 93
  94 + def events
  95 + @events = @results[:events]
  96 + @calendar = Event.date_range(params[:year], params[:month]).map do |date|
  97 + [
  98 + # the day itself
  99 + date,
  100 + # list of events of that day
  101 + @events.select do |event|
  102 + event.date_range.include?(date)
  103 + end,
  104 + # is this date in the current month?
  105 + true
  106 + ]
  107 + end
  108 +
  109 + # pad with days before
  110 + while @calendar.first.first.wday != 0
  111 + @calendar.unshift([@calendar.first.first - 1.day, [], false])
  112 + end
  113 +
  114 + # pad with days after (until Saturday)
  115 + while @calendar.last.first.wday != 6
  116 + @calendar << [@calendar.last.first + 1.day, [], false]
  117 + end
  118 +
  119 + end
  120 +
94 ####################################################### 121 #######################################################
95 122
96 # view the summary of one category 123 # view the summary of one category
@@ -104,7 +131,7 @@ class SearchController &lt; ApplicationController @@ -104,7 +131,7 @@ class SearchController &lt; ApplicationController
104 [ :comments, _('Recent comments'), @finder.recent('comments') ], 131 [ :comments, _('Recent comments'), @finder.recent('comments') ],
105 [ :most_commented_articles, _('Most commented articles'), @finder.most_commented_articles ], 132 [ :most_commented_articles, _('Most commented articles'), @finder.most_commented_articles ],
106 [ :enterprises, _('Recently created enterprises'), @finder.recent('enterprises') ], 133 [ :enterprises, _('Recently created enterprises'), @finder.recent('enterprises') ],
107 - [ :events, _('Recently added events'), @finder.recent('events') ] 134 + [ :events, _('Recently added events'), @finder.current_events(params[:year], params[:month]) ]
108 ].each do |key, name, list| 135 ].each do |key, name, list|
109 @results[key] = list 136 @results[key] = list
110 @names[key] = name 137 @names[key] = name
@@ -112,12 +139,12 @@ class SearchController &lt; ApplicationController @@ -112,12 +139,12 @@ class SearchController &lt; ApplicationController
112 end 139 end
113 attr_reader :category 140 attr_reader :category
114 141
115 - def assets  
116 - @results = { @asset => @finder.recent(@asset, LIST_LIMIT) } 142 + #def assets
  143 + #@results = { @asset => @finder.recent(@asset, LIST_LIMIT) }
117 144
118 - @asset_name = gettext(SEARCH_IN.find { |entry| entry.first == @asset }[1])  
119 - @names = { @asset => @asset_name }  
120 - end 145 + #@asset_name = gettext(SEARCH_IN.find { |entry| entry.first == @asset }[1])
  146 + #@names = { @asset => @asset_name }
  147 + #end
121 148
122 def directory 149 def directory
123 @results = { @asset => @finder.find_by_initial(@asset, params[:initial]) } 150 @results = { @asset => @finder.find_by_initial(@asset, params[:initial]) }
app/helpers/application_helper.rb
@@ -13,6 +13,8 @@ module ApplicationHelper @@ -13,6 +13,8 @@ module ApplicationHelper
13 include AssetsHelper 13 include AssetsHelper
14 14
15 include BlockHelper 15 include BlockHelper
  16 +
  17 + include DatesHelper
16 18
17 # Displays context help. You can pass the content of the help message as the 19 # Displays context help. You can pass the content of the help message as the
18 # first parameter or using template code inside a block passed to this 20 # first parameter or using template code inside a block passed to this
@@ -457,32 +459,6 @@ module ApplicationHelper @@ -457,32 +459,6 @@ module ApplicationHelper
457 459
458 end 460 end
459 461
460 - # formats a date for displaying.  
461 - def show_date(date)  
462 - if date  
463 - date.strftime(_('%d %B %Y'))  
464 - else  
465 - ''  
466 - end  
467 - end  
468 -  
469 - # formats a datetime for displaying.  
470 - def show_time(time)  
471 - if time  
472 - time.strftime(_('%d %B %Y, %H:%m'))  
473 - else  
474 - ''  
475 - end  
476 - end  
477 -  
478 - def show_period(date1, date2 = nil)  
479 - if (date1 == date2) || (date2.nil?)  
480 - show_date(date1)  
481 - else  
482 - _('from %s to %s') % [show_date(date1), show_date(date2)]  
483 - end  
484 - end  
485 -  
486 def gravatar_url_for(email, options = {}) 462 def gravatar_url_for(email, options = {})
487 # Ta dando erro de roteamento 463 # Ta dando erro de roteamento
488 url_for( { :gravatar_id => Digest::MD5.hexdigest(email), 464 url_for( { :gravatar_id => Digest::MD5.hexdigest(email),
app/helpers/dates_helper.rb 0 → 100644
@@ -0,0 +1,89 @@ @@ -0,0 +1,89 @@
  1 +module DatesHelper
  2 +
  3 + include GetText
  4 +
  5 + # formats a date for displaying.
  6 + def show_date(date)
  7 + if date
  8 + date.strftime(_('%d %B %Y'))
  9 + else
  10 + ''
  11 + end
  12 + end
  13 +
  14 + # formats a datetime for displaying.
  15 + def show_time(time)
  16 + if time
  17 + time.strftime(_('%d %B %Y, %H:%m'))
  18 + else
  19 + ''
  20 + end
  21 + end
  22 +
  23 + def show_period(date1, date2 = nil)
  24 + if (date1 == date2) || (date2.nil?)
  25 + show_date(date1)
  26 + else
  27 + _('from %s to %s') % [show_date(date1), show_date(date2)]
  28 + end
  29 + end
  30 +
  31 + def show_day_of_week(date)
  32 + # FIXME Date#strftime should translate this for us !!!!
  33 + _([
  34 + N_('Sunday'),
  35 + N_('Monday'),
  36 + N_('Tuesday'),
  37 + N_('Wednesday'),
  38 + N_('Thursday'),
  39 + N_('Friday'),
  40 + N_('Saturday'),
  41 + ][date.wday])
  42 + end
  43 +
  44 + def show_month(year, month)
  45 + # FIXME Date#strftime should translate this for us !!!
  46 + monthname = _([
  47 + N_('January'),
  48 + N_('February'),
  49 + N_('March'),
  50 + N_('April'),
  51 + N_('May'),
  52 + N_('June'),
  53 + N_('July'),
  54 + N_('August'),
  55 + N_('September'),
  56 + N_('October'),
  57 + N_('November'),
  58 + N_('December')
  59 + ][month.to_i - 1])
  60 +
  61 + _('%{month} %{year}') % { :year => year, :month => monthname }
  62 + end
  63 +
  64 + def link_to_previous_month(year, month)
  65 + year = year.to_i
  66 + month = month.to_i
  67 + if month == 1
  68 + year -= 1
  69 + month = 12
  70 + else
  71 + month -= 1
  72 + end
  73 +
  74 + link_to '&larr; ' + show_month(year, month), :year => year, :month => month
  75 + end
  76 +
  77 + def link_to_next_month(year, month)
  78 + year = year.to_i
  79 + month = month.to_i
  80 + if month == 12
  81 + year += 1
  82 + month = 1
  83 + else
  84 + month += 1
  85 + end
  86 +
  87 + link_to show_month(year, month) + ' &rarr;', :year => year, :month => month
  88 + end
  89 +end
app/models/category.rb
@@ -25,6 +25,8 @@ class Category &lt; ActiveRecord::Base @@ -25,6 +25,8 @@ class Category &lt; ActiveRecord::Base
25 has_many :articles, :through => :article_categorizations 25 has_many :articles, :through => :article_categorizations
26 has_many :comments, :through => :articles 26 has_many :comments, :through => :articles
27 27
  28 + has_many :events, :through => :article_categorizations, :class_name => 'Event', :source => :article
  29 +
28 has_many :profile_categorizations 30 has_many :profile_categorizations
29 has_many :enterprises, :through => :profile_categorizations, :source => :profile, :class_name => 'Enterprise' 31 has_many :enterprises, :through => :profile_categorizations, :source => :profile, :class_name => 'Enterprise'
30 has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person' 32 has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person'
app/models/category_finder.rb
@@ -31,6 +31,12 @@ class CategoryFinder @@ -31,6 +31,12 @@ class CategoryFinder
31 Article.find(:all, options_for_find(Article, :limit => limit, :order => 'comments_count DESC')) 31 Article.find(:all, options_for_find(Article, :limit => limit, :order => 'comments_count DESC'))
32 end 32 end
33 33
  34 + def current_events(year, month)
  35 + range = Event.date_range(year, month)
  36 +
  37 + Event.find(:all, :include => :categories, :conditions => { 'categories.id' => category_ids, :start_date => range })
  38 + end
  39 +
34 protected 40 protected
35 41
36 def find_in_categorized(klass, query, options={}) 42 def find_in_categorized(klass, query, options={})
app/models/event.rb
@@ -26,4 +26,61 @@ class Event &lt; Article @@ -26,4 +26,61 @@ class Event &lt; Article
26 'event' 26 'event'
27 end 27 end
28 28
  29 + def self.by_month(year = nil, month = nil)
  30 + self.find(:all, :conditions => { :start_date => date_range(year, month) })
  31 + end
  32 +
  33 + def self.date_range(year, month)
  34 + if year.nil? || month.nil?
  35 + today = Date.today
  36 + year = today.year
  37 + month = today.month
  38 + else
  39 + year = year.to_i
  40 + month = month.to_i
  41 + end
  42 +
  43 + first_day = Date.new(year, month, 1)
  44 + last_day = Date.new(year, month, 1) + 1.month - 1.day
  45 +
  46 + first_day..last_day
  47 + end
  48 +
  49 + def date_range
  50 + start_date..(end_date||start_date)
  51 + end
  52 +
  53 + # FIXME this shouldn't be needed
  54 + include ActionView::Helpers::TagHelper
  55 + include ActionView::Helpers::UrlHelper
  56 + include ActionController::UrlWriter
  57 + include DatesHelper
  58 +
  59 + def to_html
  60 +
  61 + result = ''
  62 + html = Builder::XmlMarkup.new(:target => result)
  63 +
  64 + html.div {
  65 + html.ul {
  66 + html.li {
  67 + html.strong _('URL:')
  68 + html.a(self.link || "", 'href' => self.link || "")
  69 + }
  70 + html.li {
  71 + html.strong _('Address:')
  72 + html.text! self.address || ""
  73 + }
  74 + html.li {
  75 + html.strong _('When:')
  76 + html.text! show_period(start_date, end_date)
  77 + }
  78 + }
  79 +
  80 + html.div self.description
  81 + }
  82 +
  83 + result
  84 + end
  85 +
29 end 86 end
app/views/search/assets.rhtml
@@ -1,10 +0,0 @@ @@ -1,10 +0,0 @@
1 -<h1><%= @category ? (_('%{asset_name} in %{category}') % { :asset_name => @asset_name, :category => @category.name}) : @asset_name %></h1>  
2 -  
3 -<div style='text-align: center'>  
4 - <%= link_to_unless_current(_('Recent'), :action => 'assets') %>  
5 - &nbsp;  
6 - <%= (?a..?z).map { |initial| link_to_unless_current(('' << initial).upcase, :action => 'directory', :initial => ('' << initial)) }.join(' &nbsp; ') %>  
7 -</div>  
8 -<br style='clear:both'/>  
9 -  
10 -<%= render :partial => 'display_results' %>  
app/views/search/events.rhtml 0 → 100644
@@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
  1 +<h1><%= show_month(params[:year], params[:month]) %></h1>
  2 +
  3 +<div style='text-align: center; margin: 1em;'>
  4 + <%= link_to_previous_month(params[:year], params[:month]) %>
  5 + &nbsp;
  6 + &nbsp;
  7 + &nbsp;
  8 + <%= link_to_next_month(params[:year], params[:month]) %>
  9 +</div>
  10 +
  11 +<table align='center'>
  12 + <tr>
  13 + <% @calendar.first(7).each do |day,event| %>
  14 + <th style='width: 10%;'><%= show_day_of_week(day) %></th>
  15 + <% end %>
  16 + </tr>
  17 + <% @calendar.in_groups_of(7).each do |week| %>
  18 + <tr style='height: 80px;'>
  19 + <% week.each do |date, events, this_month| %>
  20 + <td style='vertical-align: top; <%= ("background-color: gray;" unless this_month) %> <%= ("background-color: #ff9" if date == Date.today) %>'>
  21 + <div style='text-align: center;'>
  22 + <strong><%= date.day %></strong>
  23 + </div>
  24 +
  25 + <% events.each do |event| %>
  26 + <div style='border: 1px solid gray; margin: 0.25em;'>
  27 + <%= link_to event.name, event.url, :style => 'display: block;' %>
  28 + </div>
  29 + <% end %>
  30 +
  31 + </td>
  32 + <% end %>
  33 + </tr>
  34 + <% end %>
  35 +</table>
config/routes.rb
@@ -31,7 +31,7 @@ ActionController::Routing::Routes.draw do |map| @@ -31,7 +31,7 @@ ActionController::Routing::Routes.draw do |map|
31 map.tag 'tag/:tag', :controller => 'search', :action => 'tag' 31 map.tag 'tag/:tag', :controller => 'search', :action => 'tag'
32 # categories index 32 # categories index
33 map.category 'cat/*category_path', :controller => 'search', :action => 'category_index' 33 map.category 'cat/*category_path', :controller => 'search', :action => 'category_index'
34 - map.assets 'assets/:asset/*category_path', :controller => 'search', :action => 'assets' 34 + map.assets 'assets/:asset/*category_path', :controller => 'search', :action => 'index'
35 map.directory 'directory/:asset/:initial/*category_path', :controller => 'search', :action => 'directory' 35 map.directory 'directory/:asset/:initial/*category_path', :controller => 'search', :action => 'directory'
36 # search 36 # search
37 map.connect 'search/:action/*category_path', :controller => 'search' 37 map.connect 'search/:action/*category_path', :controller => 'search'
test/functional/search_controller_test.rb
@@ -901,14 +901,14 @@ class SearchControllerTest &lt; Test::Unit::TestCase @@ -901,14 +901,14 @@ class SearchControllerTest &lt; Test::Unit::TestCase
901 end 901 end
902 902
903 %w[ people enterprises articles events communities products comments ].each do |asset| 903 %w[ people enterprises articles events communities products comments ].each do |asset|
904 -  
905 should "render asset-specific template when searching for #{asset}" do 904 should "render asset-specific template when searching for #{asset}" do
906 get :index, :find_in => [ asset ] 905 get :index, :find_in => [ asset ]
907 assert_template asset 906 assert_template asset
908 end 907 end
909 -  
910 end 908 end
911 909
  910 + should 'test somehow the display of events as calendar'
  911 +
912 ################################################################## 912 ##################################################################
913 ################################################################## 913 ##################################################################
914 914
test/unit/application_helper_test.rb
@@ -100,28 +100,6 @@ class ApplicationHelperTest &lt; Test::Unit::TestCase @@ -100,28 +100,6 @@ class ApplicationHelperTest &lt; Test::Unit::TestCase
100 assert_not_nil theme_javascript 100 assert_not_nil theme_javascript
101 end 101 end
102 102
103 - should 'generate period with two dates' do  
104 - date1 = mock  
105 - expects(:show_date).with(date1).returns('XXX')  
106 - date2 = mock  
107 - expects(:show_date).with(date2).returns('YYY')  
108 - expects(:_).with('from %s to %s').returns('from %s to %s')  
109 - assert_equal 'from XXX to YYY', show_period(date1, date2)  
110 - end  
111 -  
112 - should 'generate period with two equal dates' do  
113 - date1 = mock  
114 - expects(:show_date).with(date1).returns('XXX')  
115 - assert_equal 'XXX', show_period(date1, date1)  
116 - end  
117 -  
118 - should 'generate period with one date only' do  
119 - date1 = mock  
120 - expects(:show_date).with(date1).returns('XXX')  
121 - assert_equal 'XXX', show_period(date1)  
122 - end  
123 -  
124 -  
125 protected 103 protected
126 104
127 def content_tag(tag, content, options) 105 def content_tag(tag, content, options)
test/unit/category_finder_test.rb
@@ -272,4 +272,18 @@ class CategoryFinderTest &lt; ActiveSupport::TestCase @@ -272,4 +272,18 @@ class CategoryFinderTest &lt; ActiveSupport::TestCase
272 assert_not_includes people, p2 272 assert_not_includes people, p2
273 end 273 end
274 274
  275 + should 'find current events' do
  276 + finder = CategoryFinder.new(@category)
  277 + person = create_user('testuser').person
  278 +
  279 + e1 = Event.create!(:name => 'e1', :profile => person, :start_date => Date.new(2008,1,1), :categories => [@category])
  280 +
  281 + # not in category
  282 + e2 = Event.create!(:name => 'e2', :profile => person, :start_date => Date.new(2008,1,15))
  283 +
  284 + events = finder.current_events(2008, 1)
  285 + assert_includes events, e1
  286 + assert_not_includes events, e2
  287 + end
  288 +
275 end 289 end
test/unit/dates_helper_test.rb 0 → 100644
@@ -0,0 +1,61 @@ @@ -0,0 +1,61 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class DatesHelperTest < Test::Unit::TestCase
  4 +
  5 + include DatesHelper
  6 +
  7 + should 'generate period with two dates' do
  8 + date1 = mock
  9 + expects(:show_date).with(date1).returns('XXX')
  10 + date2 = mock
  11 + expects(:show_date).with(date2).returns('YYY')
  12 + expects(:_).with('from %s to %s').returns('from %s to %s')
  13 + assert_equal 'from XXX to YYY', show_period(date1, date2)
  14 + end
  15 +
  16 + should 'generate period with two equal dates' do
  17 + date1 = mock
  18 + expects(:show_date).with(date1).returns('XXX')
  19 + assert_equal 'XXX', show_period(date1, date1)
  20 + end
  21 +
  22 + should 'generate period with one date only' do
  23 + date1 = mock
  24 + expects(:show_date).with(date1).returns('XXX')
  25 + assert_equal 'XXX', show_period(date1)
  26 + end
  27 +
  28 + should 'show day of week' do
  29 + expects(:_).with("Sunday").returns("Domingo")
  30 + date = mock
  31 + date.expects(:wday).returns(0)
  32 + assert_equal "Domingo", show_day_of_week(date)
  33 + end
  34 +
  35 + should 'show month' do
  36 + expects(:_).with('January').returns('January')
  37 + expects(:_).with('%{month} %{year}').returns('%{month} %{year}')
  38 + assert_equal 'January 2008', show_month(2008, 1)
  39 + end
  40 +
  41 + should 'provide link to previous month' do
  42 + expects(:link_to).with('&larr; January 2008', { :year => 2008, :month => 1})
  43 + link_to_previous_month('2008', '2')
  44 + end
  45 +
  46 + should 'support last year in link to previous month' do
  47 + expects(:link_to).with('&larr; December 2007', { :year => 2007, :month => 12})
  48 + link_to_previous_month('2008', '1')
  49 + end
  50 +
  51 + should 'provide link to next month' do
  52 + expects(:link_to).with('March 2008 &rarr;', { :year => 2008, :month => 3})
  53 + link_to_next_month('2008', '2')
  54 + end
  55 +
  56 + should 'support next year in link to next month' do
  57 + expects(:link_to).with('January 2009 &rarr;', { :year => 2009, :month => 1})
  58 + link_to_next_month('2008', '12')
  59 + end
  60 +
  61 +end
test/unit/event_test.rb
@@ -76,4 +76,74 @@ class EventTest &lt; ActiveSupport::TestCase @@ -76,4 +76,74 @@ class EventTest &lt; ActiveSupport::TestCase
76 assert !e.errors.invalid?(:start_date) 76 assert !e.errors.invalid?(:start_date)
77 end 77 end
78 78
  79 + should 'find by year and month' do
  80 + profile = create_user('testuser').person
  81 + e1 = Event.create!(:name => 'e1', :start_date => Date.new(2008,1,1), :profile => profile)
  82 + e2 = Event.create!(:name => 'e2', :start_date => Date.new(2008,2,1), :profile => profile)
  83 + e3 = Event.create!(:name => 'e3', :start_date => Date.new(2008,3,1), :profile => profile)
  84 +
  85 + found = Event.by_month(2008, 2)
  86 + assert_includes found, e2
  87 + assert_not_includes found, e1
  88 + assert_not_includes found, e3
  89 + end
  90 +
  91 + should 'find when in first day of month' do
  92 + profile = create_user('testuser').person
  93 + e1 = Event.create!(:name => 'e1', :start_date => Date.new(2008,1,1), :profile => profile)
  94 + assert_includes Event.by_month(2008, 1), e1
  95 + end
  96 +
  97 + should 'find when in last day of month' do
  98 + profile = create_user('testuser').person
  99 + e1 = Event.create!(:name => 'e1', :start_date => Date.new(2008,1,31), :profile => profile)
  100 + assert_includes Event.by_month(2008, 1), e1
  101 + end
  102 +
  103 + should 'use current month by default' do
  104 + profile = create_user('testuser').person
  105 + e1 = Event.create!(:name => 'e1', :start_date => Date.new(2008,1,31), :profile => profile)
  106 + Date.expects(:today).returns(Date.new(2008, 1, 15))
  107 + assert_includes Event.by_month, e1
  108 + end
  109 +
  110 + should 'provide period for searching in month' do
  111 + assert_equal Date.new(2008, 1, 1)..Date.new(2008,1,31), Event.date_range(2008, 1)
  112 + assert_equal Date.new(2008, 2, 1)..Date.new(2008,2,29), Event.date_range(2008, 2)
  113 + assert_equal Date.new(2007, 2, 1)..Date.new(2007,2,28), Event.date_range(2007, 2)
  114 + end
  115 +
  116 + should 'support string arguments to Event#date_range' do
  117 + assert_equal Date.new(2008,1,1)..Date.new(2008,1,31), Event.date_range('2008', '1')
  118 + end
  119 +
  120 + should 'provide range of dates for event with both dates filled' do
  121 + e = Event.new(:start_date => Date.new(2008, 1, 1), :end_date => Date.new(2008, 1, 5))
  122 +
  123 + assert_equal (Date.new(2008,1,1)..Date.new(2008,1,5)), e.date_range
  124 + end
  125 +
  126 + should 'provide range of dates for event with only start date' do
  127 + e = Event.new(:start_date => Date.new(2008, 1, 1))
  128 +
  129 + assert_equal (Date.new(2008,1,1)..Date.new(2008,1,1)), e.date_range
  130 + end
  131 +
  132 + should 'provide nice display format' do
  133 + e = Event.new(:start_date => Date.new(2008,1,1), :end_date => Date.new(2008,1,1), :link => 'http://www.myevent.org', :description => 'my somewhat short description')
  134 +
  135 + assert_tag_in_string e.to_html, :content => Regexp.new("1 January 2008")
  136 + assert_tag_in_string e.to_html, :content => 'my somewhat short description'
  137 + assert_tag_in_string e.to_html, :tag => 'a', :attributes => { :href => 'http://www.myevent.org' }, :content => 'http://www.myevent.org'
  138 +
  139 + end
  140 +
  141 + protected
  142 +
  143 + def assert_tag_in_string(text, options)
  144 + doc = HTML::Document.new(text, false, false)
  145 + tag = doc.find(options)
  146 + assert tag, "expected tag #{options.inspect}, but not found in #{text.inspect}"
  147 + end
  148 +
79 end 149 end