# vim: ts=4 et sts=4 sw=4 autoindent import gtk import Pyro.core # Searching function from Application import treeview_visible_function from sndcs_client.Config import config from sndcs_common.TrueAndFalseMixin import TrueAndFalseMixin from sndcs_common.PyroProxyMixin import PyroProxyMixin from sndcs_common.SndcsExceptions import * from sndcs_common.Logger import logger log = logger.getLogger("sndcs_gtk") class ActiveEmployeesTreeView(TrueAndFalseMixin, PyroProxyMixin): def __init__(self, tree_view, namespace=None): self.view = tree_view tree_column_1 = gtk.TreeViewColumn("Employee Name") tree_column_2 = gtk.TreeViewColumn("Activity") tree_column_1.set_sort_column_id(2) tree_column_2.set_sort_column_id(3) self.view.append_column(tree_column_1) self.view.append_column(tree_column_2) cell = gtk.CellRendererText() tree_column_1.pack_start(cell, True) tree_column_1.add_attribute(cell, "text", 2) tree_column_1.add_attribute(cell, "foreground", 4) tree_column_2.pack_start(cell, True) tree_column_2.add_attribute(cell, "text", 3) tree_column_2.add_attribute(cell, "foreground", 4) self.model = gtk.TreeStore(long, long, str, str, str, "gboolean") self.view.set_model(self.model) self.column_number = 0 self.search_criteria = "" if namespace: self.namespace = namespace else: # Get the Pyro namespace from the config self.namespace = config.get("pyro", "namespace", "sndcs") def getActivityColor(self, type_name): if type_name == "idle": return "#ff0000" elif type_name == "production": return "#000000" elif type_name == "indirect": return "#0000ff" elif type_name == "setup": return "#00ff00" def populate(self, model=False): """Populate the model""" if model: self.model = model else: self.model = gtk.TreeStore(long, long, str, str, str, "gboolean") # employee_id, labordtl_id, name, activity, color, isClockedIn (for the display clocked-in employee filter) active_employee_list = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["Employee"])) if not hasattr(self,"display_departments"): terminal_id = config.get("gtk","terminal_id", None) if terminal_id: terminal = self.selectTerminal( terminal_id ) if terminal: self.display_departments = terminal.getTerminalDepartments() terminal.disconnect() else: self.display_departments = None else: self.display_departments = None for employee in active_employee_list.getActiveEmployeeAndActivityData(): departments = employee["departments"] if self.display_departments is None: display_employee = True else: display_employee = False for x in self.display_departments: if departments.count(x): display_employee = True # Misc. departments displayed for now. if not display_employee and not len(departments)==0: continue activities = employee["activities"] if not activities: self.model.append(None, (employee["serialNum"], 0, employee["properName"], "", "#aaaaaa", False)) else: if len(activities) > 1: parent = self.model.append(None, (employee["serialNum"], 0, employee["properName"], "Ganged", "#000000", True)) else: parent = None for activity in activities: if activity["serialNum"]: labordtl_id = activity["serialNum"] else: labordtl_id = 0 self.model.append(parent, (employee["serialNum"], labordtl_id, employee["properName"], activity["description"], self.getActivityColor(activity["type"]), True)) if not self.true(config.get("gtk","show_clocked_out_employees")): # If we don't want to see clocked out employees we need to filter them out self.clocked_out_employee_filter = self.model.filter_new() self.clocked_out_employee_filter.set_visible_column(5) self.clocked_out_employee_filter.refilter() else: self.clocked_out_employee_filter = self.model self.modelfilter = self.clocked_out_employee_filter.filter_new() self.modelfilter.set_visible_func(treeview_visible_function,self) self.modelsort = gtk.TreeModelSort(self.modelfilter) self.view.set_model(self.modelsort) self.modelsort.set_sort_column_id(2, gtk.SORT_ASCENDING) # Start off sorting by the first column ASCENDING self.view.expand_all() def find_employee(self, employee_id, model=None): if not model: model = self.model p = None # (p)arent i = 0 for x in model: if x[0] == employee_id: # We found them! p = i break i += 1 return p def scroll_to_employee(self, employee_id): """ Scroll the active employee list so the employee is visible and highlight the record. It is possible that the path may not be found if the model is filtered. We will return the path if it found or None if it is not found so the calling code can decide what to do with it. (i.e. If it is a clock in and clocked-out employees are filtered out we may want to unfilter that employee and try to scroll_to_employee again) """ p = self.find_employee(employee_id, model=self.modelsort) if p is not None: self.view.set_cursor(p) self.view.grab_focus() self.view.scroll_to_cell((p), use_align=True, row_align=0.5) return p return None def add_activity(self, employee_id, labordtl_id, proper_name, activity_description, color, clockedIn): """ Add an activity to the active employee tree view. If labordtl_id is 0 it's not an actual labordtl record we are adding. It is something like clock in or idle. """ # Try to find employee in the tree model p = self.find_employee(employee_id) if p is not None: # Employee already in the model if not labordtl_id: # If no labordtl_id passed (or 0) then just update the main record self.model[p] = [employee_id, labordtl_id, proper_name, activity_description, color, clockedIn] else: parent_iter = self.model.get_iter(p) c = self.model.iter_children(parent_iter) if c: # The user is ganged self.model.append(parent_iter, (employee_id, labordtl_id, proper_name, activity_description, color, clockedIn)) else: # The user is NOT ganged # We need to check if we on a single activity... if so we need to convert into gang if self.model[p][1]: # need to convert to gang self.model.append(parent_iter, (self.model[p])) self.model[p] = [employee_id, 0, proper_name, "Ganged", "#000000", clockedIn] # And add the new activity self.model.append(parent_iter, (employee_id, labordtl_id, proper_name, activity_description, color, clockedIn)) # Expand the treeview self.view.expand_row((p), True) else: # Not on activity, no need to convert to gang self.model[p] = [employee_id, labordtl_id, proper_name, activity_description, color, clockedIn] else: # Employee not in the model yet print "not in model" def remove_activity(self, employee_id, labordtl_id, proper_name): # Try to find employee in the tree model p = self.find_employee(employee_id) if p is not None: # Employee is in the model if labordtl_id: parent_iter = self.model.get_iter(p) c = self.model.iter_children(parent_iter) if c: # The user is ganged # Check the child records for a matching labordtl_id while c: if self.model.get_value(c, 1) == labordtl_id: # Found it self.model.remove(c) # If this leaves the user with only a single activity we need to colapse the gang c = self.model.iter_children(parent_iter) i = 0 while c: i += 1 c = self.model.iter_next(c) if i == 1: self.model[p] = self.model[(p,0)] del self.model[(p,0)] return c = self.model.iter_next(c) else: # The user is NOT ganged if self.model[p][1] == labordtl_id: # If the single activity labordtl_id matches, remove it... otherwise don't do anything self.model[p] = [employee_id, 0, proper_name, "*** IDLE ***", self.getActivityColor("idle"), True] else: # Employee not in the model yet print "not in model" def remove_employee(self, employee_id, proper_name=""): # Try to find employee in the tree model p = self.find_employee(employee_id) if p is not None: parent_iter = self.model.get_iter(p) self.model.remove(parent_iter) def add_employee(self, employee_id, proper_name, departments=False, actionType="New"): if departments and self.display_departments is not None: for x in departments: if x in self.display_departments: self.model.append(None, (employee_id, 0, proper_name, "*** %s Employee ***" % actionType, "#aaaaaa", True)) break else: self.model.append(None, (employee_id, 0, proper_name, "*** %s Employee ***" % actionType, "#aaaaaa", True)) def edit_employee(self, employee_id, proper_name, departments=False): self.remove_employee(employee_id) self.add_employee(employee_id, proper_name, departments, "Edited") def clock_in_employee(self, employee_id): """Sets the employee's isClockedIn column to True. Originally created to be used when an employee clocks in (w/ barcode) but the employee is filtered out of the model (i.e. only showing clocked in employees). If that is the only filter that applies this will make it unfilterd so that the treeview can scroll to it.""" p = self.find_employee(employee_id) if p is not None: self.model[p][5] = True def update(self, event=False): if event: employee_id = event.msg["serialNum"] emp_num = event.msg["empId"] proper_name = event.msg["properName"] if event.subject == self.namespace + "_clock_out": self.add_activity(employee_id, 0, proper_name, "", "#aaaaaa", False) log.info("%s [%s] clocked out", proper_name, emp_num) elif event.subject == self.namespace + "_clock_in": self.add_activity(employee_id, 0, proper_name, "*** IDLE ***", self.getActivityColor("idle"), True) log.info("%s [%s] clocked in", proper_name, emp_num) elif event.subject == self.namespace + "_lunch_in": activity = event.msg["activity"] self.add_activity(employee_id, activity["serialNum"], proper_name, activity["description"], self.getActivityColor("indirect"), True) log.info("%s [%s] lunch in", proper_name, emp_num) elif event.subject == self.namespace + "_break_in": activity = event.msg["activity"] self.add_activity(employee_id, activity["serialNum"], proper_name, activity["description"], self.getActivityColor("indirect"), True) log.info("%s [%s] break in", proper_name, emp_num) elif event.subject == self.namespace + "_lunch_out": activity = event.msg["activity"] self.remove_activity(employee_id, activity["serialNum"], proper_name) log.info("%s [%s] lunch out", proper_name, emp_num) elif event.subject == self.namespace + "_break_out": activity = event.msg["activity"] self.remove_activity(employee_id, activity["serialNum"], proper_name) log.info("%s [%s] break out", proper_name, emp_num) elif event.subject == self.namespace + "_indirect_start" or event.subject == self.namespace + "_indirect_resume": activity = event.msg["activity"] self.add_activity(employee_id, activity["serialNum"], proper_name, activity["description"], self.getActivityColor("indirect"), True) log.info("%s [%s] started or resumed indirect activity: [%s] %s", proper_name, emp_num, activity["serialNum"], activity["description"]) elif event.subject == self.namespace + "_indirect_stop" or event.subject == self.namespace + "_indirect_suspend": activity = event.msg["activity"] self.remove_activity(employee_id, activity["serialNum"], proper_name) log.info("%s [%s] stoped or suspended indirect activity: [%s] %s", proper_name, emp_num, activity["serialNum"], activity["description"]) elif event.subject == self.namespace + "_production_start" or event.subject == self.namespace + "_production_resume": activity = event.msg["activity"] self.add_activity(employee_id, activity["serialNum"], proper_name, activity["description"], self.getActivityColor("production"), True) log.info("%s [%s] started or resumed production activity: [%s] %s", proper_name, emp_num, activity["serialNum"], activity["description"]) elif event.subject == self.namespace + "_production_stop" or event.subject == self.namespace + "_production_suspend": activity = event.msg["activity"] self.remove_activity(employee_id, activity["serialNum"], proper_name) log.info("%s [%s] stoped or suspended production activity: [%s] %s", proper_name, emp_num, activity["serialNum"], activity["description"]) elif event.subject == self.namespace + "_setup_start" or event.subject == self.namespace + "_setup_resume": activity = event.msg["activity"] self.add_activity(employee_id, activity["serialNum"], proper_name, activity["description"], self.getActivityColor("setup"), True) log.info("%s [%s] started or resumed setup activity: [%s] %s", proper_name, emp_num, activity["serialNum"], activity["description"]) elif event.subject == self.namespace + "_setup_stop" or event.subject == self.namespace + "_setup_suspend": activity = event.msg["activity"] self.remove_activity(employee_id, activity["serialNum"], proper_name) log.info("%s [%s] stoped or suspended setup activity: [%s] %s", proper_name, emp_num, activity["serialNum"], activity["description"]) elif event.subject == self.namespace + "_employee_delete": self.remove_employee(employee_id, proper_name) log.info("%s [%s] deleted", proper_name, emp_num) elif event.subject == self.namespace + "_employee_add": departments = event.msg["departments"] self.add_employee(employee_id, proper_name, departments) log.info("%s [%s] added", proper_name, emp_num) elif event.subject == self.namespace + "_employee_edit": departments = event.msg["departments"] self.edit_employee(employee_id, proper_name, departments) log.info("%s [%s] edited", proper_name, emp_num) else: self.modelfilter.refilter() self.view.expand_all() def selectTerminal(self, terminal_name): terminal_factory = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["Terminal"])) try: if terminal_name is "": raise ValueError terminal = terminal_factory.setTerminalByTerminalName( terminal_name ) return terminal except ValueError: return None except SndcsTerminalError: return None