def append_record(self, history_type, timestamp=None, **kwargs): if timestamp and not isinstance(timestamp, datetime): raise TypeError("Invalid type for timestamp: {}".format( timestamp.__class__)) if history_type not in self.record_classes: raise ValueError( 'No record class registered for {}'.format(history_type)) clazz = self.record_classes[history_type] history = self._get_history_for_writing() record = clazz(self.context, timestamp=timestamp, **kwargs) record.append_to(history) if record.needs_syncing: # currently we only sync from the submitted side # to the dossier path = self.context.load_model().physical_path request_data = { 'data': advancedjson.dumps({ 'timestamp': record.timestamp, 'data': record.data, }) } dispatch_request( self.context.get_source_admin_unit_id(), '@@receive-proposal-history', path=path, data=request_data, ) return record
def append_record(self, history_type, timestamp=None, **kwargs): if timestamp and not isinstance(timestamp, datetime): raise TypeError("Invalid type for timestamp: {}".format( timestamp.__class__)) if history_type not in self.record_classes: raise ValueError('No record class registered for {}'.format( history_type)) clazz = self.record_classes[history_type] history = self._get_history_for_writing() record = clazz(self.context, timestamp=timestamp, **kwargs) record.append_to(history) if record.needs_syncing: # currently we only sync from the submitted side # to the dossier path = self.context.load_model().physical_path request_data = {'data': advancedjson.dumps({ 'timestamp': record.timestamp, 'data': record.data, })} dispatch_request( self.context.get_source_admin_unit_id(), '@@receive-proposal-history', path=path, data=request_data,) return record
def append_record(self, history_type, timestamp=None, **kwargs): if timestamp and not isinstance(timestamp, datetime): raise TypeError("Invalid type for timestamp: {}".format( timestamp.__class__)) if history_type not in self.record_classes: raise ValueError( 'No record class registered for {}'.format(history_type)) clazz = self.record_classes[history_type] history = self._get_history_for_writing() record = clazz(self.context, timestamp=timestamp, **kwargs) record.append_to(history) if record.needs_syncing and self.context.is_submitted(): path = self.context.get_sync_target_path() admin_unit_id = self.context.get_sync_admin_unit_id() request_data = { 'data': advancedjson.dumps({ 'timestamp': record.timestamp, 'data': record.data, }) } dispatch_request( admin_unit_id, '@@receive-proposal-history', path=path, data=request_data, ) return record
def change_remote_tasks_workflow_state(self, transition, text, responsible="", responsible_client=""): tasks = self.get_tasks_to_sync(transition) if not tasks: return False for task in tasks: response = dispatch_request( task.admin_unit_id, "@@sync-task-workflow-state-receive", task.physical_path, data={ "transition": transition, "text": text and text.encode("utf-8") or "", "responsible": responsible, "responsible_client": responsible_client, }, ) response_data = response.read().strip() if response_data != "OK": raise Exception( "Could not change task on remote admin unit %s (%s)" % (task.admin_unit_id, task.physical_path) ) return tasks
def accept_task_with_successor(dossier, predecessor_oguid, response_text): predecessor = Task.query.by_oguid(predecessor_oguid) # Transport the original task (predecessor) to this dossier. The new # response and task change is not yet done and will be done later. This # is necessary for beeing as transaction aware as possible. transporter = Transporter() successor = transporter.transport_from(dossier, predecessor.admin_unit_id, predecessor.physical_path) successor_tc = ISuccessorTaskController(successor) # Set the "X-CREATING-SUCCESSOR" flag for preventing the event handler # from creating additional responses per added document. successor.REQUEST.set('X-CREATING-SUCCESSOR', True) # copy documents and map the intids doc_transporter = getUtility(ITaskDocumentsTransporter) comment = _(u'version_message_accept_task', default=u'Document copied from task (task accepted)') intids_mapping = doc_transporter.copy_documents_from_remote_task( predecessor, successor, comment=comment) # copy the responses response_transporter = IResponseTransporter(successor) response_transporter.get_responses(predecessor.admin_unit_id, predecessor.physical_path, intids_mapping=intids_mapping) # Move current responsible from predecessor task to successor center = notification_center() center.add_task_responsible(successor, successor.responsible) # First "accept" the successor task.. accept_task_with_response(successor, response_text) transaction.savepoint() response_text = response_text or '' request_data = { 'text': response_text.encode('utf-8'), 'successor_oguid': successor_tc.get_oguid() } response = dispatch_request(predecessor.admin_unit_id, '@@accept_task_workflow_transition', path=predecessor.physical_path, data=request_data) response_body = response.read() if response_body.strip() != 'OK': raise TaskRemoteRequestError( 'Adding the response and changing the workflow state on the ' 'predecessor task failed.') # Connect the predecessor and the successor task. This needs to be done # that late for preventing a deadlock because of the locked tasks table. successor_tc.set_predecessor(predecessor_oguid) return successor
def set_remote_import_stamp(context): """update the sync stap on every enabled client.""" timestamp = update_sync_stamp(context) # fake the request, because dispatch_request expects it setRequest(context.REQUEST) for admin_unit in ogds_service().all_admin_units(): try: dispatch_request(admin_unit.id(), '@@update_sync_stamp', data={REQUEST_SYNC_KEY: timestamp}) logger.info( "Issued remote request to update sync_stamp on %s to %s" % ( admin_unit.id(), timestamp)) except URLError, e: logger.warn("ERROR while trying to remotely update sync_stamp" "for %s: %s" % (admin_unit.id(), e))
def accept_task_with_successor(dossier, predecessor_oguid, response_text): predecessor = Task.query.by_oguid(predecessor_oguid) # Set the "X-CREATING-SUCCESSOR" flag for preventing the event handler # from creating additional responses per added document. getRequest().set('X-CREATING-SUCCESSOR', True) # Transport the original task (predecessor) to this dossier. The new # response and task change is not yet done and will be done later. This # is necessary for beeing as transaction aware as possible. transporter = Transporter() successor = transporter.transport_from( dossier, predecessor.admin_unit_id, predecessor.physical_path) successor_tc = ISuccessorTaskController(successor) # copy documents and map the intids doc_transporter = getUtility(ITaskDocumentsTransporter) comment = _(u'version_message_accept_task', default=u'Document copied from task (task accepted)') intids_mapping = doc_transporter.copy_documents_from_remote_task( predecessor, successor, comment=comment) # copy the responses response_transporter = IResponseTransporter(successor) response_transporter.get_responses(predecessor.admin_unit_id, predecessor.physical_path, intids_mapping=intids_mapping) # Move current responsible from predecessor task to successor center = notification_center() center.add_task_responsible(successor, successor.responsible) # First "accept" the successor task.. accept_task_with_response(successor, response_text) transaction.savepoint() response_text = response_text or '' request_data = {'text': response_text.encode('utf-8'), 'successor_oguid': successor_tc.get_oguid()} response = dispatch_request(predecessor.admin_unit_id, '@@accept_task_workflow_transition', path=predecessor.physical_path, data=request_data) response_body = response.read() if response_body.strip() != 'OK': raise TaskRemoteRequestError( 'Adding the response and changing the workflow state on the ' 'predecessor task failed.') # Connect the predecessor and the successor task. This needs to be done # that late for preventing a deadlock because of the locked tasks table. successor_tc.set_predecessor(predecessor_oguid) return successor
def set_remote_import_stamp(context): """Update the sync stamp on every enabled admin unit. """ timestamp = update_sync_stamp(context) # fake the request, because dispatch_request expects it setRequest(context.REQUEST) for admin_unit in ogds_service().all_admin_units(): try: dispatch_request(admin_unit.id(), '@@update_sync_stamp', data={REQUEST_SYNC_KEY: timestamp}) logger.info( "Issued remote request to update sync_stamp on %s to %s" % (admin_unit.id(), timestamp)) except URLError, e: logger.warn("ERROR while trying to remotely update sync_stamp" "for %s: %s" % (admin_unit.id(), e))
def close_task(self, task, text): response = dispatch_request( task.admin_unit_id, u'@@close-task-wizard-remote_close', path=task.physical_path, data={'text': text.encode('utf-8') if text else u''}) response_data = response.read().strip() if response_data != 'OK': raise Exception('Could not close task on remote site %s (%s)' % (task.admin_unit_id, task.physical_path))
def execute(self): model = self.submitted_proposal.load_model() response = dispatch_request(model.admin_unit_id, '@@reject-proposal', path=model.physical_path) response_body = response.read() if response_body != 'OK': raise ValueError( 'Unexpected response {!r} when rejecting proposal.'.format( response))
def push_to_remote_client(self, key, admin_unit_id): data = self.get_data(key) req_data = {'data-set': json.dumps(data), 'key': key} response = dispatch_request(admin_unit_id, '@@receive-wizard-data-set', data=req_data) if response.read().strip() != 'OK': raise Exception('Could not push session data to admin_unit %s' % ( admin_unit_id))
def execute(self): model = self.submitted_proposal.load_model() response = dispatch_request( model.admin_unit_id, '@@reject-proposal', path=model.physical_path) response_body = response.read() if response_body != 'OK': raise ValueError( 'Unexpected response {!r} when rejecting proposal.'.format( response_body))
def push_to_remote_client(self, key, admin_unit_id): data = self.get_data(key) req_data = {'data-set': json.dumps(data), 'key': key} response = dispatch_request(admin_unit_id, '@@receive-wizard-data-set', data=req_data) response_body = response.read() if response_body.strip() != 'OK': raise Exception('Could not push session data to admin_unit %s' % ( admin_unit_id))
def close_task(self, task, text): response = dispatch_request( task.admin_unit_id, u'@@close-task-wizard-remote_close', path=task.physical_path, data={'text': text.encode('utf-8') if text else u''}) response_data = response.read().strip() if response_data != 'OK': raise Exception( 'Could not close task on remote site %s (%s)' % ( task.admin_unit_id, task.physical_path))
def remove_scheduled(self, meeting): self.execute_transition('scheduled-submitted') IHistory(self.resolve_submitted_proposal()).append_record( u'remove_scheduled', meeting_id=meeting.meeting_id) request_data = { 'data': advancedjson.dumps({ 'meeting_id': meeting.meeting_id, }) } expect_ok_response( dispatch_request(self.admin_unit_id, '@@receive-proposal-unscheduled', path=self.physical_path, data=request_data), 'Unexpected response {!r} when unscheduling proposal.')
def sync_deadline(self, new_deadline, text, transition): sct = ISuccessorTaskController(self.context) for successor in sct.get_successors(): response = dispatch_request( successor.admin_unit_id, '@@remote_deadline_modifier', successor.physical_path, data={ 'new_deadline': new_deadline.toordinal(), 'text': safe_utf8(text), 'transition': transition}) if response.read().strip() != 'OK': raise Exception( 'Updating deadline on remote client %s. failed (%s)' % ( successor.admin_unit_id, response.read()))
def send_responses(self, target_admin_unit_id, remote_task_url, intids_mapping=None): """Sends all responses of task self.context to task on a remote admin unit. `target_admin_unit_id`: id of a target admin unit `remote_task_url`: url to a task on `target_cid` relative to its site root. `intids_mapping`: replace intids of RelationValues according to this mapping. This fixes the intids on remote admin unit. RelationValues not listed in this mapping will not be sent. """ jsondata = self.extract_responses(intids_mapping) return dispatch_request(target_admin_unit_id, '@@task-responses-receive', path=remote_task_url, data=dict(responses=jsondata))
def get_responses(self, target_admin_unit_id, remote_task_path, intids_mapping): """Retrieves all responses from the task with path `remote_task_path` on the admin_unit `target_admin_unit_id` and adds them to the current context (target task). Provide a an `intids_mapping` (dict), mapping the original intids of related objects to the new intids of the copies on this admin_unit. This is necessary for fixing the relations. """ req_data = {'intids_mapping': json.dumps(intids_mapping)} response = dispatch_request(target_admin_unit_id, '@@task-responses-extract', path=remote_task_path, data=req_data) try: data = json.loads(response.read()) except ValueError: # is a internal request data = response.read() self.create_responses(data)
def reject(self, text): assert self.workflow.can_execute_transition(self, 'submitted-pending') self.submitted_physical_path = None self.submitted_admin_unit_id = None self.submitted_int_id = None self.date_of_submission = None # set workflow state directly for once, the transition is used to # redirect to a form. self.workflow_state = self.STATE_PENDING.name request_data = { 'data': advancedjson.dumps({ 'text': text, }) } expect_ok_response( dispatch_request(self.admin_unit_id, '@@receive-proposal-rejected', path=self.physical_path, data=request_data), 'Unexpected response {!r} when rejecting proposal.')
def schedule(self, meeting): assert self.can_be_scheduled() self.execute_transition('submitted-scheduled') meeting.agenda_items.append(AgendaItem(proposal=self)) meeting.reorder_agenda_items() submitted_proposal = self.resolve_submitted_proposal() ProposalScheduledActivity(submitted_proposal, getRequest(), meeting.meeting_id).record() IHistory(self.resolve_submitted_proposal()).append_record( u'scheduled', meeting_id=meeting.meeting_id) request_data = { 'data': advancedjson.dumps({ 'meeting_id': meeting.meeting_id, }) } expect_ok_response( dispatch_request(self.admin_unit_id, '@@receive-proposal-scheduled', path=self.physical_path, data=request_data), 'Unexpected response {!r} when scheduling proposal.')
def accept_forwarding_with_successor( context, predecessor_oguid, response_text, dossier=None): # the predessecor (the forwarding on the remote client) predecessor = Task.query.by_oguid(predecessor_oguid) # Set the "X-CREATING-SUCCESSOR" flag for preventing the event handler # from creating additional responses per added document. context.REQUEST.set('X-CREATING-SUCCESSOR', True) # transport the remote forwarding to the inbox or actual yearfolder transporter = Transporter() inbox = get_current_inbox(context) if dossier: yearfolder = get_current_yearfolder(inbox=inbox) successor_forwarding = transporter.transport_from( yearfolder, predecessor.admin_unit_id, predecessor.physical_path) else: successor_forwarding = transporter.transport_from( inbox, predecessor.admin_unit_id, predecessor.physical_path) # Replace the issuer with the current inbox successor_forwarding.issuer = get_current_org_unit().inbox().id() successor_tc = ISuccessorTaskController(successor_forwarding) # copy documents and map the intids doc_transporter = getUtility(ITaskDocumentsTransporter) comment = _( u'version_message_accept_forwarding', default=u'Document copied from forwarding (forwarding accepted)') intids_mapping = doc_transporter.copy_documents_from_remote_task( predecessor, successor_forwarding, comment=comment) # copy the responses response_transporter = IResponseTransporter(successor_forwarding) response_transporter.get_responses(predecessor.admin_unit_id, predecessor.physical_path, intids_mapping=intids_mapping) # Remove current responsible from predecessor and add issuer # and responsible to successor's watcher. center = notification_center() center.remove_task_responsible(Oguid.parse(predecessor_oguid), successor_forwarding.responsible) center.add_task_responsible(successor_forwarding, successor_forwarding.responsible) center.add_task_issuer(successor_forwarding, successor_forwarding.issuer) # if a dossier is given means that a successor task must # be created in a new or a existing dossier if dossier: # we need all task field values from the forwarding fielddata = {} for fieldname in ITask.names(): value = ITask.get(fieldname).get(successor_forwarding) fielddata[fieldname] = value # Predefine the task_type to avoid tasks with an invalid task_type fielddata['task_type'] = FORWARDING_SUCCESSOR_TYPE # lets create a new task - the successor task task = createContentInContainer( dossier, 'opengever.task.task', **fielddata) # copy documents and map the intids intids_mapping = _copy_documents_from_forwarding( successor_forwarding, task) # copy the responses response_transporter = IResponseTransporter(task) response_transporter.get_responses( get_current_admin_unit().id(), '/'.join(successor_forwarding.getPhysicalPath()), intids_mapping=intids_mapping) # successor successor_tc_task = ISuccessorTaskController(task) transaction.savepoint() # Close the predessecor forwarding response_text = response_text or '' request_data = {'response_text': response_text.encode('utf-8'), 'successor_oguid': successor_tc.get_oguid(), 'transition': 'forwarding-transition-accept'} response = dispatch_request(predecessor.admin_unit_id, '@@store_forwarding_in_yearfolder', path=predecessor.physical_path, data=request_data) response_body = response.read() if response_body.strip() != 'OK': raise TaskRemoteRequestError( 'Adding the response and changing the workflow state on the ' 'predecessor forwarding failed.') if dossier: # Update watchers for created successor forwarding and task center = notification_center() center.remove_task_responsible(successor_forwarding, task.responsible) center.add_task_responsible(task, task.responsible) # When a successor task exists, we close also the successor forwarding change_task_workflow_state( successor_forwarding, 'forwarding-transition-accept', text=response_text, successor_oguid=successor_tc_task.get_oguid()) # create the succssor relations successor_tc.set_predecessor(predecessor_oguid) if dossier: successor_tc_task.set_predecessor(successor_tc.get_oguid()) return task return successor_forwarding
def deliver_documents_and_complete_task(self, formdata, response): """Delivers the selected documents to the predecesser task and complete the task: - Copy the documents to the predecessor task (no new responses) - Execute workflow transition (no new response) - Add a new response indicating the workflow transition, the added documents and containing the entered response text. """ # add documents to the response response.added_object = PersistentList() predecessor = Task.query.by_oguid(self.context.predecessor) transporter = Transporter() intids = getUtility(IIntIds) data = {'documents': [], 'text': formdata['text'], 'transition': formdata['transition']} related_ids = [] if getattr(self.context, 'relatedItems'): related_ids = [item.to_id for item in self.context.relatedItems] for doc_intid in formdata['documents']: doc = intids.getObject(int(doc_intid)) data['documents'].append(transporter.extract(doc)) # add a releation when a document from the dossier was selected if int(doc_intid) not in related_ids: # check if its a relation if aq_parent(aq_inner(doc)) != self.context: # add relation to doc on task if self.context.relatedItems: self.context.relatedItems.append( RelationValue(int(doc_intid))) else: self.context.relatedItems = [ RelationValue(int(doc_intid))] # add response change entry for this relation if not response.relatedItems: response.relatedItems = [RelationValue(int(doc_intid))] else: response.relatedItems.append( RelationValue(int(doc_intid))) # set relation flag doc._v__is_relation = True response.add_change('relatedItems', _(u'label_related_items', default=u"Related Items"), '', linked(doc, doc.Title())) else: # add entry to the response for this document response.added_object.append(RelationValue(int(doc_intid))) else: # append only the relation on the response doc._v__is_relation = True response.add_change('relatedItems', _(u'label_related_items', default=u"Related Items"), '', linked(doc, doc.Title())) request_data = {'data': json.dumps(data)} response = dispatch_request( predecessor.admin_unit_id, '@@complete_successor_task-receive_delivery', predecessor.physical_path, data=request_data) if response.read().strip() != 'OK': raise Exception('Delivering documents and updating task failed ' 'on remote client %s.' % predecessor.admin_unit_id)
def accept_forwarding_with_successor(context, predecessor_oguid, response_text, dossier=None): # the predessecor (the forwarding on the remote client) predecessor = Task.query.by_oguid(predecessor_oguid) # transport the remote forwarding to the inbox or actual yearfolder transporter = Transporter() inbox = get_current_inbox(context) if dossier: yearfolder = get_current_yearfolder(inbox=inbox) successor_forwarding = transporter.transport_from( yearfolder, predecessor.admin_unit_id, predecessor.physical_path) else: successor_forwarding = transporter.transport_from( inbox, predecessor.admin_unit_id, predecessor.physical_path) # Replace the issuer with the current inbox successor_forwarding.issuer = get_current_org_unit().inbox().id() # Set the "X-CREATING-SUCCESSOR" flag for preventing the event handler # from creating additional responses per added document. successor_forwarding.REQUEST.set('X-CREATING-SUCCESSOR', True) successor_tc = ISuccessorTaskController(successor_forwarding) # copy documents and map the intids doc_transporter = getUtility(ITaskDocumentsTransporter) comment = _( u'version_message_accept_forwarding', default=u'Document copied from forwarding (forwarding accepted)') intids_mapping = doc_transporter.copy_documents_from_remote_task( predecessor, successor_forwarding, comment=comment) # copy the responses response_transporter = IResponseTransporter(successor_forwarding) response_transporter.get_responses(predecessor.admin_unit_id, predecessor.physical_path, intids_mapping=intids_mapping) # Remove current responsible from predecessor and add issuer # and responsible to successor's watcher. center = notification_center() center.remove_task_responsible(Oguid.parse(predecessor_oguid), successor_forwarding.responsible) center.add_task_responsible(successor_forwarding, successor_forwarding.responsible) center.add_task_issuer(successor_forwarding, successor_forwarding.issuer) # if a dossier is given means that a successor task must # be created in a new or a existing dossier if dossier: # we need all task field values from the forwarding fielddata = {} for fieldname in ITask.names(): value = ITask.get(fieldname).get(successor_forwarding) fielddata[fieldname] = value # Predefine the task_type to avoid tasks with an invalid task_type fielddata['task_type'] = FORWARDING_SUCCESSOR_TYPE # lets create a new task - the successor task task = createContentInContainer(dossier, 'opengever.task.task', **fielddata) # copy documents and map the intids intids_mapping = _copy_documents_from_forwarding( successor_forwarding, task) # copy the responses response_transporter = IResponseTransporter(task) response_transporter.get_responses( get_current_admin_unit().id(), '/'.join(successor_forwarding.getPhysicalPath()), intids_mapping=intids_mapping) # successor successor_tc_task = ISuccessorTaskController(task) transaction.savepoint() # Close the predessecor forwarding response_text = response_text or '' request_data = { 'response_text': response_text.encode('utf-8'), 'successor_oguid': successor_tc.get_oguid(), 'transition': 'forwarding-transition-accept' } response = dispatch_request(predecessor.admin_unit_id, '@@store_forwarding_in_yearfolder', path=predecessor.physical_path, data=request_data) response_body = response.read() if response_body.strip() != 'OK': raise TaskRemoteRequestError( 'Adding the response and changing the workflow state on the ' 'predecessor forwarding failed.') if dossier: # Update watchers for created successor forwarding and task center = notification_center() center.remove_task_responsible(successor_forwarding, task.responsible) center.add_task_responsible(task, task.responsible) # When a successor task exists, we close also the successor forwarding change_task_workflow_state( successor_forwarding, 'forwarding-transition-accept', text=response_text, successor_oguid=successor_tc_task.get_oguid()) # create the succssor relations successor_tc.set_predecessor(predecessor_oguid) if dossier: successor_tc_task.set_predecessor(successor_tc.get_oguid()) return task return successor_forwarding
def deliver_documents_and_complete_task(self, formdata, response): """Delivers the selected documents to the predecesser task and complete the task: - Copy the documents to the predecessor task (no new responses) - Execute workflow transition (no new response) - Add a new response indicating the workflow transition, the added documents and containing the entered response text. """ predecessor = Task.query.by_oguid(self.context.predecessor) transporter = Transporter() intids = getUtility(IIntIds) data = { 'documents': [], 'text': formdata['text'], 'transition': formdata['transition'] } related_ids = [] if getattr(self.context, 'relatedItems'): related_ids = [item.to_id for item in self.context.relatedItems] for doc_intid in formdata['documents']: doc = intids.getObject(int(doc_intid)) data['documents'].append(transporter.extract(doc)) # add a releation when a document from the dossier was selected if int(doc_intid) not in related_ids: # check if its a relation if aq_parent(aq_inner(doc)) != self.context: # add relation to doc on task if self.context.relatedItems: self.context.relatedItems.append( RelationValue(int(doc_intid))) else: self.context.relatedItems = [ RelationValue(int(doc_intid)) ] # add response change entry for this relation response.add_related_item(RelationValue(int(doc_intid))) # set relation flag doc._v__is_relation = True response.add_change( 'relatedItems', '', linked(doc, doc.Title()), _(u'label_related_items', default=u"Related Items")) else: # add entry to the response for this document response.added_objects.append(RelationValue( int(doc_intid))) else: # append only the relation on the response doc._v__is_relation = True response.add_change( 'relatedItems', '', linked(doc, doc.Title()), _(u'label_related_items', default=u"Related Items")) request_data = {'data': json.dumps(data)} response = dispatch_request( predecessor.admin_unit_id, '@@complete_successor_task-receive_delivery', predecessor.physical_path, data=request_data) response_body = response.read() if response_body.strip() != 'OK': raise Exception('Delivering documents and updating task failed ' 'on remote client %s.' % predecessor.admin_unit_id)
def _dispatch_request(self, target_admin_unit_id, viewname, path, data): return dispatch_request(target_admin_unit_id, viewname, path, data)