def delete(self, program_id): ''' Delete an existing program. ''' if not program_id: raise errors.MethodError("Program ID required") with model.session_scope() as session: user_session = self.get_user_session(session) program = session.query(model.Program).get(program_id) if not program: raise errors.MissingDocError("No such program") policy = user_session.policy.derive({ 'program': program, 'surveygroups': program.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('program_del') act = Activities(session) if not program.deleted: act.record(user_session.user, program, ['delete']) act.ensure_subscription(user_session.user, program, program, self.reason) program.deleted = True self.finish()
def post(self, query_id): if query_id: raise errors.MethodError("Can't use POST for existing query.") with model.session_scope() as session: user_session = self.get_user_session(session) custom_query = model.CustomQuery() self.update(custom_query, self.request_son) self.update_auto(custom_query, user_session.user) session.add(custom_query) policy = user_session.policy.derive({ 'custom_query': custom_query, }) policy.verify('custom_query_add') session.flush() act = Activities(session) act.record(user_session.user, custom_query, ['create']) act.ensure_subscription(user_session.user, custom_query, custom_query, self.reason) query_id = str(custom_query.id) self.get(query_id)
def delete(self, user_id): if not user_id: raise errors.MethodError("User ID required") with model.session_scope() as session: user_session = self.get_user_session(session) user = session.query(model.AppUser).get(user_id) if not user: raise errors.MissingDocError("No such user") policy = user_session.policy.derive({ 'org': user.organisation, 'user': user, 'surveygroups': user.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('user_del') act = Activities(session) if not user.deleted: act.record(user_session.user, user, ['delete']) act.ensure_subscription( user_session.user, user, user.organisation, self.reason) user.deleted = True self.finish()
def delete(self, organisation_id): if not organisation_id: raise errors.MethodError("Organisation ID required") with model.session_scope() as session: user_session = self.get_user_session(session) org = session.query(model.Organisation).get(organisation_id) if not org: raise errors.MissingDocError("No such organisation") policy = user_session.policy.derive({ 'org': org, 'surveygroups': org.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('org_del') act = Activities(session) if not org.deleted: act.record(user_session.user, org, ['delete']) act.ensure_subscription(user_session.user, org, org, self.reason) org.deleted = True self.finish()
def delete(self, submission_id): if submission_id == '': raise errors.MethodError("Submission ID required") with model.session_scope() as session: user_session = self.get_user_session(session) submission = (session.query(model.Submission).get(submission_id)) if not submission: raise errors.MissingDocError("No such submission") policy = user_session.policy.derive({ 'org': submission.organisation, 'surveygroups': submission.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('submission_del') act = Activities(session) if not submission.deleted: act.record(user_session.user, submission, ['delete']) act.ensure_subscription(user_session.user, submission, submission.organisation, self.reason) submission.deleted = True self.finish()
def put(self, survey_id): '''Update existing.''' if not survey_id: raise errors.MethodError("Survey ID required") program_id = self.get_argument('programId', '') with model.session_scope() as session: user_session = self.get_user_session(session) survey = (session.query(model.Survey).options( joinedload('program')).options( joinedload('program.surveygroups')).get( (survey_id, program_id))) if not survey: raise errors.MissingDocError("No such survey") ## set only last level indexing_form 1 if no value, remove other level indexing_form levels = len(self.request_son.structure.levels) for i, l in enumerate(self.request_son.structure.levels): if i + 1 != levels and l.indexing_from: del l.indexing_from if i + 1 == levels and ( (not l.indexing_from and l.indexing_from != 0) or (not isinstance(l.indexing_from, Number)) or l.indexing_from < 0): l.indexing_from = 1 self._update(survey, self.request_son) policy = user_session.policy.derive({ 'program': survey.program, 'survey': survey, 'surveygroups': survey.program.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('survey_edit') verbs = [] if session.is_modified(survey): verbs.append('update') if survey.deleted: survey.deleted = False verbs.append('undelete') act = Activities(session) act.record(user_session.user, survey, verbs) act.ensure_subscription(user_session.user, survey, survey.program, self.reason) self.get(survey_id)
def post(self, user_id): ''' Create a new user. ''' if user_id: raise errors.MethodError("Can't use POST for existing users.") try: user_input_schema(self.request_son) except voluptuous.error.Invalid as e: raise errors.ModelError.from_voluptuous(e) with model.session_scope() as session: user_session = self.get_user_session(session) org = ( session.query(model.Organisation) .get(self.request_son['organisation']['id'])) if not org: raise errors.ModelError("No such organisation") user = model.AppUser(organisation=org) try: assign_surveygroups(user_session, user, self.request_son) except ValueError as e: raise errors.ModelError(str(e)) policy = user_session.policy.derive({ 'org': user.organisation, 'user': user, 'target': self.request_son, 'surveygroups': user.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('user_add') policy.verify('user_change_role') self.check_password(self.request_son.password) self._update(user, self.request_son, session) session.add(user) # Need to flush so object has an ID to record action against. session.flush() act = Activities(session) act.record(user_session.user, user, ['create']) act.ensure_subscription( user_session.user, user, user.organisation, self.reason) act.subscribe(user, user.organisation) self.reason("New user subscribed to organisation") user_id = user.id self.get(user_id)
def post(self, survey_id): '''Create new.''' if survey_id: raise errors.MethodError("Can't use POST for existing object") program_id = self.get_argument('programId', '') with model.session_scope() as session: user_session = self.get_user_session(session) program = (session.query(model.Program).options( joinedload('surveygroups')).get(program_id)) if not program: raise errors.ModelError("No such program") ## set only last level indexing_form 1 if no value, remove other level indexing_form levels = len(self.request_son.structure.levels) for i, l in enumerate(self.request_son.structure.levels): if i + 1 != levels and l.indexing_from: del l.indexing_from if i + 1 == levels and ( (not l.indexing_from and l.indexing_from != 0) or (not isinstance(l.indexing_from, Number)) or l.indexing_from < 0): l.indexing_from = 1 survey = model.Survey(program=program) self._update(survey, self.request_son) session.add(survey) # Need to flush so object has an ID to record action against. session.flush() policy = user_session.policy.derive({ 'program': program, 'survey': survey, 'surveygroups': program.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('survey_add') act = Activities(session) act.record(user_session.user, survey, ['create']) act.ensure_subscription(user_session.user, survey, survey.program, self.reason) survey_id = str(survey.id) self.get(survey_id)
def put(self, organisation_id): ''' Update an existing organisation. ''' if not organisation_id: raise errors.MethodError( "Can't use PUT for new organisations (no ID).") with model.session_scope() as session: user_session = self.get_user_session(session) org = session.query(model.Organisation).get(organisation_id) if not org: raise errors.MissingDocError("No such organisation") try: groups_changed = assign_surveygroups(user_session, org, self.request_son) except ValueError as e: raise errors.ModelError(str(e)) policy = user_session.policy.derive({ 'org': org, 'surveygroups': org.surveygroups, }) policy.verify('org_edit') # Check that user shares a common surveygroup with this org. # Admins need permission to edit orgs outside their surveygroups # though. if not policy.check('admin'): policy.verify('surveygroup_interact') old_locations = list(org.locations) self._update(org, self.request_son) verbs = [] if (session.is_modified(org) or org.locations != old_locations or groups_changed or session.is_modified(org.meta)): verbs.append('update') if org.deleted: org.deleted = False verbs.append('undelete') act = Activities(session) act.record(user_session.user, org, verbs) act.ensure_subscription(user_session.user, org, org, self.reason) self.get(organisation_id)
def filter(self, request, response): is_valid = self.options.methods_validator.is_valid( request.request_method) allow_methods = self.options.methods_value if not is_valid: response.allow_methods = allow_methods return errors.MethodError(request.request_method) if not allow_methods: allow_methods = [request.request_method] response.allow_methods = allow_methods
def get(self, program_id): ''' Get a list of programs that share the same lineage. ''' if program_id == '': raise errors.MethodError("Program ID is required") with model.session_scope() as session: user_session = self.get_user_session(session) program = session.query(model.Program).get(program_id) if not program: raise errors.MissingDocError("No such program") policy = user_session.policy.derive({ 'program': program, 'surveygroups': program.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('program_view') query = (session.query(model.Program).filter( model.Program.tracking_id == program.tracking_id).order_by( model.Program.created)) if not policy.check('surveygroup_interact_all'): query = filter_surveygroups(session, query, user_session.user.id, [], [model.program_surveygroup]) deleted = self.get_argument('deleted', '') if deleted != '': deleted = truthy(deleted) query = query.filter(model.Program.deleted == deleted) to_son = ToSon( r'/id$', r'/title$', r'/is_editable$', r'/deleted$', # Descend r'/[0-9]+$', ) sons = to_son(query.all()) self.set_header("Content-Type", "application/json") self.write(json_encode(sons)) self.finish()
def delete(self, qnode_id): '''Delete existing.''' if not qnode_id: raise errors.MethodError("Question node ID required") program_id = self.get_argument('programId', '') with model.session_scope() as session: user_session = self.get_user_session(session) qnode = ( session.query(model.QuestionNode) .options(joinedload('program')) .options(joinedload('program.surveygroups')) .get((qnode_id, program_id))) if not qnode: raise errors.MissingDocError("No such question node") log.debug("deleting: %s", qnode) program = qnode.program survey = qnode.survey parent = qnode.parent policy = user_session.policy.derive({ 'program': program, 'survey': survey, 'surveygroups': program.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('qnode_del') act = Activities(session) if not qnode.deleted: act.record(user_session.user, qnode, ['delete']) act.ensure_subscription( user_session.user, qnode, qnode.program, self.reason) qnode.deleted = True calculator = Calculator.structural() if parent: parent.children.reorder() calculator.mark_qnode_dirty(parent) else: survey.qnodes.reorder() calculator.mark_survey_dirty(survey) calculator.execute() self.finish()
def reorder(collection, son, id_attr='id'): ''' Update the order of items in an `ordering_list` according to a serialised list. ''' current = {str(getattr(m, id_attr)): m.seq for m in collection} proposed = {m['id']: m['seq'] for m in son} if current != proposed: raise errors.MethodError( "The proposed changes are not compatible with the " "current sequence: items have been added or removed, or another " "user has changed the order too. Try reloading the list.") order = {m['id']: i for i, m in enumerate(son)} collection.sort(key=lambda m: order[str(getattr(m, id_attr))]) collection.reorder()
def put(self, query_id): if not query_id: raise errors.MethodError("Can't use PUT for new query.") with model.session_scope(version=True) as session: custom_query = session.query(model.CustomQuery).get(query_id) if custom_query is None: raise errors.MissingDocError("No such query") user_session = self.get_user_session(session) policy = user_session.policy.derive({ 'custom_query': custom_query, }) policy.verify('custom_query_edit') self.check_concurrent_write(custom_query) if not self.should_save_new_version(custom_query, user_session.user): custom_query.version_on_update = False self.update(custom_query, self.request_son) verbs = [] if session.is_modified(custom_query): verbs.append('update') self.update_auto(custom_query, user_session.user) else: custom_query.version_on_update = False if custom_query.deleted: custom_query.deleted = False verbs.append('undelete') session.flush() act = Activities(session) act.record(user_session.user, custom_query, verbs) act.ensure_subscription(user_session.user, custom_query, custom_query, self.reason) query_id = str(custom_query.id) self.get(query_id)
def post(self, organisation_id): ''' Create a new organisation. ''' if organisation_id: raise errors.MethodError( "Can't use POST for existing organisation.") with model.session_scope() as session: user_session = self.get_user_session(session) org = model.Organisation() try: assign_surveygroups(user_session, org, self.request_son) except ValueError as e: raise errors.ModelError(str(e)) self._update(org, self.request_son) session.add(org) # Need to flush so object has an ID to record action against. session.flush() policy = user_session.policy.derive({ 'org': org, 'surveygroups': org.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('org_add') act = Activities(session) act.record(user_session.user, org, ['create']) act.ensure_subscription(user_session.user, org, org, self.reason) organisation_id = str(org.id) self.get(organisation_id)
def delete(self, survey_id): if not survey_id: raise errors.MethodError("Survey ID required") program_id = self.get_argument('programId', '') with model.session_scope() as session: user_session = self.get_user_session(session) survey = (session.query(model.Survey).options( joinedload('program')).options( joinedload('program.surveygroups')).get( (survey_id, program_id))) if not survey: raise errors.MissingDocError("No such survey") policy = user_session.policy.derive({ 'program': survey.program, 'survey': survey, 'surveygroups': survey.program.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('survey_del') act = Activities(session) if not survey.deleted: act.record(user_session.user, survey, ['delete']) act.ensure_subscription(user_session.user, survey, survey.program, self.reason) survey.deleted = True self.finish()
def delete(self, query_id): if not query_id: raise errors.MethodError("Query ID required") with model.session_scope(version=False) as session: custom_query = session.query(model.CustomQuery).get(query_id) if custom_query is None: raise errors.MissingDocError("No such query") user_session = self.get_user_session(session) policy = user_session.policy.derive({ 'custom_query': custom_query, }) policy.verify('custom_query_del') act = Activities(session) if not custom_query.deleted: act.record(user_session.user, custom_query, ['delete']) act.ensure_subscription(user_session.user, custom_query, custom_query, self.reason) custom_query.deleted = True self.get(query_id)
def put(self, submission_id): '''Update existing.''' if submission_id == '': raise errors.MethodError("Submission ID required") approval = self.get_argument('approval', '') with model.session_scope() as session: user_session = self.get_user_session(session) submission = session.query(model.Submission).get(submission_id) if not submission: raise errors.MissingDocError("No such submission") policy = user_session.policy.derive({ 'org': submission.organisation, 'approval': approval, 'surveygroups': submission.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('submission_edit') current_level = 0 verbs = [] if approval: self.check_approval_down_one(session, submission, approval) current_level = APPROVAL_STATES.index(submission.approval) if approval != submission.approval: verbs.append('state') submission.approval = approval self._update(submission, self.request_son) if session.is_modified(submission): verbs.append('update') # update measures approval state, when save edited submission no approval, so here should not if approval != '': approval_level = APPROVAL_STATES.index(approval) if 'state' in verbs and approval_level > current_level: approval_level = approval_level - 1 responses = (session.query(model.Response).filter( model.Response.submission_id == submission.id, model.Response.approval == APPROVAL_STATES[approval_level])) for response in responses: # update = updater(response, error_factory=errors.ModelError) # update('title', son) response.approval = approval try: calculator = Calculator.scoring(submission) calculator.mark_measure_dirty( response.qnode_measure) calculator.execute() except ResponseTypeError as e: raise errors.ModelError(str(e)) act = Activities(session) act.record(user_session.user, response, verbs) act.ensure_subscription(user_session.user, response, response.submission, self.reason) if submission.deleted: submission.deleted = False verbs.append('undelete') act = Activities(session) act.record(user_session.user, submission, verbs) act.ensure_subscription(user_session.user, submission, submission.organisation, self.reason) self.get(submission_id)
def post(self, submission_id): '''Create new.''' if submission_id: raise errors.MethodError("Can't use POST for existing object") program_id = self.get_argument('programId', '') if not program_id: raise errors.ModelError("Program ID is required") survey_id = self.get_argument('surveyId', '') if not survey_id: raise errors.ModelError("Survey ID is required") organisation_id = self.get_argument('organisationId', '') if not organisation_id: raise errors.ModelError("Organisation ID is required") # Source submission ID duplicate_id = self.get_argument('duplicateId', '') with model.session_scope() as session: user_session = self.get_user_session(session) org = session.query(model.Organisation).get(organisation_id) if not org: raise errors.ModelError("No such organisation") survey = session.query(model.Survey).get((survey_id, program_id)) if not survey: raise errors.ModelError("No such survey") if duplicate_id: source_submission = (session.query( model.Submission).get(duplicate_id)) if not source_submission: raise errors.MissingDocError( "Source submission (for duplication) not found") if source_submission.organisation != org: raise errors.ModelError( "Can't duplicate a submission across two " "organisations: '%s' and '%s'" % (source_submission.organisation.name, org.name)) else: source_submission = None submission = model.Submission(program=survey.program, survey=survey, organisation=org, approval='draft') self._update(submission, self.request_son) session.add(submission) surveygroups = submission.surveygroups if source_submission: surveygroups &= source_submission.surveygroups policy = user_session.policy.derive({ 'org': org, 'survey': survey, 'surveygroups': surveygroups, }) policy.verify('surveygroup_interact') policy.verify('submission_add') session.flush() submission_id = str(submission.id) if source_submission: yield SubmissionHandler.executor.submit( self.duplicate, submission, source_submission, session) act = Activities(session) act.record(user_session.user, submission, ['create']) act.ensure_subscription(user_session.user, submission, submission.organisation, self.reason) self.get(submission_id)
def put(self, user_id): ''' Update an existing user. ''' if not user_id: raise errors.MethodError("Can't use PUT for new users (no ID).") try: user_input_schema(self.request_son) except voluptuous.error.Invalid as e: raise errors.ModelError.from_voluptuous(e) with model.session_scope() as session: user_session = self.get_user_session(session) user = session.query(model.AppUser).get(user_id) if not user: raise errors.MissingDocError("No such user") try: groups_changed = assign_surveygroups( user_session, user, self.request_son) except ValueError as e: raise errors.ModelError(str(e)) policy = user_session.policy.derive({ 'org': user.organisation, 'user': user, 'target': self.request_son, 'surveygroups': user.surveygroups, }) policy.verify('user_edit') # Check that user shares a common surveygroup with the requesting # user. # Allow admins to edit users outside their surveygroups though. if not policy.check('admin'): policy.verify('surveygroup_interact') if self.request_son.role and self.request_son.role != user.role: policy.verify('user_change_role') if ('deleted' in self.request_son and self.request_son['deleted'] != user.deleted): policy.verify('user_enable') if self.request_son.get('password'): self.check_password(self.request_son.password) verbs = [] oid = self.request_son.organisation.id if oid != str(user.organisation_id): policy.verify('user_change_org') verbs.append('relation') self._update(user, self.request_son, session) act = Activities(session) if session.is_modified(user) or groups_changed: verbs.append('update') if user.deleted: user.deleted = False verbs.append('undelete') session.flush() if len(verbs) > 0: act.record(user_session.user, user, verbs) act.ensure_subscription( user_session.user, user, user.organisation, self.reason) if not act.has_subscription(user, user): act.subscribe(user, user.organisation) self.reason("User subscribed to organisation") self.get(user_id)
def get(self, activity_id): if not activity_id: yield self.query() raise errors.MethodError("GET for single activity is not implemented")
def post(self, qnode_id): '''Create new.''' if qnode_id: raise errors.MethodError("Can't use POST for existing object") program_id = self.get_argument('programId', '') survey_id = self.get_argument('surveyId', '') parent_id = self.get_argument('parentId', '') with model.session_scope() as session: user_session = self.get_user_session(session) program = ( session.query(model.Program) .options(joinedload('surveygroups')) .get(program_id)) if not program: raise errors.ModelError("No such program") qnode = model.QuestionNode(program=program) self._update(session, qnode, self.request_son) log.debug("new: %s", qnode) if survey_id: survey = ( session.query(model.Survey) .get((survey_id, program_id))) if not survey: raise errors.ModelError("No such survey") else: survey = None log.debug("survey: %s", survey) if parent_id: parent = ( session.query(model.QuestionNode) .get((parent_id, program_id))) if not parent: raise errors.ModelError("Parent does not exist") if not survey: survey = parent.survey elif parent.survey != survey: raise errors.ModelError( "Parent does not belong to that survey") else: parent = None qnode.survey = survey if parent: log.debug("Appending to parent") parent.children.append(qnode) parent.children.reorder() log.debug("committing: %s", parent.children) elif survey: log.debug("Appending to survey") survey.qnodes.append(qnode) survey.qnodes.reorder() log.debug("committing: %s", survey.qnodes) else: raise errors.ModelError("Parent or survey ID required") # Need to flush so object has an ID to record action against. session.flush() policy = user_session.policy.derive({ 'program': qnode.program, 'survey': qnode.survey, 'surveygroups': qnode.program.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('qnode_add') calculator = Calculator.structural() calculator.mark_qnode_dirty(qnode) calculator.execute() qnode_id = str(qnode.id) act = Activities(session) act.record(user_session.user, qnode, ['create']) act.ensure_subscription( user_session.user, qnode, qnode.program, self.reason) self.get(qnode_id)
def put(self, program_id): ''' Update an existing program. ''' if program_id == '': raise errors.MethodError("Can't use PUT for new program (no ID).") editable = self.get_argument('editable', '') if editable != '': self._update_state(program_id, truthy(editable)) return with model.session_scope() as session: user_session = self.get_user_session(session) program = session.query(model.Program).get(program_id) if not program: raise errors.MissingDocError("No such program") try: groups_changed = assign_surveygroups(user_session, program, self.request_son) except ValueError as e: raise errors.ModelError(str(e)) policy = user_session.policy.derive({ 'program': program, 'surveygroups': program.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('program_edit') if not program.is_editable: raise errors.MethodError("This program is closed for editing") calculator = Calculator.structural() if self.request_son['has_quality'] != program.has_quality: # Recalculate stats for surveys. This will trigger the # recalculation of the submissions in the recalculation # daemon. for survey in program.surveys: calculator.mark_survey_dirty(survey) self._update(program, self.request_son) calculator.execute() verbs = [] if session.is_modified(program) or groups_changed: verbs.append('update') if program.deleted: program.deleted = False verbs.append('undelete') act = Activities(session) act.record(user_session.user, program, verbs) act.ensure_subscription(user_session.user, program, program, self.reason) self.get(program_id)
def post(self, program_id): ''' Create a new program. ''' if program_id: raise errors.MethodError("Can't use POST for existing program.") duplicate_id = self.get_argument('duplicateId', '') with model.session_scope() as session: user_session = self.get_user_session(session) program = model.Program() self._update(program, self.request_son) session.add(program) try: assign_surveygroups(user_session, program, self.request_son) except ValueError as e: raise errors.ModelError(str(e)) policy = user_session.policy.derive({ 'program': program, 'surveygroups': program.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('program_add') # Need to flush so object has an ID to record action against. session.flush() act = Activities(session) if duplicate_id: source_program = (session.query( model.Program).get(duplicate_id)) if not source_program: raise errors.MissingDocError( "Source program does not exist") policy = user_session.policy.derive({ 'program': source_program, 'surveygroups': source_program.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('program_view') yield self.duplicate_structure(source_program, program, session) source_program.finalised_date = datetime.datetime.utcnow() act.record(user_session.user, source_program, ['state']) act.record(user_session.user, program, ['create']) act.ensure_subscription(user_session.user, program, program, self.reason) program_id = program.id self.get(program_id)