Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ## -*- coding: utf-8 -*-
- import itertools
- import time
- import xml.dom.minidom
- from openobject.i18n import format
- from openobject.widgets import JSLink, CSSLink
- from openobject.i18n.format import format_date_custom
- from openerp.utils import rpc, node_attributes
- from openerp.widgets import TinyWidget, ConcurrencyInfo
- from view_calendar.widgets._base import ICalendar, TinyCalendar, TinyEvent, choice_colors
- from view_calendar.widgets.utils import Day, Week, Month, Year
- from view_calendar.widgets import GanttCalendar
- from view_calendar.widgets.widgets import _get_selection_day, MiniCalendar, GroupBox, Sidebar
- from mitr.widgets.utils import ProjectWeek
- from mitr.utils.weeks import weeks_from, parse_datetime2date
- from calendar import timegm
- from mitr.utils.weeks import fmt_striftime_th
- from datetime import timedelta, datetime
- from random import randrange
- from mitr.settings import COLOR_KEYS
- ACTUAL_FACTOR = 0.1
- ACTUAL_COLOR = "#666666"
- class OsTinyEvent(TinyEvent):
- params = ['starts', 'ends', 'title', 'description', 'color', 'record_id', 'create_date', 'create_uid', 'write_uid', 'write_date', 'actual', 'plan', 'progress' ]
- def __init__(self, record, starts, ends, title='', description='', dayspan=0, color=None, classes=None, actual=0, plan=0, progress=0):
- super(OsTinyEvent, self).__init__(record, starts, ends, title=title, description=description, dayspan=dayspan, color=color, classes=classes)
- self.actual = actual
- self.plan = plan
- self.progress = progress
- class MitrGanttCalendar(ICalendar):
- template = 'mitr/widgets/templates/gantt.mako'
- params = ['title', 'level', 'groups', 'days', 'events', 'calendar_fields', 'date_format',
- 'selected_day', 'mode', 'headers', 'subheaders', 'model', 'ids']
- member_widgets = ['sidebar']
- level = None
- groups = None
- title = None
- days = None
- headers = None
- subheaders = None
- mode = 'week'
- sidebar = None
- css = [CSSLink("view_calendar", 'css/calendar.css'),
- CSSLink("view_calendar", 'css/screen.css'),
- CSSLink("view_calendar", 'css/calendar_gantt.css'),
- CSSLink("mitr", 'css/gantt.css')]
- javascript = [JSLink("view_calendar", 'javascript/calendar_date.js'),
- JSLink("view_calendar", 'javascript/calendar_utils.js'),
- JSLink("view_calendar", 'javascript/calendar_box.js'),
- JSLink("view_calendar", 'javascript/calendar_month.js'),
- JSLink("view_calendar", 'javascript/calendar_week.js'),
- #JSLink("view_calendar", 'javascript/calendar_gantt.js'),
- JSLink("mitr", 'javascript/mitr_calendar_gantt.js')
- ]
- def __init__(self, model, ids, view, domain=[], context={}, options=None, conditions=[]):
- self.level = None
- self.groups = []
- self.days = []
- self.headers = []
- super(MitrGanttCalendar, self).__init__(model, ids, view, domain, context, options)
- # y, m, d = time.localtime()[:3]
- # if options:
- # y, m, d = options.date1[:3]
- # if ids:
- proxy = rpc.RPCProxy(self.model)
- if not self.ids:
- self.ids = proxy.search(conditions, order="date_start")
- ctx = rpc.session.context.copy()
- start_week_id = proxy.search(conditions, order='date_start', limit=1)
- if not start_week_id:
- return None
- start_week = time.strptime(proxy.read(start_week_id)[0]['date_start'], "%Y-%m-%d %H:%M:%S")
- y = start_week.tm_year
- m = start_week.tm_mon
- d = start_week.tm_mday
- end_week = time.strptime(proxy.read(proxy.search(conditions, order='date_end DESC', limit=1))[0]['date_end'], "%Y-%m-%d %H:%M:%S")
- weeks = weeks_from(start_week, end_week) + 2
- #y, m, d = time.strptime(self._result[0]['date_start'], "%y-%m-%d ")
- #raise Exception(weeks)
- day = Day(y, m, d)
- w = ProjectWeek(day, weeks)
- # wp = w - 1
- # wn = w + 1
- #self.days = wp.days + w.days + wn.days
- self.days = w.days
- self.title = _(u"%s - %s") % (ustr(self.days[0]), ustr(self.days[-1]))
- self.selected_day = _get_selection_day(day, self.selected_day, 'week')
- self.headers = [(7, _("สัปดาห์ที่ %s") % fmt_striftime_th(w[0]+(w1*7), '%W (%b %Y)', ['%b'])) for w1 in range(0,weeks)]
- #self.headers = [(7, _("สัปดาห์ที่ %s") % (w[0]+(w1*7)).strftime('%W (%b %Y)')) for w1 in range(0,weeks)]
- self.subheaders = [format_date_custom(x, "E d") for x in itertools.chain(w)]
- if self.level:
- field = self.level['link']
- fields = rpc.RPCProxy(self.model).fields_get([field], rpc.session.context)
- self.fields.update(fields)
- self.events = self.get_events(self.days)
- self.groups = self.get_groups(self.events)
- minical = MiniCalendar(day)
- groupbox = GroupBox(self.colors, self.color_values, day,
- group_relation=self.fields[self.color_field],
- title=(self.color_field or None) and self.fields[self.color_field]['string'], mode=self.mode)
- self.sidebar = Sidebar(minical, groupbox, self.use_search)
- def parse(self, root, fields):
- info_fields = []
- attrs = node_attributes(root)
- for node in root.childNodes:
- attrs = node_attributes(node)
- if node.localName == 'field':
- info_fields.append(attrs['name'])
- if node.localName == 'level':
- self.level = attrs
- info_fields += self.parse(node, fields)
- return info_fields
- def get_event_widget(self, event):
- title = '' # the title
- description = [] # the description
- if self.info_fields:
- f = self.info_fields[0]
- s = event[f]
- if isinstance(s, (tuple, list)): s = s[-1]
- title = ustr(s)
- for f in self.info_fields[1:]:
- s = event[f]
- if isinstance(s, (tuple, list)):
- s = s[-1]
- if s:
- description.append(ustr(s))
- starts = event.get(self.date_start)
- ends = event.get(self.date_stop)
- #ends = event.get(self.date_delay) or 1.0
- span = 0
- """if starts and ends:
- n = 0
- h = ends
- if ends == self.day_length:
- span = 1
- elif ends > self.day_length:
- n = ends / self.day_length
- h = ends % self.day_length
- n = int(math.floor(n))
- if h > 0:
- span = n + 1
- else:
- span = n
- ends = time.localtime(time.mktime(starts) + (h * 60 * 60) + (n * 24 * 60 * 60))
- if starts and self.date_stop:
- ends = event.get(self.date_stop)
- if not ends:
- ends = time.localtime(time.mktime(starts) + 60 * 60)
- tds = time.mktime(starts)
- tde = time.mktime(ends)
- if tds >= tde:
- tde = tds + 60 * 60
- ends = time.localtime(tde)
- n = (tde - tds) / (60 * 60)
- if n >= self.day_length:
- span = math.ceil(n / 24)"""
- starts = format.format_datetime(starts, "datetime", True)
- ends = format.format_datetime(ends, "datetime", True)
- color_key = event.get(self.color_field)
- #color = self.colors.get(color_key)
- color = (rpc.session['user_name'], rpc.session['uid'], COLOR_KEYS[event['color']])
- classes = self._get_classes(event)
- title = title.strip()
- description = ', '.join(description).strip()
- timesheet_res = rpc.RPCProxy('os.res.timesheet')
- task_week_res = rpc.RPCProxy('os.task.week')
- actual = timesheet_res.sum_actual_by_task(event['id'])
- plan = task_week_res.sum_plan_by_task(event['id'])
- progress = 0
- if plan != 0:
- progress = (actual/plan) * 100
- if isinstance(event['id'], int):
- event_log = rpc.session.execute('object', 'execute', self.model, 'perm_read', [event['id']])[0]
- event['create_date'] = event_log['create_date']
- event['create_uid'] = event_log['create_uid'][1]
- if isinstance(event_log['write_uid'], tuple):
- event_log['write_uid'] = event_log['write_uid'][1]
- event['write_uid'] = event_log['write_uid']
- event['write_date'] = event_log['write_date']
- else:
- color = (rpc.session['user_name'], rpc.session['uid'], ACTUAL_COLOR)
- return OsTinyEvent(event, starts, ends, title, description, dayspan=span, color=(color or None) and color[-1], classes=classes, actual=actual, plan=plan, progress=progress)
- def get_events(self, days):
- proxy = rpc.RPCProxy(self.model)
- """if self.date_stop:
- # use the correct algorithm:
- domain = self.domain + [(self.date_stop, '<=', days[-1].isoformat() + ' 23:59:59'),
- (self.date_start, '>=', days[0].isoformat() + ' 00:00:00')]
- else:
- # cannot use the correct algorithm, use the old one:
- first = days[0].month2.prev()[0] #HACK: add prev month
- domain = self.domain + [(self.date_start, '>', first.isoformat()),
- (self.date_start, '<', days[-1].next().isoformat())]"""
- domain = self.domain
- # convert color values from string to python values
- if self.color_values and self.color_field in self.fields:
- try:
- atr = self.fields[self.color_field]
- atr['required'] = False
- wid = get_widget(atr['type'])(**atr)
- vals = self.color_values[:]
- for i, v in enumerate(vals):
- try:
- vals[i] = wid.validator.to_python(v)
- except:
- pass
- domain.append((self.color_field, "in", vals))
- except Exception:
- pass
- if self.options and self.options.use_search:
- domain += self.options.search_domain
- ctx = rpc.session.context.copy()
- ctx.update(self.context)
- order_by = ('sequence' in self.fields or 0) and 'sequence'
- if self.color_field and self.fields[self.color_field].get('relation') and self.color_field not in [item[0] for item in domain]:
- if self.options and self.options.get('_terp_color_filters'):
- clr_field = self.options['_terp_color_filters']
- else:
- search_limit = 10
- need_to_add_the_user_to_the_list_of_users = False
- if self.context and self.color_field and \
- self.context.get('search_default_user_id') and \
- self.color_field == 'user_id' and \
- self.fields[self.color_field].get('relation') == 'res.users':
- need_to_add_the_user_to_the_list_of_users = True
- clr_field = rpc.RPCProxy(self.fields[self.color_field]['relation']).search([], 0, search_limit, 0, ctx)
- if need_to_add_the_user_to_the_list_of_users and self.context.get('search_default_user_id') not in clr_field:
- clr_field[search_limit-1] = self.context.get('search_default_user_id')
- domain.append((self.color_field, 'in', clr_field))
- if not self.ids:
- ids = proxy.search(domain, 0, 0, order_by, ctx)
- else:
- ids = self.ids
- result = proxy.read(ids, self.fields.keys()+['__last_update', 'color'], ctx)
- ConcurrencyInfo.update(self.model, result)
- self.concurrency_info = ConcurrencyInfo(self.model, ids)
- if self.color_field:
- for evt in result:
- key = evt[self.color_field]
- name = key
- value = key
- if isinstance(key, list): # M2O, XMLRPC returns List instead of Tuple
- evt[self.color_field] = key = tuple(key)
- if isinstance(key, tuple): # M2O
- value, name = key
- self.colors[key] = (name, value, None)
- colors = choice_colors(len(self.colors))
- for i, (key, value) in enumerate(self.colors.items()):
- self.colors[key] = (value[0], value[1], colors[i])
- events = []
- for evt in result:
- self.convert(evt)
- events.append(self.get_event_widget(evt))
- # add the actual data
- evt2 = self.get_actual_data(evt)
- events.append(self.get_event_widget(evt2))
- if self.date_stop:
- result = events
- else:
- # filter out the events which are not in the range
- result = []
- for e in events:
- if e.starts is None:
- continue
- if e.dayspan > 0 and days[0] - e.dayspan < e.starts:
- result.append(e)
- if e.dayspan == 0 and days[0] <= e.starts:
- result.append(e)
- return result
- def get_groups(self, events):
- if not self.level:
- return []
- obj = self.level['object']
- field = self.level['link']
- keys = []
- groups = {}
- for evt in events:
- group_id = evt.record[field]
- group_title = 'None'
- if not group_id: # create dummy group
- group_id = 0
- if isinstance(group_id, (list, tuple)):
- group_id, group_title = evt.record[field]
- elif group_id:
- group_id, group_title = rpc.RPCProxy(obj).name_get([group_id], rpc.session.context)[0]
- group = groups.setdefault(group_id, {'id': str(group_id), 'title': group_title, 'model': obj, 'items': [], 'actual':0, 'plan':0, 'progress':0})
- group['items'].append(str(evt.record_id))
- # get all event in this group and sum actual and planed then progress
- group['actual'] += evt.actual
- group['plan'] += evt.plan
- group['progress'] += evt.progress
- if group_id not in keys:
- keys.append(group_id)
- return [groups[i] for i in keys]
- def get_actual_data(self, evt):
- data = evt.copy()
- data['name'] = ""
- # get timesheets which contain task id
- # find first and last order by date
- timesheet_res = rpc.RPCProxy('os.res.timesheet')
- args = [('task_id', '=', int(data['id'])), "!", ('hour', '=', 0)]
- first_ids = timesheet_res.search(args, order="date", limit=1)
- last_ids = timesheet_res.search(args, order='date DESC', limit=1)
- new_date_start = new_date_end = None
- if first_ids and last_ids:
- first = timesheet_res.read(first_ids[0], ('date',))
- last = timesheet_res.read(last_ids[0], ('date',))
- new_date_start = first['date']
- new_date_end = last['date']
- data['id'] += ACTUAL_FACTOR
- if new_date_start is not None:
- data[self.date_start] = parse_datetime2date(new_date_start).timetuple()
- else:
- start = data[self.date_start]
- new_date = datetime(start.tm_year-5, start.tm_mon, start.tm_mday, start.tm_hour, start.tm_min, start.tm_sec)
- data[self.date_start] = datetime(day=new_date.day, month=new_date.month, year=new_date.year, hour=new_date.hour, minute=new_date.minute, second=new_date.second).timetuple()
- if new_date_end is not None:
- data[self.date_stop] = parse_datetime2date(new_date_end).timetuple()
- else:
- end = data[self.date_start]
- new_date = datetime(end.tm_year-5, end.tm_mon, end.tm_mday, end.tm_hour, end.tm_min, end.tm_sec)
- data[self.date_stop] = datetime(day=new_date.day, month=new_date.month, year=new_date.year, hour=new_date.hour, minute=new_date.minute, second=new_date.second).timetuple()
- """data['id'] += ACTUAL_FACTOR
- start = data[self.date_start]
- new_date = datetime(start.tm_year, start.tm_mon, start.tm_mday, start.tm_hour, start.tm_min, start.tm_sec) + timedelta(days=randrange(0,2))
- data[self.date_start] = datetime(day=new_date.day, month=new_date.month, year=new_date.year, hour=new_date.hour, minute=new_date.minute, second=new_date.second).timetuple()
- end = evt[self.date_stop]
- new_date = datetime(end.tm_year, end.tm_mon, end.tm_mday, end.tm_hour, end.tm_min, end.tm_sec) + timedelta(days=randrange(0,2))
- data[self.date_stop] = datetime(day=new_date.day, month=new_date.month, year=new_date.year, hour=new_date.hour, minute=new_date.minute, second=new_date.second ).timetuple()"""
- return data
Add Comment
Please, Sign In to add comment