<< Masa.20101111-setup-eshop update-customer-data | 2010 | Masa.20101109-setup-eshop >>
Davatz-san advice
you need to load the client file and setup the yus-Admin User. Then you can login with the Admin Account and administer the other Users. All users come through a file like the products.
Notes
Question
Apache log
[Wed Nov 10 08:50:26 2010] [error] [client 127.0.0.1] File does not exist: /home/masa/ywesee/bbmb/doc/de, referer: http://bbmb.masa.ch/
Notes
Experiment
/etc/apache2/vhosts.d/04_bbmb_vhost.conf
<Directory /home/masa/ywesee/bbmb/doc> Options ExecCGI FollowSymlinks Indexes AllowOverride None Order allow,deny Allow from all </Directory> <VirtualHost *:80> DocumentRoot /home/masa/ywesee/bbmb/doc ServerName bbmb.masa.ch DirectoryIndex index.rbx RubyAddPath /home/masa/ywesee/bbmb/src RubyRequire 'sbsm/trans_handler' SetHandler ruby-object RubyTransHandler SBSM::TransHandler.instance SetEnv DRB_SERVER druby://localhost:12000 </VirtualHost>
Result
Experiment
masa@masa ~/ywesee/yus $ bin/yusd server_url="druby://localhost:12001"
Result
yusd log
I, [2010-11-10T09:44:22.698802 #4756] INFO -- start: starting yus-server on druby://localhost:12001 I, [2010-11-10T09:48:08.782417 #4756] INFO -- Yus::Server: Login attempt for mhatakeyama@ywesee.com from ch.bbmb I, [2010-11-10T09:48:08.782685 #4756] INFO -- Yus::Server: Authentication succeeded for mhatakeyama@ywesee.com D, [2010-11-10T09:48:09.020803 #4756] DEBUG -- Yus::EntitySession: allowed?(login, ch.bbmb.Admin) D, [2010-11-10T09:48:09.022578 #4756] DEBUG -- Yus::EntitySession: allowed?(login, ch.bbmb.Customer)
Notes
Confirm the current yus information
masa@masa ~/ywesee/yus $ bin/yus_show mhatakeyama@ywesee.com --server_url="druby://localhost:12001" Password for mhatakeyama@ywesee.com: ["mhatakeyama@ywesee.com", ["edit", [["yus.entities"]]], ["edti", [["yus.entities"]]], ["grant", [["create"], ["credit"], ["edit"], ["invoice"], ["login"], ["view"]]], ["login", [["org.oddb.RootUser"], ["org.oddb.de.Admin"], ["org.oddb.de.PowerUser"]]], ["reset_password", []], ["set_password", [["mhatakeyama@ywesee.com"]]], ["view", [["org.oddb.de"]]], ["RootUser"]]
Add a privilege for bbmb
ch.bbmb.Admin
masa@masa ~/ywesee/yus $ bin/yus_grant --server_url="druby://localhost:12001" mhatakeyama@ywesee.com login ch.bbmb.Admin Password for mhatakeyama@ywesee.com: granted permission to login ch.bbmb.Admin for mhatakeyama@ywesee.com until masa@masa ~/ywesee/yus $ bin/yus_show --server_url="druby://localhost:12001" mhatakeyama@ywesee.com Password for mhatakeyama@ywesee.com: ["mhatakeyama@ywesee.com", ["edit", [["yus.entities"]]], ["edti", [["yus.entities"]]], ["grant", [["create"], ["credit"], ["edit"], ["invoice"], ["login"], ["view"]]], ["login", [["ch.bbmb.Admin"], ["org.oddb.RootUser"], ["org.oddb.de.Admin"], ["org.oddb.de.PowerUser"]]], ["reset_password", []], ["set_password", [["mhatakeyama@ywesee.com"]]], ["view", [["org.oddb.de"]]], ["RootUser"]]
Result
ch.bbmb.Customer
masa@masa ~/ywesee/yus $ bin/yus_grant --server_url="druby://localhost:12001" mhatakeyama@ywesee.com login ch.bbmb.Customer Password for mhatakeyama@ywesee.com: granted permission to login ch.bbmb.Customer for mhatakeyama@ywesee.com until masa@masa ~/ywesee/yus $ bin/yus_show --server_url="druby://localhost:12001" mhatakeyama@ywesee.com Password for mhatakeyama@ywesee.com: ["mhatakeyama@ywesee.com", ["edit", [["yus.entities"]]], ["edti", [["yus.entities"]]], ["grant", [["create"], ["credit"], ["edit"], ["invoice"], ["login"], ["view"]]], ["login", [["ch.bbmb.Admin"], ["ch.bbmb.Customer"], ["org.oddb.RootUser"], ["org.oddb.de.Admin"], ["org.oddb.de.PowerUser"]]], ["reset_password", []], ["set_password", [["mhatakeyama@ywesee.com"]]], ["view", [["org.oddb.de"]]], ["RootUser"]]
Result
bbmbd log
error in SBSM::Session#process: /de NoMethodError undefined method `current_order' for nil:NilClass /home/masa/ywesee/bbmb/lib/bbmb/html/state/current_order.rb:20:in `init' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:406:in `process' /home/masa/ywesee/bbmb/lib/bbmb/html/util/session.rb:45:in `process' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:173:in `drb_process' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:171:in `synchronize' error in SBSM::Session#process: /de/current_order/ NoMethodError undefined method `current_order' for nil:NilClass /home/masa/ywesee/bbmb/lib/bbmb/html/state/current_order.rb:20:in `init' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:406:in `process' /home/masa/ywesee/bbmb/lib/bbmb/html/util/session.rb:45:in `process' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:173:in `drb_process' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:171:in `synchronize' error in SBSM::Session#to_html: /de/current_order/ NoMethodError undefined method `empty?' for #<SBSM::UnknownUser:0x7f05e9040fb0> /home/masa/ywesee/bbmb/lib/bbmb/html/view/current_order.rb:438:in `init' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/component.rb:138:in `initialize' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/template.rb:67:in `new' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/template.rb:67:in `__standard_component' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/template.rb:54:in `content' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/composite.rb:141:in `send' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/composite.rb:141:in `create' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/divcomposite.rb:33:in `compose' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/divcomposite.rb:13:in `each' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/divcomposite.rb:13:in `compose' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/composite.rb:55:in `init' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/component.rb:138:in `initialize' /usr/lib64/ruby/site_ruby/1.8/sbsm/state.rb:272:in `new' /usr/lib64/ruby/site_ruby/1.8/sbsm/state.rb:272:in `view' /usr/lib64/ruby/site_ruby/1.8/sbsm/state.rb:192:in `to_html' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:496:in `to_html' /usr/lib64/ruby/site_ruby/1.8/sbsm/redirector.rb:33:in `to_html' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:174:in `drb_process' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:171:in `synchronize' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:171:in `drb_process' /usr/lib64/ruby/1.8/drb/drb.rb:1556:in `__send__' /usr/lib64/ruby/1.8/drb/drb.rb:1556:in `perform_without_block' /usr/lib64/ruby/1.8/drb/drb.rb:1516:in `perform' /usr/lib64/ruby/1.8/drb/drb.rb:1590:in `main_loop' /usr/lib64/ruby/1.8/drb/drb.rb:1586:in `loop' /usr/lib64/ruby/1.8/drb/drb.rb:1586:in `main_loop' /usr/lib64/ruby/1.8/drb/drb.rb:1582:in `start' /usr/lib64/ruby/1.8/drb/drb.rb:1582:in `main_loop' /usr/lib64/ruby/1.8/drb/drb.rb:1431:in `run' /usr/lib64/ruby/1.8/drb/drb.rb:1428:in `start' /usr/lib64/ruby/1.8/drb/drb.rb:1428:in `run' /usr/lib64/ruby/1.8/drb/drb.rb:1348:in `initialize' /usr/lib64/ruby/1.8/drb/drb.rb:1628:in `new' /usr/lib64/ruby/1.8/drb/drb.rb:1628:in `start_service' bin/bbmbd:51 BBMB::Html::View::CurrentOrder::COMPONENTS[[0, 1]] in create(content) error in SBSM::Session#http_headers: /de/current_order/ NoMethodError undefined method `empty?' for #<SBSM::UnknownUser:0x7f05e9040fb0> /home/masa/ywesee/bbmb/lib/bbmb/html/view/current_order.rb:438:in `init' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/component.rb:138:in `initialize' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/template.rb:67:in `new' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/template.rb:67:in `__standard_component' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/template.rb:54:in `content'
Consideration
Memo
--revoke
option
masa@masa ~/ywesee/yus $ bin/yus_show --server_url="druby://localhost:12001" mhatakeyama@ywesee.com Password for mhatakeyama@ywesee.com: ["mhatakeyama@ywesee.com", ["edit", [["yus.entities"]]], ["edti", [["yus.entities"]]], ["grant", [["create"], ["credit"], ["edit"], ["invoice"], ["login"], ["view"]]], ["login", [["ch.bbmb.Admin"], ["ch.bbmb.Customer"], ["org.oddb.RootUser"], ["org.oddb.de.Admin"], ["org.oddb.de.PowerUser"]]], ["reset_password", []], ["set_password", [["mhatakeyama@ywesee.com"]]], ["view", [["org.oddb.de"]]], ["RootUser"]] masa@masa ~/ywesee/yus $ bin/yus_grant --server_url="druby://localhost:12001" --revoke mhatakeyama@ywesee.com login ch.bbmb.Customer Password for mhatakeyama@ywesee.com: revokeed permission to login ch.bbmb.Customer for mhatakeyama@ywesee.com from masa@masa ~/ywesee/yus $ bin/yus_show --server_url="druby://localhost:12001" mhatakeyama@ywesee.com Password for mhatakeyama@ywesee.com: ["mhatakeyama@ywesee.com", ["edit", [["yus.entities"]]], ["edti", [["yus.entities"]]], ["grant", [["create"], ["credit"], ["edit"], ["invoice"], ["login"], ["view"]]], ["login", [["ch.bbmb.Admin"], ["org.oddb.RootUser"], ["org.oddb.de.Admin"], ["org.oddb.de.PowerUser"]]], ["reset_password", []], ["set_password", [["mhatakeyama@ywesee.com"]]], ["view", [["org.oddb.de"]]], ["RootUser"]]
Confirm the error again
masa@masa ~/ywesee/yus $ bin/yus_grant --server_url="druby://localhost:12001" --revoke mhatakeyama@ywesee.com login ch.bbmb.Admin Password for mhatakeyama@ywesee.com: revokeed permission to login ch.bbmb.Admin for mhatakeyama@ywesee.com from masa@masa ~/ywesee/yus $ bin/yus_grant --server_url="druby://localhost:12001" mhatakeyama@ywesee.com login ch.bbmb.Customer Password for mhatakeyama@ywesee.com: granted permission to login ch.bbmb.Customer for mhatakeyama@ywesee.com until masa@masa ~/ywesee/yus $ bin/yus_show --server_url="druby://localhost:12001" mhatakeyama@ywesee.com Password for mhatakeyama@ywesee.com: ["mhatakeyama@ywesee.com", ["edit", [["yus.entities"]]], ["edti", [["yus.entities"]]], ["grant", [["create"], ["credit"], ["edit"], ["invoice"], ["login"], ["view"]]], ["login", [["ch.bbmb.Customer"], ["org.oddb.RootUser"], ["org.oddb.de.Admin"], ["org.oddb.de.PowerUser"]]], ["reset_password", []], ["set_password", [["mhatakeyama@ywesee.com"]]], ["view", [["org.oddb.de"]]], ["RootUser"]]
Reboot bbmbd and yusd (Just in case)
masa@masa ~/ywesee/bbmb $ bin/bbmbd config="config.yml"
masa@masa ~/ywesee/yus $ bin/yusd server_url="druby://localhost:12001"
Login as a customer
Confirm the customer page error
NoMethodError undefined method `empty?' for #<SBSM::UnknownUser:0x7f4635f0c440>
I, [2010-11-10T11:28:50.506942 #6796] INFO -- start: starting bbmb-server on druby://localhost:12000 D, [2010-11-10T11:29:46.473273 #6796] DEBUG -- User: mhatakeyama@ywesee.com: allowed?(login, ch.bbmb.Admin) -> false D, [2010-11-10T11:29:46.474280 #6796] DEBUG -- User: mhatakeyama@ywesee.com: allowed?(login, ch.bbmb.Customer) -> true error in SBSM::Session#process: /de NoMethodError undefined method `current_order' for nil:NilClass /home/masa/ywesee/bbmb/lib/bbmb/html/state/current_order.rb:20:in `init' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:406:in `process' /home/masa/ywesee/bbmb/lib/bbmb/html/util/session.rb:45:in `process' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:173:in `drb_process' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:171:in `synchronize' error in SBSM::Session#process: /de/current_order/ NoMethodError undefined method `current_order' for nil:NilClass /home/masa/ywesee/bbmb/lib/bbmb/html/state/current_order.rb:20:in `init' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:406:in `process' /home/masa/ywesee/bbmb/lib/bbmb/html/util/session.rb:45:in `process' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:173:in `drb_process' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:171:in `synchronize' error in SBSM::Session#to_html: /de/current_order/ NoMethodError undefined method `empty?' for #<SBSM::UnknownUser:0x7f4635f0c440> /home/masa/ywesee/bbmb/lib/bbmb/html/view/current_order.rb:438:in `init' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/component.rb:138:in `initialize' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/template.rb:67:in `new' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/template.rb:67:in `__standard_component' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/template.rb:54:in `content' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/composite.rb:141:in `send' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/composite.rb:141:in `create' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/divcomposite.rb:33:in `compose' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/divcomposite.rb:13:in `each' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/divcomposite.rb:13:in `compose' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/composite.rb:55:in `init' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/component.rb:138:in `initialize' /usr/lib64/ruby/site_ruby/1.8/sbsm/state.rb:272:in `new' /usr/lib64/ruby/site_ruby/1.8/sbsm/state.rb:272:in `view' /usr/lib64/ruby/site_ruby/1.8/sbsm/state.rb:192:in `to_html' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:496:in `to_html' /usr/lib64/ruby/site_ruby/1.8/sbsm/redirector.rb:33:in `to_html' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:174:in `drb_process' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:171:in `synchronize' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:171:in `drb_process' /usr/lib64/ruby/1.8/drb/drb.rb:1556:in `__send__' /usr/lib64/ruby/1.8/drb/drb.rb:1556:in `perform_without_block' /usr/lib64/ruby/1.8/drb/drb.rb:1516:in `perform' /usr/lib64/ruby/1.8/drb/drb.rb:1590:in `main_loop' /usr/lib64/ruby/1.8/drb/drb.rb:1586:in `loop' /usr/lib64/ruby/1.8/drb/drb.rb:1586:in `main_loop' /usr/lib64/ruby/1.8/drb/drb.rb:1582:in `start' /usr/lib64/ruby/1.8/drb/drb.rb:1582:in `main_loop' /usr/lib64/ruby/1.8/drb/drb.rb:1431:in `run' /usr/lib64/ruby/1.8/drb/drb.rb:1428:in `start' /usr/lib64/ruby/1.8/drb/drb.rb:1428:in `run' /usr/lib64/ruby/1.8/drb/drb.rb:1348:in `initialize' /usr/lib64/ruby/1.8/drb/drb.rb:1628:in `new' /usr/lib64/ruby/1.8/drb/drb.rb:1628:in `start_service' bin/bbmbd:51 BBMB::Html::View::CurrentOrder::COMPONENTS[[0, 1]] in create(content) error in SBSM::Session#http_headers: /de/current_order/ NoMethodError undefined method `empty?' for #<SBSM::UnknownUser:0x7f4635f0c440> /home/masa/ywesee/bbmb/lib/bbmb/html/view/current_order.rb:438:in `init' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/component.rb:138:in `initialize' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/template.rb:67:in `new' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/template.rb:67:in `__standard_component' /usr/lib64/ruby/site_ruby/1.8/htmlgrid/template.rb:54:in `content'
Notes
lib/bbmb/html/state/current_order.rb:20
module BBMB module Html module State class CurrentOrder < Global DIRECT_EVENT = :current_order VIEW = View::CurrentOrder def init @model = _customer.current_order #<= HERE @model.calculate_effective_prices end
Notes
grep search (local)
masa@masa ~/ywesee/bbmb/lib $ grep -r "def _customer" * bbmb/html/state/viral/admin.rb: def _customer(customer_id = @session.user_input(:customer_id)) bbmb/html/state/viral/customer.rb: def _customer
grep search (server)
ywesee@server /var/www/xxx.bbmb.ch/lib $ grep -r "def _customer" * ywesee@server /var/www/xxx.bbmb.ch/lib $
Notes
There is no definition!!
Experiment
lib/bbmb/html/state/viral/customer.rb
def _customer p "BBMB::Html::Viral::Customer#_customer" @customer ||= Model::Customer.find_by_email(@session.user.name) end
Result
D, [2010-11-10T14:10:36.252003 #8972] DEBUG -- User: mhatakeyama@ywesee.com: allowed?(login, ch.bbmb.Admin) -> false D, [2010-11-10T14:10:36.252740 #8972] DEBUG -- User: mhatakeyama@ywesee.com: allowed?(login, ch.bbmb.Customer) -> true "BBMB::Html::Viral::Customer#_customer" error in SBSM::Session#process: /de NoMethodError undefined method `current_order' for nil:NilClass /home/masa/ywesee/bbmb/lib/bbmb/html/state/current_order.rb:20:in `init' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:406:in `process' /home/masa/ywesee/bbmb/lib/bbmb/html/util/session.rb:45:in `process' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:173:in `drb_process' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:171:in `synchronize' ...
Notes
Experiment
lib/bbmb/html/state/viral/customer.rb
def _customer p "BBMB::Html::Viral::Customer#_customer" print "@session.user.name=" p @session.user.name print "Model::Customer.find_by_email(@session.user.name)=" p Model::Customer.find_by_email(@session.user.name) @customer ||= Model::Customer.find_by_email(@session.user.name) end
Result
D, [2010-11-10T14:15:34.498557 #9043] DEBUG -- User: mhatakeyama@ywesee.com: allowed?(login, ch.bbmb.Admin) -> false D, [2010-11-10T14:15:34.499294 #9043] DEBUG -- User: mhatakeyama@ywesee.com: allowed?(login, ch.bbmb.Customer) -> true "BBMB::Html::Viral::Customer#_customer" @session.user.name="mhatakeyama@ywesee.com" Model::Customer.find_by_email(@session.user.name)=nil error in SBSM::Session#process: /de NoMethodError undefined method `current_order' for nil:NilClass /home/masa/ywesee/bbmb/lib/bbmb/html/state/current_order.rb:20:in `init' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:406:in `process' /home/masa/ywesee/bbmb/lib/bbmb/html/util/session.rb:45:in `process' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:173:in `drb_process' /usr/lib64/ruby/site_ruby/1.8/sbsm/session.rb:171:in `synchronize'
Notes
masa@masa ~/ywesee/bbmb $ grep -r find_by_email * lib/bbmb/html/state/viral/customer.rb: @customer ||= Model::Customer.find_by_email(@session.user.name)
Check method_missing method
masa@masa ~/ywesee/bbmb $ grep -r method_missing * lib/bbmb/html/state/history.rb: def method_missing(key, *args, &block) lib/bbmb/html/util/known_user.rb: alias :method_missing :remote_call lib/bbmb/model/order.rb: def method_missing(name, *args, &block) lib/bbmb/util/multilingual.rb: def method_missing(meth, *args, &block)
Notes
Question
Hypothesis
Next
1. bin/bbmbd
begin @server = BBMB::Util::Server.new(@persistence) @server.extend(DRbUndumped) if(@config.update?) @server.run_updater #<= HERE end
2. lib/bbmb/util/server.rb
def run_updater @updater ||= Thread.new { loop { day = Date.today now = Time.now if(now.hour >= BBMB.config.update_hour) day += 1 end at = Time.local(day.year, day.month, day.day, BBMB.config.update_hour) secs = at - now BBMB.logger.debug("updater") { "sleeping %.2f seconds" % secs } sleep(secs) update #<= HERE } } end
3. lib/bbmb/util/server.rb
def update Updater.run #<= HERE rescue Exception => e Mail.notify_error(e) end
4. lib/bbmb/util/updater.rb
module BBMB module Util module Updater def Updater.run require 'pp' PollingManager.new.poll_sources { |filename, io| importer, *args = BBMB.config.importers[filename] if(importer) import(importer, args, io) #<= HERE end } end
Notes
--- !ruby/object:BBMB::Util::FileMission backup_dir: var/backup directory: chroot-jail/home/xxx/data glob_pattern: *.txt delete: true
5. lib/bbmb/util/updater.rb
def Updater.import(importer, args, io) klass = Util.const_get(importer) count = klass.new(*args).import(io) #<= HERE BBMB.logger.debug('updater') { sprintf("%s imported %i entities", importer, count) } end
6. lib/bbmb/util/csv_importer.rb
class CsvImporter def import(io, persistence=BBMB.persistence) count = 0 iconv = Iconv.new('utf-8', 'latin1') io.each { |line| line = u(iconv.iconv(line)) record = line.split("\t") if(object = import_record(record)) persistence.save(object) end count += 1 } postprocess(persistence) count end