<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    Copyright (C) 2013 EIA-FR (https://eia-fr.ch/)
#    Copyright (C) 2014 The DoMo Team (https://launchpad.net/~domo)
#    Luis Domingues & Romain Monnard
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
from openerp.osv.orm import except_orm
from openerp.tools.translate import _
from datetime import datetime
import calendar
from staff_utils import staff_utils

class staff_scheduler(orm.Model):
	_name="staff.scheduler"

	_columns={
		'user_id':fields.many2one('res.users', 'User', readonly=True, relate=True),
		'task_id':fields.many2one('account.analytic.account', 'Task', readonly=False),
		'date': fields.date('Date', readonly=True),
		'hour_from': fields.float('Start Hour', readonly=False),
		'hour_to': fields.float('End Hour', readonly=False),
		'comment':fields.char('Comment',size= 512 ,required=False),
		'work_time':fields.float('Worked time', readonly=False),
		'confirm':fields.boolean('Confirm', readonly=False),
		'replaceable':fields.boolean('Replaceable', readonly=False, default=False),
	}

	#Check if the hour from is between 0 and 24
	def _check_hour_to(self,cr,uid,ids):
		for event in self.browse(cr, uid, ids):
			if(event.hour_to < 0 or event.hour_to >= 24):
				return False
		return True

	#Check if the hour to is between 0 and 24
	def _check_hour_from(self,cr,uid,ids):
		for event in self.browse(cr, uid, ids):
			if(event.hour_from < 0 or event.hour_from >= 24):
				return False
		return True

	_constraints = [(_check_hour_from,_('Start hour must be between 0 and 24.'), ['hour_from']),
				(_check_hour_to,_('End hour must be between 0 and 24.'), ['hour_to'])]

	#Update elements
	#Make a control to ensure that the user can do the task.
	def write(self, cr, user, ids, vals, context=None):
		if context is not None:
			# If write_worked_time on context,
			# write the wirked time on te timesheet
			if 'write_worked_time' in context:
				if 'work_time' in vals:
					worked_time = vals['work_time']
				else:
					worked_time = None
				self.writeTimesheet(cr, user, ids, worked_time)
				return True
		#Records is a list of dictonary. Each dictionary is in this form: [key:(int_id, string_name)]
		records = super(staff_scheduler, self).read(cr, user, ids, None, context)
		#Control if the availability exits when the write is performed
		if len(records) == 0:
			raise except_orm(_('Error'), _("The user removed this availability."))
		#Control if the work time is entered.
		if records[0]['confirm']:
			if context is not None:
				raise except_orm(_('Error'), _("Work time already entered."))
		#Get the authorizations. If the auth list is empty, the authorization don't exist.
		if 'task_id' in vals and vals['task_id'] != False:
			obj = self.pool.get('staff.authorization')
			auth = obj.search(cr, user, [('user_id','=',records[0]['user_id'][0]),('task_id','=',vals['task_id'])])
			if len(auth) == 0:
				raise except_orm(_('Error'), _("This user can not do this task"))
		#Control if the work time is possible.
		if ('hour_from' in vals) and ('hour_to' in vals):
			if vals['hour_to'] < vals['hour_from']:
				raise except_orm(_('Error'), _("You need to specify a correct work time."))
		elif ('hour_from' in vals) and  not ('hour_to' in vals):
			for event in self.browse(cr, user, ids):
				if(event.hour_to < vals['hour_from']):
					raise except_orm(_('Error'), _("You need to specify a correct work time."))
		elif not ('hour_from' in vals) and  ('hour_to' in vals):
			for event in self.browse(cr, user, ids):
				if(vals['hour_to'] < event.hour_from):
					raise except_orm(_('Error'), _("You need to specify a correct work time."))
		#Knowned bug. Need to change the two hours to update the worked time.
		if ('hour_from' in vals) and ('hour_to' in vals):
			#get the break time to substract to the worked time
			time = vals['hour_to'] - vals['hour_from']
			break_management = self.pool.get('staff.break.management')
			breaks = break_management.search(cr, user, [('work_time_min', '<=', time),('work_time_max', '>=', time)])
			if len(breaks) != 0:
				break_length = break_management.browse(cr, user, breaks)[0].break_time
				time = time - break_length
			vals['work_time'] = time
		return super(staff_scheduler, self).write(cr, user, ids, vals, context)

	#push the time worked into timesheet
	def writeTimesheet(self, cr, user, ids, worked_time):
		utils = staff_utils()
		schedule_row = self.browse(cr, user, ids[0])
		user_id = schedule_row.user_id.id
		employees = self.pool.get("hr.employee")
		employee_id = employees.search(cr, user, [("user_id", "=", user_id)])[0]
		employee = employees.browse(cr, user, employee_id)
		products = self.pool.get('product.product')
		pr(cr, user, employee.product_id.id)
		hour_price = product.list_price
		journal_id = employee.journal_id.id
		task_id = schedule_row.task_id.id
		timesheet = self.pool.get("hr_timesheet_sheet.sheet")
		first_day = utils.get_first_day_month(schedule_row.date)
		last_day = utils.get_last_day_month(schedule_row.date)
		sheets = timesheet.search(cr, user, [("date_from", "=", first_day),("user_id", "=", user_id)])
		#Check if the worked time entry is in the future.
		if self.checkFutureDay(datetime.strptime(schedule_row.date, "%Y-%m-%d")):
			raise except_orm(_('Error'), _("You cannot enter worked time in the future."))
		#check if the worked time has been changed.
		if worked_time is None:
			work_time_entry = schedule_row.work_time
		else:
			work_time_entry = worked_time
		amount = work_time_entry * hour_price
		#Enter a new timesheet with the activity
		if len(sheets) == 0:
			line = timesheet.create(cr, user, {'employee_id': employee_id, 'date_from': first_day, 'date_to': last_day})
			timesheet.write(cr, user, [line], {'message_follower_ids': False, 'timesheet_ids': [[5, False, False], [0, False, {'user_id': user_id, 'account_id': task_id, 'journal_id': journal_id, 'general_account_id': 6,'to_invoice': 1, 'amount': amount, 'unit_amount': work_time_entry, 'date': schedule_row.date, 'name': '/'}]], 'employee_id': employee_id, 'date_from': first_day, 'date_to': last_day})
		else:
			#Check if an activity exist. If yes, update, if no, create new one.
			timeLines = self.pool.get("account.analytic.line")
			lines_ids = timeLines.search(cr, user, [('user_id','=', user_id),('date','=',schedule_row.date),('account_id','=', task_id)])
			if len(lines_ids) == 0:
				line = sheets[0]
				timesheet.write(cr, user, [line], {'timesheet_ids': [[0, False, {'user_id': user_id, 'account_id': task_id, 'journal_id': journal_id, 'general_account_id': 6,'to_invoice': 1, 'amount': amount, 'unit_amount': work_time_entry, 'date': schedule_row.date, 'name': '/'}]]})
			else:
				timeLines.write(cr, user, lines_ids, {'unit_amount': work_time_entry, 'amount': amount})
		self.write(cr, user, ids, {'work_time': work_time_entry, 'confirm': True})

	# check if past day. Return true if day is in the past
	def checkPastDay(self, dayDate):
		stamp = datetime.now()
		today = datetime(stamp.year, stamp.month, stamp.day)
		return dayDate < today

	# check if past day. Return true if day is in the past
	def checkFutureDay(self, dayDate):
		stamp = datetime.now()
		today = datetime(stamp.year, stamp.month, stamp.day)
		return dayDate > today

	# Get user informations
	def getPersonalInfo(self, cr, uid, users_id):
		ret = {}
		for user in users_id:
			users = self.pool.get("res.users")
			partner_id = users.browse(cr, uid, user).partner_id.id
			partners = self.pool.get("res.partner")
			partner = partners.browse(cr, uid, partner_id)
			authorizations = self.pool.get("staff.authorization")
			user_auth = authorizations.search(cr, uid, [("user_id", "=", user)])
			auths = []
			tasks = self.pool.get("account.analytic.account")
			for auth_id in user_auth:
				auth_obj = authorizations.browse(cr, uid, auth_id)
				task = tasks.browse(cr, uid, auth_obj.task_id.id)
				auths.append(task.name)
			ret[user] = {"name":partner.name, "mobile":partner.mobile,"auths":auths, "image":partner.image_medium}
		return ret

	# Swap activities for replacement
	def swapUidTask(self, cr, uid, task_id):
		# Check if task_id can is replaceable.
		task = self.browse(cr, uid, task_id)
		if not task.replaceable:
			raise except_orm(_('Error'), _("This task is not replaceable."))
		# Check if user has the right authorization. No -> raise error. Yes -> continue.
		auths = self.pool.get("staff.authorization")
		auth = auths.search(cr, uid, [("user_id","=", uid),("task_id","=",task.task_id.id)])
		if len(auth) < 1:
			raise except_orm(_('Error'), _("You do not have the authorization to replace this task."))
		# Check if uid has empty task_id. No task -> create one, Task defined -> raise error.
		task_self = self.search(cr, uid, [("user_id","=",uid),("date","=", task.date)])
		if len(task_self) < 1:
			self.create(cr, uid, {"date":task.date})
			task_self = self.search(cr, uid, [("user_id","=",uid),("date","=", task.date)])
		elif task_self.task_id.id is False:
			raise except_orm(_('Error'), _("You have already a task. You can not replace."))
		#Swap user_ids, {'hour_from': 9.5, 'hour_to': 18.5, 'task_id': 14}
		task_s = self.browse(cr, uid, task_self[0])
		self.write(cr, uid, [task_s.id], {"user_id":task.user_id.id})
		self.write(cr, uid, [task.id], {"user_id":uid})

	# add user_id to create the elements
	def create(self, cr, user, vals, context=None):
		vals['user_id']=user
		vals['task_id']=False
		#Control if an event is set this day.
		listTasks = self.search(cr, user, [('date','=',vals['date']),('user_id','=',user)])
		if len(listTasks) >= 1:
			raise except_orm(_('Error'), _("You have already an availability this day."))
		#Control if the changed date is in the future.
		#datetime.strptime(vals['date'], "%Y-%m-%d") < datetime.now():
		if self.checkPastDay(datetime.strptime(vals['date'], "%Y-%m-%d")):
			raise except_orm(_('Error'), _("Only future dates can be changed."))
		return super(staff_scheduler, self).create(cr, user, vals, context)

	#Remove an availability with assignement check.
	def unlink(self, cr, uid, ids, context=None):
		records = super(staff_scheduler, self).read(cr, uid, ids, ['id', 'task_id'], context)
		#raise Exception(records) if a user want remove an availibility with a task
		if(records[0]['task_id']):
			raise except_orm(_('Error'), _("You can't delete this availability because there is an assigned task on it."))
		#Check the unlink date.
		date = super(staff_scheduler, self).read(cr, uid, ids, ['date'], context)
		if self.checkPastDay(datetime.strptime(date[0]['date'], "%Y-%m-%d")):
			raise except_orm(_('Error'), _("Only future dates can be changed."))
		return super(staff_scheduler, self).unlink(cr, uid, ids, context)

	#Count the activities of the mounth for selected user
	#Return a dictonary of { key [ day with activity, day available.] }
	#User_id is the planner, start and and are the first and last day for the search pool.
	def countActivitie(self, cr, uid, users_id, start, end, context=None):
		ret = {}
		for user in users_id:
			listGet = self.search(cr, uid, [('user_id','=',user),('date','>=',start),('date', '<=', end), ('task_id','!=',False)])
			listTot = self.search(cr, uid, [('user_id','=',user),('date','>=',start),('date', '<=', end)])
			ret[user] = [len(listGet), len(listTot)]
		return ret

staff_scheduler()
<commit_msg>Set replaceable to False after swap.<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    Copyright (C) 2013 EIA-FR (https://eia-fr.ch/)
#    Copyright (C) 2014 The DoMo Team (https://launchpad.net/~domo)
#    Luis Domingues & Romain Monnard
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
from openerp.osv.orm import except_orm
from openerp.tools.translate import _
from datetime import datetime
import calendar
from staff_utils import staff_utils

class staff_scheduler(orm.Model):
	_name="staff.scheduler"

	_columns={
		'user_id':fields.many2one('res.users', 'User', readonly=True, relate=True),
		'task_id':fields.many2one('account.analytic.account', 'Task', readonly=False),
		'date': fields.date('Date', readonly=True),
		'hour_from': fields.float('Start Hour', readonly=False),
		'hour_to': fields.float('End Hour', readonly=False),
		'comment':fields.char('Comment',size= 512 ,required=False),
		'work_time':fields.float('Worked time', readonly=False),
		'confirm':fields.boolean('Confirm', readonly=False),
		'replaceable':fields.boolean('Replaceable', readonly=False, default=False),
	}

	#Check if the hour from is between 0 and 24
	def _check_hour_to(self,cr,uid,ids):
		for event in self.browse(cr, uid, ids):
			if(event.hour_to < 0 or event.hour_to >= 24):
				return False
		return True

	#Check if the hour to is between 0 and 24
	def _check_hour_from(self,cr,uid,ids):
		for event in self.browse(cr, uid, ids):
			if(event.hour_from < 0 or event.hour_from >= 24):
				return False
		return True

	_constraints = [(_check_hour_from,_('Start hour must be between 0 and 24.'), ['hour_from']),
				(_check_hour_to,_('End hour must be between 0 and 24.'), ['hour_to'])]

	#Update elements
	#Make a control to ensure that the user can do the task.
	def write(self, cr, user, ids, vals, context=None):
		if context is not None:
			# If write_worked_time on context,
			# write the wirked time on te timesheet
			if 'write_worked_time' in context:
				if 'work_time' in vals:
					worked_time = vals['work_time']
				else:
					worked_time = None
				self.writeTimesheet(cr, user, ids, worked_time)
				return True
		#Records is a list of dictonary. Each dictionary is in this form: [key:(int_id, string_name)]
		records = super(staff_scheduler, self).read(cr, user, ids, None, context)
		#Control if the availability exits when the write is performed
		if len(records) == 0:
			raise except_orm(_('Error'), _("The user removed this availability."))
		#Control if the work time is entered.
		if records[0]['confirm']:
			if context is not None:
				raise except_orm(_('Error'), _("Work time already entered."))
		#Get the authorizations. If the auth list is empty, the authorization don't exist.
		if 'task_id' in vals and vals['task_id'] != False:
			obj = self.pool.get('staff.authorization')
			auth = obj.search(cr, user, [('user_id','=',records[0]['user_id'][0]),('task_id','=',vals['task_id'])])
			if len(auth) == 0:
				raise except_orm(_('Error'), _("This user can not do this task"))
		#Control if the work time is possible.
		if ('hour_from' in vals) and ('hour_to' in vals):
			if vals['hour_to'] < vals['hour_from']:
				raise except_orm(_('Error'), _("You need to specify a correct work time."))
		elif ('hour_from' in vals) and  not ('hour_to' in vals):
			for event in self.browse(cr, user, ids):
				if(event.hour_to < vals['hour_from']):
					raise except_orm(_('Error'), _("You need to specify a correct work time."))
		elif not ('hour_from' in vals) and  ('hour_to' in vals):
			for event in self.browse(cr, user, ids):
				if(vals['hour_to'] < event.hour_from):
					raise except_orm(_('Error'), _("You need to specify a correct work time."))
		#Knowned bug. Need to change the two hours to update the worked time.
		if ('hour_from' in vals) and ('hour_to' in vals):
			#get the break time to substract to the worked time
			time = vals['hour_to'] - vals['hour_from']
			break_management = self.pool.get('staff.break.management')
			breaks = break_management.search(cr, user, [('work_time_min', '<=', time),('work_time_max', '>=', time)])
			if len(breaks) != 0:
				break_length = break_management.browse(cr, user, breaks)[0].break_time
				time = time - break_length
			vals['work_time'] = time
		return super(staff_scheduler, self).write(cr, user, ids, vals, context)

	#push the time worked into timesheet
	def writeTimesheet(self, cr, user, ids, worked_time):
		utils = staff_utils()
		schedule_row = self.browse(cr, user, ids[0])
		user_id = schedule_row.user_id.id
		employees = self.pool.get("hr.employee")
		employee_id = employees.search(cr, user, [("user_id", "=", user_id)])[0]
		employee = employees.browse(cr, user, employee_id)
		products = self.pool.get('product.product')
		pr(cr, user, employee.product_id.id)
		hour_price = product.list_price
		journal_id = employee.journal_id.id
		task_id = schedule_row.task_id.id
		timesheet = self.pool.get("hr_timesheet_sheet.sheet")
		first_day = utils.get_first_day_month(schedule_row.date)
		last_day = utils.get_last_day_month(schedule_row.date)
		sheets = timesheet.search(cr, user, [("date_from", "=", first_day),("user_id", "=", user_id)])
		#Check if the worked time entry is in the future.
		if self.checkFutureDay(datetime.strptime(schedule_row.date, "%Y-%m-%d")):
			raise except_orm(_('Error'), _("You cannot enter worked time in the future."))
		#check if the worked time has been changed.
		if worked_time is None:
			work_time_entry = schedule_row.work_time
		else:
			work_time_entry = worked_time
		amount = work_time_entry * hour_price
		#Enter a new timesheet with the activity
		if len(sheets) == 0:
			line = timesheet.create(cr, user, {'employee_id': employee_id, 'date_from': first_day, 'date_to': last_day})
			timesheet.write(cr, user, [line], {'message_follower_ids': False, 'timesheet_ids': [[5, False, False], [0, False, {'user_id': user_id, 'account_id': task_id, 'journal_id': journal_id, 'general_account_id': 6,'to_invoice': 1, 'amount': amount, 'unit_amount': work_time_entry, 'date': schedule_row.date, 'name': '/'}]], 'employee_id': employee_id, 'date_from': first_day, 'date_to': last_day})
		else:
			#Check if an activity exist. If yes, update, if no, create new one.
			timeLines = self.pool.get("account.analytic.line")
			lines_ids = timeLines.search(cr, user, [('user_id','=', user_id),('date','=',schedule_row.date),('account_id','=', task_id)])
			if len(lines_ids) == 0:
				line = sheets[0]
				timesheet.write(cr, user, [line], {'timesheet_ids': [[0, False, {'user_id': user_id, 'account_id': task_id, 'journal_id': journal_id, 'general_account_id': 6,'to_invoice': 1, 'amount': amount, 'unit_amount': work_time_entry, 'date': schedule_row.date, 'name': '/'}]]})
			else:
				timeLines.write(cr, user, lines_ids, {'unit_amount': work_time_entry, 'amount': amount})
		self.write(cr, user, ids, {'work_time': work_time_entry, 'confirm': True})

	# check if past day. Return true if day is in the past
	def checkPastDay(self, dayDate):
		stamp = datetime.now()
		today = datetime(stamp.year, stamp.month, stamp.day)
		return dayDate < today

	# check if past day. Return true if day is in the past
	def checkFutureDay(self, dayDate):
		stamp = datetime.now()
		today = datetime(stamp.year, stamp.month, stamp.day)
		return dayDate > today

	# Get user informations
	def getPersonalInfo(self, cr, uid, users_id):
		ret = {}
		for user in users_id:
			users = self.pool.get("res.users")
			partner_id = users.browse(cr, uid, user).partner_id.id
			partners = self.pool.get("res.partner")
			partner = partners.browse(cr, uid, partner_id)
			authorizations = self.pool.get("staff.authorization")
			user_auth = authorizations.search(cr, uid, [("user_id", "=", user)])
			auths = []
			tasks = self.pool.get("account.analytic.account")
			for auth_id in user_auth:
				auth_obj = authorizations.browse(cr, uid, auth_id)
				task = tasks.browse(cr, uid, auth_obj.task_id.id)
				auths.append(task.name)
			ret[user] = {"name":partner.name, "mobile":partner.mobile,"auths":auths, "image":partner.image_medium}
		return ret

	# Swap activities for replacement
	def swapUidTask(self, cr, uid, task_id):
		# Check if task_id can is replaceable.
		task = self.browse(cr, uid, task_id)
		if not task.replaceable:
			raise except_orm(_('Error'), _("This task is not replaceable."))
		# Check if user has the right authorization. No -> raise error. Yes -> continue.
		auths = self.pool.get("staff.authorization")
		auth = auths.search(cr, uid, [("user_id","=", uid),("task_id","=",task.task_id.id)])
		if len(auth) < 1:
			raise except_orm(_('Error'), _("You do not have the authorization to replace this task."))
		# Check if uid has empty task_id. No task -> create one, Task defined -> raise error.
		task_self = self.search(cr, uid, [("user_id","=",uid),("date","=", task.date)])
		if len(task_self) < 1:
			self.create(cr, uid, {"date":task.date})
			task_self = self.search(cr, uid, [("user_id","=",uid),("date","=", task.date)])
		elif task_self.task_id.id is False:
			raise except_orm(_('Error'), _("You have already a task. You can not replace."))
		#Swap user_ids
		task_s = self.browse(cr, uid, task_self[0])
		self.write(cr, uid, [task_s.id], {"user_id":task.user_id.id, "replaceable":False})
		self.write(cr, uid, [task.id], {"user_id":uid, ,"replaceable":False})

	# add user_id to create the elements
	def create(self, cr, user, vals, context=None):
		vals['user_id']=user
		vals['task_id']=False
		#Control if an event is set this day.
		listTasks = self.search(cr, user, [('date','=',vals['date']),('user_id','=',user)])
		if len(listTasks) >= 1:
			raise except_orm(_('Error'), _("You have already an availability this day."))
		#Control if the changed date is in the future.
		#datetime.strptime(vals['date'], "%Y-%m-%d") < datetime.now():
		if self.checkPastDay(datetime.strptime(vals['date'], "%Y-%m-%d")):
			raise except_orm(_('Error'), _("Only future dates can be changed."))
		return super(staff_scheduler, self).create(cr, user, vals, context)

	#Remove an availability with assignement check.
	def unlink(self, cr, uid, ids, context=None):
		records = super(staff_scheduler, self).read(cr, uid, ids, ['id', 'task_id'], context)
		#raise Exception(records) if a user want remove an availibility with a task
		if(records[0]['task_id']):
			raise except_orm(_('Error'), _("You can't delete this availability because there is an assigned task on it."))
		#Check the unlink date.
		date = super(staff_scheduler, self).read(cr, uid, ids, ['date'], context)
		if self.checkPastDay(datetime.strptime(date[0]['date'], "%Y-%m-%d")):
			raise except_orm(_('Error'), _("Only future dates can be changed."))
		return super(staff_scheduler, self).unlink(cr, uid, ids, context)

	#Count the activities of the mounth for selected user
	#Return a dictonary of { key [ day with activity, day available.] }
	#User_id is the planner, start and and are the first and last day for the search pool.
	def countActivitie(self, cr, uid, users_id, start, end, context=None):
		ret = {}
		for user in users_id:
			listGet = self.search(cr, uid, [('user_id','=',user),('date','>=',start),('date', '<=', end), ('task_id','!=',False)])
			listTot = self.search(cr, uid, [('user_id','=',user),('date','>=',start),('date', '<=', end)])
			ret[user] = [len(listGet), len(listTot)]
		return ret

staff_scheduler()
<|endoftext|>


<commit_before>from __future__ import unicode_literals
from __future__ import absolute_import
from fig import Service
from fig.service import CannotBeScaledError, ConfigError
from .testcases import DockerClientTestCase

class ServiceTest(DockerClientTestCase):
    def test_name_validations(self):
        self.assertRaises(ConfigError, lambda: Service(name=''))

        self.assertRaises(ConfigError, lambda: Service(name=' '))
        self.assertRaises(ConfigError, lambda: Service(name='/'))
        self.assertRaises(ConfigError, lambda: Service(name='!'))
        self.assertRaises(ConfigError, lambda: Service(name='\xe2'))
        self.assertRaises(ConfigError, lambda: Service(name='_'))
        self.assertRaises(ConfigError, lambda: Service(name='____'))
        self.assertRaises(ConfigError, lambda: Service(name='foo_bar'))
        self.assertRaises(ConfigError, lambda: Service(name='__foo_bar__'))

        Service('a')
        Service('foo')

    def test_project_validation(self):
        self.assertRaises(ConfigError, lambda: Service(name='foo', project='_'))
        Service(name='foo', project='bar')

    def test_config_validation(self):
        self.assertRaises(ConfigError, lambda: Service(name='foo', port=['8000']))
        Service(name='foo', ports=['8000'])

    def test_containers(self):
        foo = self.create_service('foo')
        bar = self.create_service('bar')

        foo.start_container()

        self.assertEqual(len(foo.containers()), 1)
        self.assertEqual(foo.containers()[0].name, 'figtest_foo_1')
        self.assertEqual(len(bar.containers()), 0)

        bar.start_container()
        bar.start_container()

        self.assertEqual(len(foo.containers()), 1)
        self.assertEqual(len(bar.containers()), 2)

        names = [c.name for c in bar.containers()]
        self.assertIn('figtest_bar_1', names)
        self.assertIn('figtest_bar_2', names)

    def test_containers_one_off(self):
        db = self.create_service('db')
        container = db.create_container(one_off=True)
        self.assertEqual(db.containers(stopped=True), [])
        self.assertEqual(db.containers(one_off=True, stopped=True), [container])

    def test_project_is_added_to_container_name(self):
        service = self.create_service('web')
        service.start_container()
        self.assertEqual(service.containers()[0].name, 'figtest_web_1')

    def test_start_stop(self):
        service = self.create_service('scalingtest')
        self.assertEqual(len(service.containers(stopped=True)), 0)

        service.create_container()
        self.assertEqual(len(service.containers()), 0)
        self.assertEqual(len(service.containers(stopped=True)), 1)

        service.start()
        self.assertEqual(len(service.containers()), 1)
        self.assertEqual(len(service.containers(stopped=True)), 1)

        service.stop(timeout=1)
        self.assertEqual(len(service.containers()), 0)
        self.assertEqual(len(service.containers(stopped=True)), 1)

        service.stop(timeout=1)
        self.assertEqual(len(service.containers()), 0)
        self.assertEqual(len(service.containers(stopped=True)), 1)

    def test_kill_remove(self):
        service = self.create_service('scalingtest')

        service.start_container()
        self.assertEqual(len(service.containers()), 1)

        service.remove_stopped()
        self.assertEqual(len(service.containers()), 1)

        service.kill()
        self.assertEqual(len(service.containers()), 0)
        self.assertEqual(len(service.containers(stopped=True)), 1)

        service.remove_stopped()
        self.assertEqual(len(service.containers(stopped=True)), 0)

    def test_create_container_with_one_off(self):
        db = self.create_service('db')
        container = db.create_container(one_off=True)
        self.assertEqual(container.name, 'figtest_db_run_1')

    def test_create_container_with_one_off_when_existing_container_is_running(self):
        db = self.create_service('db')
        db.start()
        container = db.create_container(one_off=True)
        self.assertEqual(container.name, 'figtest_db_run_1')

    def test_create_container_with_unspecified_volume(self):
        service = self.create_service('db', volumes=['/var/db'])
        container = service.create_container()
        service.start_container(container)
        self.assertIn('/var/db', container.inspect()['Volumes'])

    def test_recreate_containers(self):
        service = self.create_service('db', environment={'FOO': '1'}, volumes=['/var/db'], entrypoint=['ps'])
        old_container = service.create_container()
        self.assertEqual(old_container.dictionary['Config']['Entrypoint'], ['ps'])
        self.assertIn('FOO=1', old_container.dictionary['Config']['Env'])
        self.assertEqual(old_container.name, 'figtest_db_1')
        service.start_container(old_container)
        volume_path = old_container.inspect()['Volumes']['/var/db']

        num_containers_before = len(self.client.containers(all=True))

        service.options['environment']['FOO'] = '2'
        (intermediate, new) = service.recreate_containers()
        self.assertEqual(len(intermediate), 1)
        self.assertEqual(len(new), 1)

        new_container = new[0]
        intermediate_container = intermediate[0]
        self.assertEqual(intermediate_container.dictionary['Config']['Entrypoint'], None)

        self.assertEqual(new_container.dictionary['Config']['Entrypoint'], ['ps'])
        self.assertIn('FOO=2', new_container.dictionary['Config']['Env'])
        self.assertEqual(new_container.name, 'figtest_db_1')
        service.start_container(new_container)
        self.assertEqual(new_container.inspect()['Volumes']['/var/db'], volume_path)

        self.assertEqual(len(self.client.containers(all=True)), num_containers_before + 1)
        self.assertNotEqual(old_container.id, new_container.id)

    def test_start_container_passes_through_options(self):
        db = self.create_service('db')
        db.start_container(environment={'FOO': 'BAR'})
        self.assertEqual(db.containers()[0].environment['FOO'], 'BAR')

    def test_start_container_inherits_options_from_constructor(self):
        db = self.create_service('db', environment={'FOO': 'BAR'})
        db.start_container()
        self.assertEqual(db.containers()[0].environment['FOO'], 'BAR')

    def test_start_container_creates_links(self):
        db = self.create_service('db')
        web = self.create_service('web', links=[db])
        db.start_container()
        web.start_container()
        self.assertIn('figtest_db_1', web.containers()[0].links())
        self.assertIn('db_1', web.containers()[0].links())

    def test_start_container_creates_links_to_its_own_service(self):
        db1 = self.create_service('db')
        db2 = self.create_service('db')
        db1.start_container()
        db2.start_container()
        self.assertIn('db_1', db2.containers()[0].links())

    def test_start_container_builds_images(self):
        service = Service(
            name='test',
            client=self.client,
            build='tests/fixtures/simple-dockerfile',
            project='figtest',
        )
        container = service.start_container()
        container.wait()
        self.assertIn('success', container.logs())
        self.assertEqual(len(self.client.images(name='figtest_test')), 1)

    def test_start_container_uses_tagged_image_if_it_exists(self):
        self.client.build('tests/fixtures/simple-dockerfile', tag='figtest_test')
        service = Service(
            name='test',
            client=self.client,
            build='this/does/not/exist/and/will/throw/error',
            project='figtest',
        )
        container = service.start_container()
        container.wait()
        self.assertIn('success', container.logs())

    def test_start_container_creates_ports(self):
        service = self.create_service('web', ports=[8000])
        container = service.start_container().inspect()
        self.assertEqual(list(container['HostConfig']['PortBindings'].keys()), ['8000/tcp'])
        self.assertNotEqual(container['HostConfig']['PortBindings']['8000/tcp'][0]['HostPort'], '8000')

    def test_start_container_creates_port_with_explicit_protocol(self):
        service = self.create_service('web', ports=['8000/udp'])
        container = service.start_container().inspect()
        self.assertEqual(list(container['HostConfig']['PortBindings'].keys()), ['8000/udp'])

    def test_start_container_creates_fixed_external_ports(self):
        service = self.create_service('web', ports=['8000:8000'])
        container = service.start_container().inspect()
        self.assertIn('8000/tcp', container['HostConfig']['PortBindings'])
        self.assertEqual(container['HostConfig']['PortBindings']['8000/tcp'][0]['HostPort'], '8000')

    def test_start_container_creates_fixed_external_ports_when_it_is_different_to_internal_port(self):
        service = self.create_service('web', ports=['8001:8000'])
        container = service.start_container().inspect()
        self.assertIn('8000/tcp', container['HostConfig']['PortBindings'])
        self.assertEqual(container['HostConfig']['PortBindings']['8000/tcp'][0]['HostPort'], '8001')

    def test_scale(self):
        service = self.create_service('web')
        service.scale(1)
        self.assertEqual(len(service.containers()), 1)
        service.scale(3)
        self.assertEqual(len(service.containers()), 3)
        service.scale(1)
        self.assertEqual(len(service.containers()), 1)
        service.scale(0)
        self.assertEqual(len(service.containers()), 0)

    def test_scale_on_service_that_cannot_be_scaled(self):
        service = self.create_service('web', ports=['8000:8000'])
        self.assertRaises(CannotBeScaledError, lambda: service.scale(1))

    def test_scale_sets_ports(self):
        service = self.create_service('web', ports=['8000'])
        service.scale(2)
        containers = service.containers()
        self.assertEqual(len(containers), 2)
        for container in containers:
            self.assertEqual(list(container.inspect()['HostConfig']['PortBindings'].keys()), ['8000/tcp'])

<commit_msg>Fix links related test<commit_after>from __future__ import unicode_literals
from __future__ import absolute_import
from fig import Service
from fig.service import CannotBeScaledError, ConfigError
from .testcases import DockerClientTestCase

class ServiceTest(DockerClientTestCase):
    def test_name_validations(self):
        self.assertRaises(ConfigError, lambda: Service(name=''))

        self.assertRaises(ConfigError, lambda: Service(name=' '))
        self.assertRaises(ConfigError, lambda: Service(name='/'))
        self.assertRaises(ConfigError, lambda: Service(name='!'))
        self.assertRaises(ConfigError, lambda: Service(name='\xe2'))
        self.assertRaises(ConfigError, lambda: Service(name='_'))
        self.assertRaises(ConfigError, lambda: Service(name='____'))
        self.assertRaises(ConfigError, lambda: Service(name='foo_bar'))
        self.assertRaises(ConfigError, lambda: Service(name='__foo_bar__'))

        Service('a')
        Service('foo')

    def test_project_validation(self):
        self.assertRaises(ConfigError, lambda: Service(name='foo', project='_'))
        Service(name='foo', project='bar')

    def test_config_validation(self):
        self.assertRaises(ConfigError, lambda: Service(name='foo', port=['8000']))
        Service(name='foo', ports=['8000'])

    def test_containers(self):
        foo = self.create_service('foo')
        bar = self.create_service('bar')

        foo.start_container()

        self.assertEqual(len(foo.containers()), 1)
        self.assertEqual(foo.containers()[0].name, 'figtest_foo_1')
        self.assertEqual(len(bar.containers()), 0)

        bar.start_container()
        bar.start_container()

        self.assertEqual(len(foo.containers()), 1)
        self.assertEqual(len(bar.containers()), 2)

        names = [c.name for c in bar.containers()]
        self.assertIn('figtest_bar_1', names)
        self.assertIn('figtest_bar_2', names)

    def test_containers_one_off(self):
        db = self.create_service('db')
        container = db.create_container(one_off=True)
        self.assertEqual(db.containers(stopped=True), [])
        self.assertEqual(db.containers(one_off=True, stopped=True), [container])

    def test_project_is_added_to_container_name(self):
        service = self.create_service('web')
        service.start_container()
        self.assertEqual(service.containers()[0].name, 'figtest_web_1')

    def test_start_stop(self):
        service = self.create_service('scalingtest')
        self.assertEqual(len(service.containers(stopped=True)), 0)

        service.create_container()
        self.assertEqual(len(service.containers()), 0)
        self.assertEqual(len(service.containers(stopped=True)), 1)

        service.start()
        self.assertEqual(len(service.containers()), 1)
        self.assertEqual(len(service.containers(stopped=True)), 1)

        service.stop(timeout=1)
        self.assertEqual(len(service.containers()), 0)
        self.assertEqual(len(service.containers(stopped=True)), 1)

        service.stop(timeout=1)
        self.assertEqual(len(service.containers()), 0)
        self.assertEqual(len(service.containers(stopped=True)), 1)

    def test_kill_remove(self):
        service = self.create_service('scalingtest')

        service.start_container()
        self.assertEqual(len(service.containers()), 1)

        service.remove_stopped()
        self.assertEqual(len(service.containers()), 1)

        service.kill()
        self.assertEqual(len(service.containers()), 0)
        self.assertEqual(len(service.containers(stopped=True)), 1)

        service.remove_stopped()
        self.assertEqual(len(service.containers(stopped=True)), 0)

    def test_create_container_with_one_off(self):
        db = self.create_service('db')
        container = db.create_container(one_off=True)
        self.assertEqual(container.name, 'figtest_db_run_1')

    def test_create_container_with_one_off_when_existing_container_is_running(self):
        db = self.create_service('db')
        db.start()
        container = db.create_container(one_off=True)
        self.assertEqual(container.name, 'figtest_db_run_1')

    def test_create_container_with_unspecified_volume(self):
        service = self.create_service('db', volumes=['/var/db'])
        container = service.create_container()
        service.start_container(container)
        self.assertIn('/var/db', container.inspect()['Volumes'])

    def test_recreate_containers(self):
        service = self.create_service('db', environment={'FOO': '1'}, volumes=['/var/db'], entrypoint=['ps'])
        old_container = service.create_container()
        self.assertEqual(old_container.dictionary['Config']['Entrypoint'], ['ps'])
        self.assertIn('FOO=1', old_container.dictionary['Config']['Env'])
        self.assertEqual(old_container.name, 'figtest_db_1')
        service.start_container(old_container)
        volume_path = old_container.inspect()['Volumes']['/var/db']

        num_containers_before = len(self.client.containers(all=True))

        service.options['environment']['FOO'] = '2'
        (intermediate, new) = service.recreate_containers()
        self.assertEqual(len(intermediate), 1)
        self.assertEqual(len(new), 1)

        new_container = new[0]
        intermediate_container = intermediate[0]
        self.assertEqual(intermediate_container.dictionary['Config']['Entrypoint'], None)

        self.assertEqual(new_container.dictionary['Config']['Entrypoint'], ['ps'])
        self.assertIn('FOO=2', new_container.dictionary['Config']['Env'])
        self.assertEqual(new_container.name, 'figtest_db_1')
        service.start_container(new_container)
        self.assertEqual(new_container.inspect()['Volumes']['/var/db'], volume_path)

        self.assertEqual(len(self.client.containers(all=True)), num_containers_before + 1)
        self.assertNotEqual(old_container.id, new_container.id)

    def test_start_container_passes_through_options(self):
        db = self.create_service('db')
        db.start_container(environment={'FOO': 'BAR'})
        self.assertEqual(db.containers()[0].environment['FOO'], 'BAR')

    def test_start_container_inherits_options_from_constructor(self):
        db = self.create_service('db', environment={'FOO': 'BAR'})
        db.start_container()
        self.assertEqual(db.containers()[0].environment['FOO'], 'BAR')

    def test_start_container_creates_links(self):
        db = self.create_service('db')
        web = self.create_service('web', links=[(db, None)])
        db.start_container()
        web.start_container()
        self.assertIn('figtest_db_1', web.containers()[0].links())
        self.assertIn('db_1', web.containers()[0].links())

    def test_start_container_creates_links_to_its_own_service(self):
        db1 = self.create_service('db')
        db2 = self.create_service('db')
        db1.start_container()
        db2.start_container()
        self.assertIn('db_1', db2.containers()[0].links())

    def test_start_container_builds_images(self):
        service = Service(
            name='test',
            client=self.client,
            build='tests/fixtures/simple-dockerfile',
            project='figtest',
        )
        container = service.start_container()
        container.wait()
        self.assertIn('success', container.logs())
        self.assertEqual(len(self.client.images(name='figtest_test')), 1)

    def test_start_container_uses_tagged_image_if_it_exists(self):
        self.client.build('tests/fixtures/simple-dockerfile', tag='figtest_test')
        service = Service(
            name='test',
            client=self.client,
            build='this/does/not/exist/and/will/throw/error',
            project='figtest',
        )
        container = service.start_container()
        container.wait()
        self.assertIn('success', container.logs())

    def test_start_container_creates_ports(self):
        service = self.create_service('web', ports=[8000])
        container = service.start_container().inspect()
        self.assertEqual(list(container['HostConfig']['PortBindings'].keys()), ['8000/tcp'])
        self.assertNotEqual(container['HostConfig']['PortBindings']['8000/tcp'][0]['HostPort'], '8000')

    def test_start_container_creates_port_with_explicit_protocol(self):
        service = self.create_service('web', ports=['8000/udp'])
        container = service.start_container().inspect()
        self.assertEqual(list(container['HostConfig']['PortBindings'].keys()), ['8000/udp'])

    def test_start_container_creates_fixed_external_ports(self):
        service = self.create_service('web', ports=['8000:8000'])
        container = service.start_container().inspect()
        self.assertIn('8000/tcp', container['HostConfig']['PortBindings'])
        self.assertEqual(container['HostConfig']['PortBindings']['8000/tcp'][0]['HostPort'], '8000')

    def test_start_container_creates_fixed_external_ports_when_it_is_different_to_internal_port(self):
        service = self.create_service('web', ports=['8001:8000'])
        container = service.start_container().inspect()
        self.assertIn('8000/tcp', container['HostConfig']['PortBindings'])
        self.assertEqual(container['HostConfig']['PortBindings']['8000/tcp'][0]['HostPort'], '8001')

    def test_scale(self):
        service = self.create_service('web')
        service.scale(1)
        self.assertEqual(len(service.containers()), 1)
        service.scale(3)
        self.assertEqual(len(service.containers()), 3)
        service.scale(1)
        self.assertEqual(len(service.containers()), 1)
        service.scale(0)
        self.assertEqual(len(service.containers()), 0)

    def test_scale_on_service_that_cannot_be_scaled(self):
        service = self.create_service('web', ports=['8000:8000'])
        self.assertRaises(CannotBeScaledError, lambda: service.scale(1))

    def test_scale_sets_ports(self):
        service = self.create_service('web', ports=['8000'])
        service.scale(2)
        containers = service.containers()
        self.assertEqual(len(containers), 2)
        for container in containers:
            self.assertEqual(list(container.inspect()['HostConfig']['PortBindings'].keys()), ['8000/tcp'])

<|endoftext|>


<commit_before><commit_msg>cleanup [ci skip]<commit_after><|endoftext|>


<commit_before>try:
    from cyordereddict import OrderedDict
except:
    from collections import OrderedDict

from .util import valid_identifier

class AttrTree(object):
    """
    An AttrTree offers convenient, multi-level attribute access for
    collections of objects. AttrTree objects may also be combined
    together using the update method or merge classmethod. Here is an
    example of adding a ViewableElement to an AttrTree and accessing it:

    >>> t = AttrTree()
    >>> t.Example.Path = 1
    >>> t.Example.Path                             #doctest: +ELLIPSIS
    1
    """

    @classmethod
    def merge(cls, trees):
        """
        Merge a collection of AttrTree objects.
        """
        first = trees[0]
        for tree in trees:
            first.update(tree)
        return first

    def __init__(self, items=None, identifier=None, parent=None):
        """
        identifier: A string identifier for the current node (if any)
        parent:     The parent node (if any)
        items:      Items as (path, value) pairs to construct
                    (sub)tree down to given leaf values.

        Note that the root node does not have a parent and does not
        require an identifier.
        """
        self.__dict__['parent'] = parent
        self.__dict__['identifier'] = valid_identifier(identifier)
        self.__dict__['children'] = []
        self.__dict__['_fixed'] = False

        fixed_error = 'No attribute %r in this AttrTree, and none can be added because fixed=True'
        self.__dict__['_fixed_error'] = fixed_error
        self.__dict__['data'] = OrderedDict()
        items = items.items() if isinstance(items, OrderedDict) else items
        # Python 3
        items = list(items) if items else items
        items = [] if not items else items
        for path, item in items:
            self.set_path(path, item)

    @property
    def path(self):
        "Returns the path up to the root for the current node."
        if self.parent:
            return '.'.join([self.parent.path, str(self.identifier)])
        else:
            return self.identifier if self.identifier else self.__class__.__name__

    @property
    def fixed(self):
        "If fixed, no new paths can be created via attribute access"
        return self.__dict__['_fixed']

    @fixed.setter
    def fixed(self, val):
        self.__dict__['_fixed'] = val

    def update(self, other):
        """
        Updated the contents of the current AttrTree with the
        contents of a second AttrTree.
        """
        if not isinstance(other, AttrTree):
            raise Exception('Can only update with another AttrTree type.')
        fixed_status = (self.fixed, other.fixed)
        (self.fixed, other.fixed) = (False, False)
        for identifier, element in other.items():
            if identifier not in self.data:
                self[identifier] = element
            else:
                self[identifier].update(element)
        (self.fixed, other.fixed) = fixed_status

    def set_path(self, path, val):
        """
        Set the given value at the supplied path where path is either
        a tuple of strings or a string in A.B.C format.
        """
        path = tuple(path.split('.')) if isinstance(path , str) else tuple(path)

        if not all(p[0].isupper() for p in path):
            raise Exception("All paths elements must be capitalized.")

        if len(path) > 1:
            attrtree = self.__getattr__(path[0])
            attrtree.set_path(path[1:], val)
        else:
            self.__setattr__(path[0], val)

    def filter(self, path_filters):
        """
        Filters the loaded AttrTree using the supplied path_filters.
        """
        if not path_filters: return self

        # Convert string path filters
        path_filters = [tuple(pf.split('.')) if not isinstance(pf, tuple)
                        else pf for pf in path_filters]

        # Search for substring matches between paths and path filters
        new_attrtree = self.__class__()
        for path, item in self.data.items():
            if any([all([subpath in path for subpath in pf]) for pf in path_filters]):
                new_attrtree.set_path(path, item)

        return new_attrtree

    def _propagate(self, path, val):
        """
        Propagate the value up to the root node.
        """
        self.data[path] = val
        if self.parent is not None:
            self.parent._propagate((self.identifier,)+path, val)

    def __setitem__(self, identifier, val):
        """
        Set a value at a child node with given identifier. If at a root
        node, multi-level path specifications is allowed (i.e. 'A.B.C'
        format or tuple format) in which case the behaviour matches
        that of set_path.
        """
        if isinstance(identifier, str) and '.' not in identifier:
            self.__setattr__(identifier, val)
        elif isinstance(identifier, str) and self.parent is None:
            self.set_path(tuple(identifier.split('.')), val)
        elif isinstance(identifier, tuple) and self.parent is None:
            self.set_path(identifier, val)
        else:
            raise Exception("Multi-level item setting only allowed from root node.")

    def __getitem__(self, identifier):
        """
        For a given non-root node, access a child element by identifier.

        If the node is a root node, you may also access elements using
        either tuple format or the 'A.B.C' string format.
        """
        keyerror_msg = ''
        split_label = (tuple(identifier.split('.'))
                       if isinstance(identifier, str) else tuple(identifier))
        if len(split_label) == 1:
            identifier = split_label[0]
            if identifier in self.children:
                return self.__dict__[identifier]
            else:
                raise KeyError(identifier + ((' : %s' % keyerror_msg) if keyerror_msg else ''))
        path_item = self
        for identifier in split_label:
            path_item = path_item[identifier]
        return path_item

    def __setattr__(self, identifier, val):
        identifier = valid_identifier(identifier)
        # Getattr is skipped for root and first set of children
        shallow = (self.parent is None or self.parent.parent is None)
        if identifier[0].isupper() and self.fixed and shallow:
            raise AttributeError(self._fixed_error % identifier)

        super(AttrTree, self).__setattr__(identifier, val)

        if identifier[0].isupper():
            if not identifier in self.children:
                self.children.append(identifier)
            self._propagate((identifier,), val)

    def __getattr__(self, identifier):
        """
        Access a identifier from the AttrTree or generate a new AttrTree
        with the chosen attribute path.
        """
        try:
            return super(AttrTree, self).__getattr__(identifier)
        except AttributeError: pass

        if identifier.startswith('_'):   raise AttributeError(str(identifier))
        elif self.fixed==True:           raise AttributeError(self._fixed_error % identifier)
        identifier = valid_identifier(identifier)

        if identifier in self.children:
            return self.__dict__[identifier]

        if identifier[0].isupper():
            self.children.append(identifier)
            child_tree = self.__class__(identifier=identifier, parent=self)
            self.__dict__[identifier] = child_tree
            return child_tree
        else:
            raise AttributeError("%s: Custom paths elements must be capitalized." % identifier)

    def __iter__(self):
        return iter(self.data.values())

    def __contains__(self, name):
        return name in self.children or name in self.data

    def __len__(self):
        return len(self.data)

    def get(self, identifier, default=None):
        return self.__dict__.get(identifier, default)

    def keys(self):
        return list(self.data.keys())

    def items(self):
        return list(self.data.items())

    def values(self):
        return list(self.data.values())

    def pop(self, identifier, default=None):
        if identifier in self.children:
            item = self[identifier]
            self.__delitem__(identifier)
            return item
        else:
            return default
<commit_msg>Added PrettyPrinter repr to AttrTree<commit_after>try:
    from cyordereddict import OrderedDict
except:
    from collections import OrderedDict

from .pprint import PrettyPrinter
from .util import valid_identifier

class AttrTree(object):
    """
    An AttrTree offers convenient, multi-level attribute access for
    collections of objects. AttrTree objects may also be combined
    together using the update method or merge classmethod. Here is an
    example of adding a ViewableElement to an AttrTree and accessing it:

    >>> t = AttrTree()
    >>> t.Example.Path = 1
    >>> t.Example.Path                             #doctest: +ELLIPSIS
    1
    """

    @classmethod
    def merge(cls, trees):
        """
        Merge a collection of AttrTree objects.
        """
        first = trees[0]
        for tree in trees:
            first.update(tree)
        return first

    def __init__(self, items=None, identifier=None, parent=None):
        """
        identifier: A string identifier for the current node (if any)
        parent:     The parent node (if any)
        items:      Items as (path, value) pairs to construct
                    (sub)tree down to given leaf values.

        Note that the root node does not have a parent and does not
        require an identifier.
        """
        self.__dict__['parent'] = parent
        self.__dict__['identifier'] = valid_identifier(identifier)
        self.__dict__['children'] = []
        self.__dict__['_fixed'] = False

        fixed_error = 'No attribute %r in this AttrTree, and none can be added because fixed=True'
        self.__dict__['_fixed_error'] = fixed_error
        self.__dict__['data'] = OrderedDict()
        items = items.items() if isinstance(items, OrderedDict) else items
        # Python 3
        items = list(items) if items else items
        items = [] if not items else items
        for path, item in items:
            self.set_path(path, item)

    @property
    def path(self):
        "Returns the path up to the root for the current node."
        if self.parent:
            return '.'.join([self.parent.path, str(self.identifier)])
        else:
            return self.identifier if self.identifier else self.__class__.__name__

    @property
    def fixed(self):
        "If fixed, no new paths can be created via attribute access"
        return self.__dict__['_fixed']

    @fixed.setter
    def fixed(self, val):
        self.__dict__['_fixed'] = val

    def update(self, other):
        """
        Updated the contents of the current AttrTree with the
        contents of a second AttrTree.
        """
        if not isinstance(other, AttrTree):
            raise Exception('Can only update with another AttrTree type.')
        fixed_status = (self.fixed, other.fixed)
        (self.fixed, other.fixed) = (False, False)
        for identifier, element in other.items():
            if identifier not in self.data:
                self[identifier] = element
            else:
                self[identifier].update(element)
        (self.fixed, other.fixed) = fixed_status

    def set_path(self, path, val):
        """
        Set the given value at the supplied path where path is either
        a tuple of strings or a string in A.B.C format.
        """
        path = tuple(path.split('.')) if isinstance(path , str) else tuple(path)

        if not all(p[0].isupper() for p in path):
            raise Exception("All paths elements must be capitalized.")

        if len(path) > 1:
            attrtree = self.__getattr__(path[0])
            attrtree.set_path(path[1:], val)
        else:
            self.__setattr__(path[0], val)

    def filter(self, path_filters):
        """
        Filters the loaded AttrTree using the supplied path_filters.
        """
        if not path_filters: return self

        # Convert string path filters
        path_filters = [tuple(pf.split('.')) if not isinstance(pf, tuple)
                        else pf for pf in path_filters]

        # Search for substring matches between paths and path filters
        new_attrtree = self.__class__()
        for path, item in self.data.items():
            if any([all([subpath in path for subpath in pf]) for pf in path_filters]):
                new_attrtree.set_path(path, item)

        return new_attrtree

    def _propagate(self, path, val):
        """
        Propagate the value up to the root node.
        """
        self.data[path] = val
        if self.parent is not None:
            self.parent._propagate((self.identifier,)+path, val)

    def __setitem__(self, identifier, val):
        """
        Set a value at a child node with given identifier. If at a root
        node, multi-level path specifications is allowed (i.e. 'A.B.C'
        format or tuple format) in which case the behaviour matches
        that of set_path.
        """
        if isinstance(identifier, str) and '.' not in identifier:
            self.__setattr__(identifier, val)
        elif isinstance(identifier, str) and self.parent is None:
            self.set_path(tuple(identifier.split('.')), val)
        elif isinstance(identifier, tuple) and self.parent is None:
            self.set_path(identifier, val)
        else:
            raise Exception("Multi-level item setting only allowed from root node.")

    def __getitem__(self, identifier):
        """
        For a given non-root node, access a child element by identifier.

        If the node is a root node, you may also access elements using
        either tuple format or the 'A.B.C' string format.
        """
        keyerror_msg = ''
        split_label = (tuple(identifier.split('.'))
                       if isinstance(identifier, str) else tuple(identifier))
        if len(split_label) == 1:
            identifier = split_label[0]
            if identifier in self.children:
                return self.__dict__[identifier]
            else:
                raise KeyError(identifier + ((' : %s' % keyerror_msg) if keyerror_msg else ''))
        path_item = self
        for identifier in split_label:
            path_item = path_item[identifier]
        return path_item

    def __setattr__(self, identifier, val):
        identifier = valid_identifier(identifier)
        # Getattr is skipped for root and first set of children
        shallow = (self.parent is None or self.parent.parent is None)
        if identifier[0].isupper() and self.fixed and shallow:
            raise AttributeError(self._fixed_error % identifier)

        super(AttrTree, self).__setattr__(identifier, val)

        if identifier[0].isupper():
            if not identifier in self.children:
                self.children.append(identifier)
            self._propagate((identifier,), val)

    def __getattr__(self, identifier):
        """
        Access a identifier from the AttrTree or generate a new AttrTree
        with the chosen attribute path.
        """
        try:
            return super(AttrTree, self).__getattr__(identifier)
        except AttributeError: pass

        if identifier.startswith('_'):   raise AttributeError(str(identifier))
        elif self.fixed==True:           raise AttributeError(self._fixed_error % identifier)
        identifier = valid_identifier(identifier)

        if identifier in self.children:
            return self.__dict__[identifier]

        if identifier[0].isupper():
            self.children.append(identifier)
            child_tree = self.__class__(identifier=identifier, parent=self)
            self.__dict__[identifier] = child_tree
            return child_tree
        else:
            raise AttributeError("%s: Custom paths elements must be capitalized." % identifier)

    def __iter__(self):
        return iter(self.data.values())

    def __contains__(self, name):
        return name in self.children or name in self.data

    def __len__(self):
        return len(self.data)

    def get(self, identifier, default=None):
        return self.__dict__.get(identifier, default)

    def keys(self):
        return list(self.data.keys())

    def items(self):
        return list(self.data.items())

    def values(self):
        return list(self.data.values())

    def pop(self, identifier, default=None):
        if identifier in self.children:
            item = self[identifier]
            self.__delitem__(identifier)
            return item
        else:
            return default

    def __repr__(self):
        return PrettyPrinter.pprint(self)
<|endoftext|>


<commit_before>#!/usr/bin/env python

import calendar
import time
import argparse
import sys
import json
sys.path.append("../")
sys.path.append("../scripts")

import libmysql
import misc
import binascii

# PeeWee models -- to replace hand-coded versions
from models import PeeWeeEvent, PeeWeeSuperblock, PeeWeeProposal

class GovernanceObjectMananger:

    @staticmethod
    def object_with_name_exists(name):

        sql = """
            select count(*) from governance_object
            where governance_object.object_name = %s
            limit 1
        """

        cursor = libmysql.db.cursor()
        cursor.execute(sql, (name,))
        count = cursor.fetchone()[0]
        cursor.close()

        return count > 0

class GovernanceObject:
    # object data for specific classes

    def __init__(self):
        self.subclasses = [] #object based subclasses
        # mysql record data
        self.governance_object = {}
        self.fee_tx = None

    def get_hash(self):
        return self.governance_object["object_hash"];

    def get_id(self):
        return self.governance_object["id"]

    def init(self):
        self.governance_object = {
            "id" : 0,
            "parent_id" : 0,
            "object_hash" : 0,
            "object_parent_hash" : 0,
            "object_creation_time" : 0,
            "object_name" : "root",
            "object_type" : "0",
            "object_revision" : 1,
            "object_fee_tx" : "",
            "object_data" : binascii.hexlify(json.dumps([]))
        }

    def create_new(self, parent, object_name, object_type, object_revision, fee_tx):
        creation_time = calendar.timegm(time.gmtime())
        self.fee_tx = fee_tx

        if parent == None:
            return False

        self.governance_object = {
            "id" : 0,
            "parent_id" : parent.get_id(),
            "object_hash" : "",
            "object_parent_hash" : parent.get_hash(),
            "object_creation_time" : creation_time,
            "object_name" : object_name,
            "object_type" : object_type,
            "object_revision" : object_revision,
            "object_fee_tx" : fee_tx.get_hash(),
            "object_data" : ""
        }

        self.object_name = object_name
        self.object_type = object_type
        self.object_revision = object_revision
        self.fee_tx = fee_tx
        self.creation_time = creation_time

        return True

    """
        Subclasses:

        - Governance objects can be converted into many subclasses by using the data field.
        - See subclasses.py for more information

    """

    def compile_subclasses(self):
        objects = []

        for (obj_type, obj) in self.subclasses:
            objects.append((obj_type, obj.get_dict()))

        self.governance_object["object_data"] = binascii.hexlify(json.dumps(objects, sort_keys = True))

        return True

    def save_subclasses(self):
        objects = []
        for (obj_type, obj) in self.subclasses:
            print obj
            obj.save()

        return True

    def load_subclasses(self):
        objects = json.loads(binascii.unhexlify(self.governance_object["object_data"]))

        ## todo -- make plugin system for subclasses?
        for (obj_type, obj_data) in objects:

            if obj_type == "proposal":
               obj = Proposal()
               obj.load_dict(obj_data)
               self.subclasses.append((obj_type, obj))

            if obj_type == "trigger":
               trigger = Superblock()
               trigger.load_dict(obj_data)
               self.subclasses.append((obj_type, trigger))

        return True

    """
        load/save/update from database
    """

    def load(self, record_id):

        self.init()

        sql = """
            select
                id,
                parent_id,
                object_creation_time,
                object_hash,
                object_parent_hash,
                object_name,
                object_type,
                object_revision,
                object_data,
                object_fee_tx
            from governance_object where
                id = %s
        """ % record_id

        libmysql.db.query(sql)
        res = libmysql.db.store_result()
        row = res.fetch_row()

        if row:
            (
                self.governance_object["id"],
                self.governance_object["parent_id"],
                self.governance_object["object_creation_time"],
                self.governance_object["object_hash"],
                self.governance_object["object_parent_hash"],
                self.governance_object["object_name"],
                self.governance_object["object_type"],
                self.governance_object["object_revision"],
                self.governance_object["object_data"],
                self.governance_object["object_fee_tx"]
            ) = row[0]

            print "loaded govobj successfully: ", self.governance_object["id"]

            self.load_subclasses()
        else:
            print "object not found"
            print
            print "SQL:"
            print sql
            print

    def update_field(self, field, value):
        self.governance_object[field] = value

    def get_field(self, field):
        return self.governance_object[field]

    def save(self):
        self.compile_subclasses()

        if self.governance_object["id"] == 0:
            sql = """
                INSERT INTO governance_object
                    (parent_id, object_hash, object_parent_hash, object_creation_time, object_name, object_type, object_revision,
                        object_fee_tx, object_data)
                VALUES
                    ('%(parent_id)s', '%(object_hash)s', '%(object_parent_hash)s',  '%(object_creation_time)s', '%(object_name)s',  '%(object_type)s', '%(object_revision)s',
                        '%(object_fee_tx)s', '%(object_data)s')
            """

            print sql % self.governance_object

            libmysql.db.query(sql % self.governance_object)
            self.save_subclasses()

            self.governance_object["id"] = libmysql.db.insert_id()
            return self.governance_object["id"]

        else:
            sql = """
                UPDATE governance_object SET
                    parent_id='%(parent_id)s',
                    object_hash='%(object_hash)s',
                    object_parent_hash='%(object_parent_hash)s',
                    object_creation_time='%(object_creation_time)s',
                    object_name='%(object_name)s',
                    object_type='%(object_type)s',
                    object_revision='%(object_revision)s',
                    object_fee_tx='%(object_fee_tx)s',
                    object_data='%(object_data)s'
                WHERE
                    id='%(id)s'
            """

            print sql % self.governance_object

            libmysql.db.query(sql % self.governance_object)

            self.save_subclasses()

            return self.governance_object["id"]

    def get_prepare_command(self):
        cmd = "gobject prepare %(object_parent_hash)s %(object_revision)s %(object_creation_time)s %(object_name)s %(object_data)s" % self.governance_object
        return cmd

    def get_fee_tx_age(self):
        return -1

    def get_submit_command(self):
        cmd = "gobject submit %(object_fee_tx)s %(object_parent_hash)s %(object_revision)s %(object_creation_time)s %(object_name)s %(object_data)s" % self.governance_object
        return cmd

    def last_error(self):
        return "n/a"

    def add_subclass(self, typename, obj):
        self.subclasses.append((typename,obj))

    def is_valid(self):
        """
            - check tree position validity
            - check signatures of owners
            - check validity of revision (must be n+1)
            - check validity of field data (address format, etc)
        """

        return True

class Event:

    def __init__(self):
        self.event = {}

    def create_new(self, last_id):
        self.event["governance_object_id"] = last_id
        self.event["start_time"] = calendar.timegm(time.gmtime())
        self.event["prepare_time"] = 0
        self.event["submit_time"] = 0
        self.event["error_time"] = 0

    def load(self, record_id):
        sql = """
            select
                id,
                governance_object_id,
                start_time,
                prepare_time,
                submit_time,
                error_time
            from event where
                id = %s """

        cursor = libmysql.db.cursor()
        cursor.execute(sql, record_id)
        row = cursor.fetchone()

        if row:
            print "retrieving record", row
            (self.event["id"], self.event["governance_object_id"], self.event["start_time"],
                self.event["prepare_time"], self.event["submit_time"], self.event["error_time"]) = row
            print "loaded event successfully", record_id
        else:
            print "event not found", sql

        cursor.close()

    def get_id(self):
        return self.event["governance_object_id"]

    def set_prepared(self):
        self.event["prepare_time"] = calendar.timegm(time.gmtime())

    def set_submitted(self):
        self.event["submit_time"] = calendar.timegm(time.gmtime())

    def set_start_time(self):
        self.event["start_time"] = calendar.timegm(time.gmtime())

    def save(self):
        sql = """
            INSERT INTO event
                (governance_object_id, start_time, prepare_time, submit_time)
            VALUES
                (%(governance_object_id)s,%(start_time)s,%(prepare_time)s,%(submit_time)s)
            ON DUPLICATE KEY UPDATE
                governance_object_id=%(governance_object_id)s,
                start_time=%(start_time)s,
                prepare_time=%(prepare_time)s,
                submit_time=%(submit_time)s,
                error_time=%(error_time)s
        """

        print "save governance_object"
        print sql % self.event
        libmysql.db.query(sql % self.event)

    def update_field(self, field, value):
        self.event[field] = value

    def update_error_message(self, message):
        sql = """
            UPDATE event
               SET error_message = %s
             WHERE id = %s
        """

        c = libmysql.db.cursor()
        c.execute(sql , (message, self.event['id']))
        c.close()

class Setting:

    def __init__(self):
        self.setting = {}

    def create_new(self, setting, name, value):
        self.setting["id"] = 0
        self.setting["setting"] = setting
        self.setting["name"] = name
        self.setting["value"] = value

    def load(self, record_id):
        sql = """
            select
                id,
                name,
                value
            from setting where
                id = %s """ % record_id

        libmysql.db.query(sql)
        res = libmysql.db.store_result()
        row = res.fetch_row()
        if row:
            print row[0]
            (self.setting["id"], self.setting["name"], self.setting["value"]) = row[0]
            print "loaded setting successfully"

            return True

        return False

    def get_id(self):
        pass

    def save(self):
        sql = """
            INSERT INTO setting
                (id, setting, name, value)
            VALUES
                ('%(id)s','%(setting)s','%(name)s','%(value)s')
            ON DUPLICATE KEY UPDATE
                id='%(id)s',
                setting='%(setting)s',
                name='%(name)s',
                value='%(value)s'
        """

        libmysql.db.query(sql % self.user)

        return True

    def set_field(self, name, value):
        self.user[name] = value
<commit_msg>limit is useless in count(*) query<commit_after>#!/usr/bin/env python

import calendar
import time
import argparse
import sys
import json
sys.path.append("../")
sys.path.append("../scripts")

import libmysql
import misc
import binascii

# PeeWee models -- to replace hand-coded versions
from models import PeeWeeEvent, PeeWeeSuperblock, PeeWeeProposal

class GovernanceObjectMananger:

    @staticmethod
    def object_with_name_exists(name):

        sql = """
            select count(*) from governance_object
            where governance_object.object_name = %s
        """

        cursor = libmysql.db.cursor()
        cursor.execute(sql, (name,))
        count = cursor.fetchone()[0]
        cursor.close()

        return count > 0

class GovernanceObject:
    # object data for specific classes

    def __init__(self):
        self.subclasses = [] #object based subclasses
        # mysql record data
        self.governance_object = {}
        self.fee_tx = None

    def get_hash(self):
        return self.governance_object["object_hash"];

    def get_id(self):
        return self.governance_object["id"]

    def init(self):
        self.governance_object = {
            "id" : 0,
            "parent_id" : 0,
            "object_hash" : 0,
            "object_parent_hash" : 0,
            "object_creation_time" : 0,
            "object_name" : "root",
            "object_type" : "0",
            "object_revision" : 1,
            "object_fee_tx" : "",
            "object_data" : binascii.hexlify(json.dumps([]))
        }

    def create_new(self, parent, object_name, object_type, object_revision, fee_tx):
        creation_time = calendar.timegm(time.gmtime())
        self.fee_tx = fee_tx

        if parent == None:
            return False

        self.governance_object = {
            "id" : 0,
            "parent_id" : parent.get_id(),
            "object_hash" : "",
            "object_parent_hash" : parent.get_hash(),
            "object_creation_time" : creation_time,
            "object_name" : object_name,
            "object_type" : object_type,
            "object_revision" : object_revision,
            "object_fee_tx" : fee_tx.get_hash(),
            "object_data" : ""
        }

        self.object_name = object_name
        self.object_type = object_type
        self.object_revision = object_revision
        self.fee_tx = fee_tx
        self.creation_time = creation_time

        return True

    """
        Subclasses:

        - Governance objects can be converted into many subclasses by using the data field.
        - See subclasses.py for more information

    """

    def compile_subclasses(self):
        objects = []

        for (obj_type, obj) in self.subclasses:
            objects.append((obj_type, obj.get_dict()))

        self.governance_object["object_data"] = binascii.hexlify(json.dumps(objects, sort_keys = True))

        return True

    def save_subclasses(self):
        objects = []
        for (obj_type, obj) in self.subclasses:
            print obj
            obj.save()

        return True

    def load_subclasses(self):
        objects = json.loads(binascii.unhexlify(self.governance_object["object_data"]))

        ## todo -- make plugin system for subclasses?
        for (obj_type, obj_data) in objects:

            if obj_type == "proposal":
               obj = Proposal()
               obj.load_dict(obj_data)
               self.subclasses.append((obj_type, obj))

            if obj_type == "trigger":
               trigger = Superblock()
               trigger.load_dict(obj_data)
               self.subclasses.append((obj_type, trigger))

        return True

    """
        load/save/update from database
    """

    def load(self, record_id):

        self.init()

        sql = """
            select
                id,
                parent_id,
                object_creation_time,
                object_hash,
                object_parent_hash,
                object_name,
                object_type,
                object_revision,
                object_data,
                object_fee_tx
            from governance_object where
                id = %s
        """ % record_id

        libmysql.db.query(sql)
        res = libmysql.db.store_result()
        row = res.fetch_row()

        if row:
            (
                self.governance_object["id"],
                self.governance_object["parent_id"],
                self.governance_object["object_creation_time"],
                self.governance_object["object_hash"],
                self.governance_object["object_parent_hash"],
                self.governance_object["object_name"],
                self.governance_object["object_type"],
                self.governance_object["object_revision"],
                self.governance_object["object_data"],
                self.governance_object["object_fee_tx"]
            ) = row[0]

            print "loaded govobj successfully: ", self.governance_object["id"]

            self.load_subclasses()
        else:
            print "object not found"
            print
            print "SQL:"
            print sql
            print

    def update_field(self, field, value):
        self.governance_object[field] = value

    def get_field(self, field):
        return self.governance_object[field]

    def save(self):
        self.compile_subclasses()

        if self.governance_object["id"] == 0:
            sql = """
                INSERT INTO governance_object
                    (parent_id, object_hash, object_parent_hash, object_creation_time, object_name, object_type, object_revision,
                        object_fee_tx, object_data)
                VALUES
                    ('%(parent_id)s', '%(object_hash)s', '%(object_parent_hash)s',  '%(object_creation_time)s', '%(object_name)s',  '%(object_type)s', '%(object_revision)s',
                        '%(object_fee_tx)s', '%(object_data)s')
            """

            print sql % self.governance_object

            libmysql.db.query(sql % self.governance_object)
            self.save_subclasses()

            self.governance_object["id"] = libmysql.db.insert_id()
            return self.governance_object["id"]

        else:
            sql = """
                UPDATE governance_object SET
                    parent_id='%(parent_id)s',
                    object_hash='%(object_hash)s',
                    object_parent_hash='%(object_parent_hash)s',
                    object_creation_time='%(object_creation_time)s',
                    object_name='%(object_name)s',
                    object_type='%(object_type)s',
                    object_revision='%(object_revision)s',
                    object_fee_tx='%(object_fee_tx)s',
                    object_data='%(object_data)s'
                WHERE
                    id='%(id)s'
            """

            print sql % self.governance_object

            libmysql.db.query(sql % self.governance_object)

            self.save_subclasses()

            return self.governance_object["id"]

    def get_prepare_command(self):
        cmd = "gobject prepare %(object_parent_hash)s %(object_revision)s %(object_creation_time)s %(object_name)s %(object_data)s" % self.governance_object
        return cmd

    def get_fee_tx_age(self):
        return -1

    def get_submit_command(self):
        cmd = "gobject submit %(object_fee_tx)s %(object_parent_hash)s %(object_revision)s %(object_creation_time)s %(object_name)s %(object_data)s" % self.governance_object
        return cmd

    def last_error(self):
        return "n/a"

    def add_subclass(self, typename, obj):
        self.subclasses.append((typename,obj))

    def is_valid(self):
        """
            - check tree position validity
            - check signatures of owners
            - check validity of revision (must be n+1)
            - check validity of field data (address format, etc)
        """

        return True

class Event:

    def __init__(self):
        self.event = {}

    def create_new(self, last_id):
        self.event["governance_object_id"] = last_id
        self.event["start_time"] = calendar.timegm(time.gmtime())
        self.event["prepare_time"] = 0
        self.event["submit_time"] = 0
        self.event["error_time"] = 0

    def load(self, record_id):
        sql = """
            select
                id,
                governance_object_id,
                start_time,
                prepare_time,
                submit_time,
                error_time
            from event where
                id = %s """

        cursor = libmysql.db.cursor()
        cursor.execute(sql, record_id)
        row = cursor.fetchone()

        if row:
            print "retrieving record", row
            (self.event["id"], self.event["governance_object_id"], self.event["start_time"],
                self.event["prepare_time"], self.event["submit_time"], self.event["error_time"]) = row
            print "loaded event successfully", record_id
        else:
            print "event not found", sql

        cursor.close()

    def get_id(self):
        return self.event["governance_object_id"]

    def set_prepared(self):
        self.event["prepare_time"] = calendar.timegm(time.gmtime())

    def set_submitted(self):
        self.event["submit_time"] = calendar.timegm(time.gmtime())

    def set_start_time(self):
        self.event["start_time"] = calendar.timegm(time.gmtime())

    def save(self):
        sql = """
            INSERT INTO event
                (governance_object_id, start_time, prepare_time, submit_time)
            VALUES
                (%(governance_object_id)s,%(start_time)s,%(prepare_time)s,%(submit_time)s)
            ON DUPLICATE KEY UPDATE
                governance_object_id=%(governance_object_id)s,
                start_time=%(start_time)s,
                prepare_time=%(prepare_time)s,
                submit_time=%(submit_time)s,
                error_time=%(error_time)s
        """

        print "save governance_object"
        print sql % self.event
        libmysql.db.query(sql % self.event)

    def update_field(self, field, value):
        self.event[field] = value

    def update_error_message(self, message):
        sql = """
            UPDATE event
               SET error_message = %s
             WHERE id = %s
        """

        c = libmysql.db.cursor()
        c.execute(sql , (message, self.event['id']))
        c.close()

class Setting:

    def __init__(self):
        self.setting = {}

    def create_new(self, setting, name, value):
        self.setting["id"] = 0
        self.setting["setting"] = setting
        self.setting["name"] = name
        self.setting["value"] = value

    def load(self, record_id):
        sql = """
            select
                id,
                name,
                value
            from setting where
                id = %s """ % record_id

        libmysql.db.query(sql)
        res = libmysql.db.store_result()
        row = res.fetch_row()
        if row:
            print row[0]
            (self.setting["id"], self.setting["name"], self.setting["value"]) = row[0]
            print "loaded setting successfully"

            return True

        return False

    def get_id(self):
        pass

    def save(self):
        sql = """
            INSERT INTO setting
                (id, setting, name, value)
            VALUES
                ('%(id)s','%(setting)s','%(name)s','%(value)s')
            ON DUPLICATE KEY UPDATE
                id='%(id)s',
                setting='%(setting)s',
                name='%(name)s',
                value='%(value)s'
        """

        libmysql.db.query(sql % self.user)

        return True

    def set_field(self, name, value):
        self.user[name] = value
<|endoftext|>