def load(self): query = globals.db.query('tasks', order_by='due_date,active_date', limit=10) # Exclude deleted cases via /cases/ LEFT JOIN query.join('LEFT JOIN cases USING (case_id)') subquery = query.sub_expr(conjunction='OR') subquery.where('NOT cases.deleted') subquery.where('cases.deleted IS null') subquery.where('tasks.case_id IS null') if 0: # Filter for tasks assigned to us subquery = query.sub_expr(conjunction = 'OR') task_subscription_query(subquery, self.credentials) # OR tasks assigned by us that are overdue overdue_query = subquery.sub_expr(conjunction = 'AND') uquery = overdue_query.sub_expr(conjunction = 'OR') uquery.where('assigner_id = %s', self.credentials.user.user_id) uquery.where('originator_id = %s', self.credentials.user.user_id) overdue_query.where('due_date <= CURRENT_TIMESTAMP') else: task_subscription_query(query, self.credentials) query.where('active_date <= CURRENT_TIMESTAMP') query.where('completed_date is null') query.join('LEFT JOIN persons USING (person_id)') now = datetime.now() self[:] = [QuickTask(now, *row) for row in query.fetchcols(QuickTask.cols)]
def __init__(self, params, syndrome_id, title, row, column, page, empty_rowsncols=True, empty_pages=True): self.params = params self.syndrome_id = syndrome_id self.title = title self.row = row self.col = column self.page = page self.empty_rowsncols = empty_rowsncols self.empty_pages = empty_pages self.form_name = None self.form_table = None self.cols = [] for axis in (self.row, self.col, self.page): if axis.form_name: if self.form_name and self.form_name != axis.form_name: raise Error('Crosstab between different forms is not ' 'supported') self.form_name = axis.form_name self.form_table = axis.table if axis.table: self.cols.append(axis.tabcol) axis.options.append((None, 'Missing')) axis.options.append((TOTAL, 'TOTAL')) self.date = datetime.now()
def new(cls, credentials, syndrome_id, from_search=None, use_person_id=None, defer_case_id=False, **kwargs): """ Alternate constructor, used when case is new """ if defer_case_id: case_id = None else: case_id = globals.db.nextval('cases', 'case_id') case_seed = dict(case_id=case_id, syndrome_id=syndrome_id, notification_datetime=datetime.now()) if from_search is not None: if use_person_id is None: kwargs['seed_person'] = from_search.person if from_search.case_status != '!': case_seed['case_status'] = from_search.case_status if from_search.case_assignment != '!': case_seed['case_assignment'] = from_search.case_assignment case_seed['local_case_id'] = from_search.local_case_id # case_seed['notes'] = from_search.notes # case_seed['notification_datetime'] = from_search.notification_datetime # case_seed['onset_datetime'] = from_search.onset_datetime case_seed['notifier_name'] = from_search.notifier_name # case_seed['notifier_contact'] = from_search.notifier_contact case_row = globals.db.new_row('cases', **case_seed) case = cls(credentials, case_row, **kwargs) if use_person_id is not None: case.use_person_id(use_person_id)
def load(self): query = globals.db.query('tasks', order_by='due_date,active_date', limit=10) # Exclude deleted cases via /cases/ LEFT JOIN query.join('LEFT JOIN cases USING (case_id)') subquery = query.sub_expr(conjunction='OR') subquery.where('NOT cases.deleted') subquery.where('cases.deleted IS null') subquery.where('tasks.case_id IS null') if 0: # Filter for tasks assigned to us subquery = query.sub_expr(conjunction='OR') task_subscription_query(subquery, self.credentials) # OR tasks assigned by us that are overdue overdue_query = subquery.sub_expr(conjunction='AND') uquery = overdue_query.sub_expr(conjunction='OR') uquery.where('assigner_id = %s', self.credentials.user.user_id) uquery.where('originator_id = %s', self.credentials.user.user_id) overdue_query.where('due_date <= CURRENT_TIMESTAMP') else: task_subscription_query(query, self.credentials) query.where('active_date <= CURRENT_TIMESTAMP') query.where('completed_date is null') query.join('LEFT JOIN persons USING (person_id)') now = datetime.now() self[:] = [ QuickTask(now, *row) for row in query.fetchcols(QuickTask.cols) ]
def _update(self, db, inplace=False, complete=False): if inplace: # Update in place task = self._locked_fetch(db, self.seed_task_id, self.this_user_id) if task is None: raise TaskError('Update failed - the task has been changed' ' by another user') _set_unlocked(task) _clear_completed(task) else: # Create a new task, closing the old one if necessary. if self.seed_task_id is not None: task = self._locked_fetch(db, self.seed_task_id) if _our_lock(task, self.this_user_id): _set_unlocked(task) _set_completed(task, self.this_user_id) task.db_update() task = db.new_row('tasks') task.parent_task_id = self.seed_task_id task.creation_date = datetime.now() self._copy(self, task) if not self.task_description or not self.task_description.strip(): raise TaskError('Task must have a description') old_due = None if task.due_date and task.active_date: old_due = task.due_date - task.active_date if self.active_abs and self.active_abs.strip(): try: active_abs = datetime.mx_parse_datetime(self.active_abs) except datetime.Error, e: raise TaskError('Start date: %s' % e) if not datetime.near(active_abs, task.active_date): task.active_date = active_abs
def new(cls, credentials, syndrome_id, from_search=None, use_person_id=None, defer_case_id=False, **kwargs): """ Alternate constructor, used when case is new """ if defer_case_id: case_id = None else: case_id = globals.db.nextval('cases', 'case_id') case_seed = dict(case_id=case_id, syndrome_id=syndrome_id, notification_datetime=datetime.now()) if from_search is not None: if use_person_id is None: kwargs['seed_person'] = from_search.person if from_search.case_status != '!': case_seed['case_status'] = from_search.case_status if from_search.case_assignment != '!': case_seed['case_assignment'] = from_search.case_assignment case_seed['local_case_id'] = from_search.local_case_id # case_seed['notes'] = from_search.notes # case_seed['notification_datetime'] = from_search.notification_datetime # case_seed['onset_datetime'] = from_search.onset_datetime case_seed['notifier_name'] = from_search.notifier_name # case_seed['notifier_contact'] = from_search.notifier_contact case_row = globals.db.new_row('cases', **case_seed) case = cls(credentials, case_row, **kwargs) if use_person_id is not None: case.use_person_id(use_person_id) if from_search and from_search.tags: # This doesn't give the normal "seed" semantics. case.tags.cur = from_search.tags return case
def _write_version(self, name, *args): form = self.to_form() if self.cred: form.author = self.cred.user.fullname form.username = self.cred.user.username form.update_time = datetime.now() globals.formlib.save(form, name, *args) self.root.update_time = form.update_time return form.version, form.update_time
def set_deleted(self, delete, reason=None): if delete: timestamp = datetime.now().mx() else: timestamp = None reason = None query = globals.db.query('case_form_summary') query.where('summary_id = %s', self.summary_id) query.update('deleted=%s, delete_reason=%s, delete_timestamp=%s', delete, reason, timestamp) globals.db.commit() self.deleted = delete
def __init__(self, db, credentials, task_id): self.user_id = credentials.user.user_id self.task_id = task_id self.done = False task = self._locked_fetch(db, self.task_id) self._copy(task, self) self.assigner = unituser.users[task.assigner_id] self.was_locked = None if task.locked_by_id is not None and task.locked_by_id != self.user_id: username = unituser.users[task.locked_by_id].username self.was_locked = '%s %s' % (username, datetime.relative(task.locked_date)) task.locked_by_id = self.user_id task.locked_date = datetime.now() task.db_update()
def load(self): query = globals.db.query('tasks') if self.queue_id is not None: query.where('queue_id = %s', self.queue_id) now = datetime.now() self.total = self.completed = self.active =\ self.overdue = self.locked = 0 cols = 'due_date', 'completed_date', 'locked_by_id' for due_date, completed_date, locked_by_id in query.fetchcols(cols): self.total += 1 if completed_date is None: self.active += 1 if due_date < now: self.overdue += 1 if locked_by_id is not None: self.locked += 1 else: self.completed += 1
def to_dobprec(self, now=None): """ Given an integer age and unit code, return a DOB & precision """ if self.units == 'y': age = DateTime.RelativeDateTime(years=self.age) prec = PREC_YEAR elif self.units == 'm': age = DateTime.RelativeDateTime(months=self.age) prec = PREC_MONTH elif self.units == 'w': age = DateTime.RelativeDateTime(weeks=self.age) prec = PREC_WEEK elif self.units == 'd': age = DateTime.RelativeDateTime(days=self.age) prec = PREC_DAY else: raise AssertionError('Bad age units: %s' % self.units) if now is None: now = datetime.now() return now - age, prec
def active(self): now = datetime.now() return (self.enabled and (not self.post_date or self.post_date <= now) and (not self.expiry_date or self.expiry_date > now))
try: case_row.notification_datetime = \ datetime.mx_parse_datetime(case_row.notification_datetime) except datetime.Error, e: raise ValidationError('%s: %s' % (_label('notification_datetime'), e)) if datetime.is_later_than(self.case_row.onset_datetime, self.case_row.notification_datetime): raise ValidationError( '%s must be after %s' % (_label('notification_datetime'), _label('onset_datetime'))) if datetime.is_later_than(self.person.DOB, self.case_row.onset_datetime): raise ValidationError('%s must be after %s' % (_label('onset_datetime'), _label('DOB'))) if datetime.is_later_than(self.person.DOB, datetime.now()): raise ValidationError('\'%s\': %s must not be in the future' % (self.person.DOB, _label('DOB'))) if datetime.is_later_than(self.case_row.onset_datetime, datetime.now()): raise ValidationError( '\'%s\': %s must not be in the future' % (self.case_row.onset_datetime, _label('onset_datetime'))) if datetime.is_later_than(self.case_row.notification_datetime, datetime.now()): raise ValidationError('\'%s\': %s must not be in the future' % (self.case_row.notification_datetime, _label('notification_datetime'))) def has_changed(self): return (self.tags.has_changed() or self.case_row.db_has_changed()
def page_rows(self, cred): self.page_jump() # Fetch task rows results = paged_search.SortablePagedSearch.page_rows(self) # Fetch associated entities case_dict = self.db.table_dict('cases') user_dict = self.db.table_dict('users') unit_dict = self.db.table_dict('units') person_dict = self.db.table_dict('persons') queue_dict = self.db.table_dict('workqueues') for task in results: if task.case_id is not None: case_dict.want(task.case_id) if task.originator_id is not None: user_dict.want(task.originator_id) if task.assigner_id is not None: user_dict.want(task.assigner_id) if task.locked_by_id is not None: user_dict.want(task.locked_by_id) if task.completed_by_id is not None: user_dict.want(task.completed_by_id) queue_dict.want(task.queue_id) queue_dict.preload() for queue in queue_dict.values(): if queue.unit_id is not None: unit_dict.want(queue.unit_id) if queue.user_id is not None: user_dict.want(queue.user_id) case_dict.preload() unit_dict.preload() user_dict.preload() for case in case_dict.itervalues(): person_dict.want(case.person_id) person_dict.preload() # Now join it all together now = datetime.now() for task in results: task.active_relative = datetime.relative(task.active_date, now) active_days = (now - task.active_date).days task.active_color = trafficlight.web_trafficlight(active_days, 30) if task.completed_date: task.complete_color = '#cccccc' if task.action == tasks.ACTION_THREAD_DELETED: task.complete_relative = 'DELETED' else: task.complete_relative = 'completed' completed_by = user_dict.get(task.completed_by_id) if completed_by is not None: task.complete_relative += ' by %s ' % completed_by.username task.complete_relative += str(task.completed_date) elif task.due_date is None: task.complete_color = 'transparent' task.complete_relative = '' else: due_days = (now - task.due_date).days + 15 task.complete_color = trafficlight.web_trafficlight( due_days, 30) task.complete_relative = datetime.relative(task.due_date, now) task.originator = user_dict.get(task.originator_id) task.assigner = user_dict.get(task.assigner_id) task.locked_by = user_dict.get(task.locked_by_id) task.locked_relative = datetime.relative(task.locked_date, now) queue = queue_dict.get(task.queue_id) if queue is None: task.assignee = '' elif queue.unit_id is not None: task.assignee = '%s: %s' %\ (config.unit_label, unit_dict.get(queue.unit_id).name) elif queue.user_id is not None: task.assignee = 'User: %s' %\ user_dict.get(queue.user_id).username else: task.assignee = 'Queue: %s' % queue.name task.case_summary = task.contact_summary = '' if task.case_id is not None: task.case_summary = case_summary(self.db, task.case_id, case_dict, person_dict) task.action_summary = tasks.action_desc.get(task.action, 'unknown') return results
def merge(self, credentials): # lock the relevent cases case_a, case_b = self._fetch_cases(for_update=True) # Safety checks: not_same_person = (case_a.person_id != case_b.person_id) syndrome_mismatch = (case_a.syndrome_id != case_b.syndrome_id) if not_same_person or syndrome_mismatch: raise MergeError('Unable to merge - consistency check failed') for mc in self.fields: try: mc.apply(case_a, case_b) except CaseHasChanged: case_a.db_revert() case_b.db_revert() self.init_fields(case_a, case_b) raise # Which direction to merge? if self.keep == 'a': update_case, delete_case = case_a, case_b else: update_case, delete_case = case_b, case_a update_desc = update_case.db_desc() delete_desc = delete_case.db_desc() # XXX Describe tag changes - how? if not update_desc: update_desc = 'no edits required' if not delete_desc: delete_desc = 'no edits required' if update_case.deleted and not delete_case.deleted: update_case.deleted = False update_case.delete_reason = None update_case.delete_timestamp = None delete_case.deleted = True delete_case.delete_reason = 'Merged to %s' % update_case.case_id delete_case.delete_timestamp = datetime.now() curs = globals.db.cursor() try: # merge contacts dbobj.execute( curs, 'UPDATE case_contacts SET contact_id=%s' ' WHERE contact_id=%s' ' AND case_id != %s' ' AND case_id NOT IN' ' (SELECT case_id FROM case_contacts' ' WHERE contact_id=%s)', (update_case.case_id, delete_case.case_id, update_case.case_id, update_case.case_id)) dbobj.execute( curs, 'UPDATE case_contacts SET case_id=%s' ' WHERE case_id=%s' ' AND contact_id != %s' ' AND contact_id NOT IN' ' (SELECT contact_id FROM case_contacts' ' WHERE case_id=%s)', (update_case.case_id, delete_case.case_id, update_case.case_id, update_case.case_id)) dbobj.execute( curs, 'DELETE FROM case_contacts' ' WHERE case_id=%s OR contact_id=%s', (delete_case.case_id, delete_case.case_id)) # case_form_summary dbobj.execute( curs, 'UPDATE case_form_summary SET case_id=%s' ' WHERE case_id=%s', (update_case.case_id, delete_case.case_id)) # case_acl dbobj.execute(curs, 'UPDATE case_acl SET case_id=%s' ' WHERE case_id=%s', (update_case.case_id, delete_case.case_id)) # tasks dbobj.execute(curs, 'UPDATE tasks SET case_id=%s' ' WHERE case_id=%s', (update_case.case_id, delete_case.case_id)) dbobj.execute(curs, 'UPDATE tasks SET case_id=%s' ' WHERE case_id=%s', (update_case.case_id, delete_case.case_id)) dbobj.execute(curs, 'UPDATE user_log SET case_id=%s' ' WHERE case_id=%s', (update_case.case_id, delete_case.case_id)) dbobj.execute(curs, 'UPDATE user_log SET case_id=%s' ' WHERE case_id=%s', (update_case.case_id, delete_case.case_id)) finally: curs.close() update_case.db_update() tag_desc = casetags.set_case_tags(update_case.case_id, update_case.tags) delete_case.db_update() desc = 'Merge System ID %s into %s, UPDATED %s %s, DELETED %s' %\ (delete_case.case_id, update_case.case_id, update_desc, tag_desc, delete_desc) credentials.user_log(globals.db, desc, case_id=update_case.case_id) return update_case, delete_case
def _set_completed(task, user_id): assert user_id is not None if not task.completed_date: task.completed_date = datetime.now() task.completed_by_id = user_id
def page_rows(self, cred): self.page_jump() # Fetch task rows results = paged_search.SortablePagedSearch.page_rows(self) # Fetch associated entities case_dict = self.db.table_dict('cases') user_dict = self.db.table_dict('users') unit_dict = self.db.table_dict('units') person_dict = self.db.table_dict('persons') queue_dict = self.db.table_dict('workqueues') for task in results: if task.case_id is not None: case_dict.want(task.case_id) if task.originator_id is not None: user_dict.want(task.originator_id) if task.assigner_id is not None: user_dict.want(task.assigner_id) if task.locked_by_id is not None: user_dict.want(task.locked_by_id) if task.completed_by_id is not None: user_dict.want(task.completed_by_id) queue_dict.want(task.queue_id) queue_dict.preload() for queue in queue_dict.values(): if queue.unit_id is not None: unit_dict.want(queue.unit_id) if queue.user_id is not None: user_dict.want(queue.user_id) case_dict.preload() unit_dict.preload() user_dict.preload() for case in case_dict.itervalues(): person_dict.want(case.person_id) person_dict.preload() # Now join it all together now = datetime.now() for task in results: task.active_relative = datetime.relative(task.active_date, now) active_days = (now - task.active_date).days task.active_color = trafficlight.web_trafficlight(active_days, 30) if task.completed_date: task.complete_color = '#cccccc' if task.action == tasks.ACTION_THREAD_DELETED: task.complete_relative = 'DELETED' else: task.complete_relative = 'completed' completed_by = user_dict.get(task.completed_by_id) if completed_by is not None: task.complete_relative += ' by %s ' % completed_by.username task.complete_relative += str(task.completed_date) elif task.due_date is None: task.complete_color = 'transparent' task.complete_relative = '' else: due_days = (now - task.due_date).days + 15 task.complete_color = trafficlight.web_trafficlight(due_days, 30) task.complete_relative = datetime.relative(task.due_date, now) task.originator = user_dict.get(task.originator_id) task.assigner = user_dict.get(task.assigner_id) task.locked_by = user_dict.get(task.locked_by_id) task.locked_relative = datetime.relative(task.locked_date, now) queue = queue_dict.get(task.queue_id) if queue is None: task.assignee = '' elif queue.unit_id is not None: task.assignee = '%s: %s' %\ (config.unit_label, unit_dict.get(queue.unit_id).name) elif queue.user_id is not None: task.assignee = 'User: %s' %\ user_dict.get(queue.user_id).username else: task.assignee = 'Queue: %s' % queue.name task.case_summary = task.contact_summary = '' if task.case_id is not None: task.case_summary = case_summary(self.db, task.case_id, case_dict, person_dict) task.action_summary = tasks.action_desc.get(task.action, 'unknown') return results
def run_report(ctx, reportparams): try: rpt = reportparams.report(ctx.locals._credentials, msgs=ctx) except reports.ReportParamError, e: ctx.add_error(e) else: if ctx.have_errors(): return if not rpt: raise reports.ReportParamError('No matching records found') else: ctx.push_page('report_' + rpt.render, rpt) def report_export(ctx, reportparams): # Consistency check (form versions) msgs = reportparams.check() ctx.add_messages(msgs) if msgs.have_errors(): # XXX Uh oh - warnings go into a black hole? return try: row_gen = reportparams.export_rows(ctx.locals._credentials) except reports.ReportParamError, e: ctx.add_error(e) else: filename = datetime.now().strftime(config.appname + '-%Y%m%d-%H%M.csv') page_common.csv_download(ctx, row_gen, filename)
def _set_assigned(task, user_id): assert user_id is not None task.assigner_id = user_id task.assignment_date = datetime.now()
try: case_row.notification_datetime = \ datetime.mx_parse_datetime(case_row.notification_datetime) except datetime.Error, e: raise ValidationError('%s: %s' % (_label('notification_datetime'), e)) if datetime.is_later_than(self.case_row.onset_datetime, self.case_row.notification_datetime): raise ValidationError('%s must be after %s' % (_label('notification_datetime'), _label('onset_datetime'))) if datetime.is_later_than(self.person.DOB, self.case_row.onset_datetime): raise ValidationError('%s must be after %s' % (_label('onset_datetime'), _label('DOB'))) if datetime.is_later_than(self.person.DOB, datetime.now()): raise ValidationError('\'%s\': %s must not be in the future' % (self.person.DOB, _label('DOB'))) if datetime.is_later_than(self.case_row.onset_datetime, datetime.now()): raise ValidationError('\'%s\': %s must not be in the future' % (self.case_row.onset_datetime, _label('onset_datetime'))) if datetime.is_later_than(self.case_row.notification_datetime, datetime.now()): raise ValidationError('\'%s\': %s must not be in the future' % (self.case_row.notification_datetime, _label('notification_datetime'))) def has_changed(self): return ( self.tags.has_changed() or self.case_row.db_has_changed() or
def merge(self, credentials): # lock the relevent cases case_a, case_b = self._fetch_cases(for_update=True) # Safety checks: not_same_person = (case_a.person_id != case_b.person_id) syndrome_mismatch = (case_a.syndrome_id != case_b.syndrome_id) if not_same_person or syndrome_mismatch: raise MergeError('Unable to merge - consistency check failed') for mc in self.fields: try: mc.apply(case_a, case_b) except CaseHasChanged: case_a.db_revert() case_b.db_revert() self.init_fields(case_a, case_b) raise # Which direction to merge? if self.keep == 'a': update_case, delete_case = case_a, case_b else: update_case, delete_case = case_b, case_a update_desc = update_case.db_desc() delete_desc = delete_case.db_desc() # XXX Describe tag changes - how? if not update_desc: update_desc = 'no edits required' if not delete_desc: delete_desc = 'no edits required' if update_case.deleted and not delete_case.deleted: update_case.deleted = False update_case.delete_reason = None update_case.delete_timestamp = None delete_case.deleted = True delete_case.delete_reason = 'Merged to %s' % update_case.case_id delete_case.delete_timestamp = datetime.now() curs = globals.db.cursor() try: # merge contacts dbobj.execute(curs, 'UPDATE case_contacts SET contact_id=%s' ' WHERE contact_id=%s' ' AND case_id != %s' ' AND case_id NOT IN' ' (SELECT case_id FROM case_contacts' ' WHERE contact_id=%s)', (update_case.case_id, delete_case.case_id, update_case.case_id, update_case.case_id)) dbobj.execute(curs, 'UPDATE case_contacts SET case_id=%s' ' WHERE case_id=%s' ' AND contact_id != %s' ' AND contact_id NOT IN' ' (SELECT contact_id FROM case_contacts' ' WHERE case_id=%s)', (update_case.case_id, delete_case.case_id, update_case.case_id, update_case.case_id)) dbobj.execute(curs, 'DELETE FROM case_contacts' ' WHERE case_id=%s OR contact_id=%s', (delete_case.case_id, delete_case.case_id)) # case_form_summary dbobj.execute(curs, 'UPDATE case_form_summary SET case_id=%s' ' WHERE case_id=%s', (update_case.case_id, delete_case.case_id)) # case_acl dbobj.execute(curs, 'UPDATE case_acl SET case_id=%s' ' WHERE case_id=%s', (update_case.case_id, delete_case.case_id)) # tasks dbobj.execute(curs, 'UPDATE tasks SET case_id=%s' ' WHERE case_id=%s', (update_case.case_id, delete_case.case_id)) dbobj.execute(curs, 'UPDATE tasks SET case_id=%s' ' WHERE case_id=%s', (update_case.case_id, delete_case.case_id)) dbobj.execute(curs, 'UPDATE user_log SET case_id=%s' ' WHERE case_id=%s', (update_case.case_id, delete_case.case_id)) dbobj.execute(curs, 'UPDATE user_log SET case_id=%s' ' WHERE case_id=%s', (update_case.case_id, delete_case.case_id)) finally: curs.close() update_case.db_update() tag_desc = casetags.set_case_tags(update_case.case_id, update_case.tags) delete_case.db_update() desc = 'Merge System ID %s into %s, UPDATED %s %s, DELETED %s' %\ (delete_case.case_id, update_case.case_id, update_desc, tag_desc, delete_desc) credentials.user_log(globals.db, desc, case_id=update_case.case_id) return update_case, delete_case
def get_default(self): if self.default == 'now': return datetime.now() else: return self.default