def get_settings(): settings = get_minimal_settings() settings.update({ "cookie_secret": get_cookie_secret(), "xsrf_cookies": truthy(tornado.options.options.xsrf), "debug": truthy(tornado.options.options.dev), "serve_traceback": truthy(tornado.options.options.dev), "gzip": True }) return settings
def query(self): ''' Get a list of programs. ''' term = self.get_argument('term', '') is_editable = truthy(self.get_argument('editable', '')) sons = [] with model.session_scope() as session: user_session = self.get_user_session(session) query = session.query(model.Program) policy = user_session.policy.derive({}) if not policy.check('surveygroup_interact_all'): query = filter_surveygroups(session, query, user_session.user.id, [], [model.program_surveygroup]) # Get all programs for a survey group surveygroup_id = self.get_argument("surveyGroupId", None) if surveygroup_id: query = (query.join( model.SurveyGroup, model.Program.surveygroups).filter( model.SurveyGroup.id == surveygroup_id)) if term != '': query = query.filter( model.Program.title.ilike(r'%{}%'.format(term))) if is_editable: query = query.filter(model.Program.finalised_date == None) deleted = self.get_argument('deleted', '') if deleted != '': deleted = truthy(deleted) query = query.filter(model.Program.deleted == deleted) query = query.order_by(model.Program.created.desc()) query = self.paginate(query) to_son = ToSon(r'/id$', r'/title$', r'</description$', r'/deleted$', r'^/[0-9]+/error$', r'/[0-9]+$') sons = to_son(query.all()) self.set_header("Content-Type", "application/json") self.write(json_encode(sons)) self.finish()
def prepare(self): if (truthy(tornado.options.options.force_https) and 'X-Forwarded-Proto' in self.request.headers and self.request.headers['X-Forwarded-Proto'] != 'https'): # Redirect from HTTP to HTTPS when behind a load balancer on AWS. # http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/x-forwarded-headers.html#x-forwarded-proto self.redirect(re.sub(r'^([^:]+)', 'https', self.request.full_url())) return
def test_truthy(self): self.assertTrue(truthy(True)) self.assertTrue(truthy('True')) self.assertTrue(truthy('true')) self.assertTrue(truthy('yes')) self.assertTrue(truthy(1)) self.assertTrue(truthy('1')) self.assertFalse(truthy('False'))
def uses_old_url(self, session): if truthy(tornado.options.options.dev): return False if not config.get_setting(session, 'app_redirect'): return False base_url = config.get_setting(session, 'app_base_url') default_url = config.get_setting(session, 'app_base_url', True) if base_url == default_url: return False return not self.request.full_url().startswith(base_url)
def query(self, organisation_id): deleted = self.get_argument('deleted', '') 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('submission_browse') query = (session.query(model.Survey).join( model.PurchasedSurvey).filter( model.PurchasedSurvey.organisation_id == organisation_id)) if deleted: deleted = truthy(deleted) if deleted: del_filter = ((model.Survey.deleted == True) | (model.Program.deleted == True)) else: del_filter = ((model.Survey.deleted == False) & (model.Program.deleted == False)) query = query.join(model.Program).filter(del_filter) if not policy.check('surveygroup_interact_all'): query = filter_surveygroups(session, query, user_session.user.id, [model.Program], [model.program_surveygroup]) surveys = query.all() to_son = ToSon( r'/id$', r'/title$', r'/deleted$', r'/n_measures$', r'/program/tracking_id$', # Descend into list r'/[0-9]+$', r'/program$') sons = to_son(surveys) self.set_header("Content-Type", "application/json") self.write(json_encode(sons)) self.finish()
def query(self): sons = [] with model.session_scope() as session: user_session = self.get_user_session(session) policy = user_session.policy.derive({}) policy.verify('org_browse') query = session.query(model.Organisation) # Filter out orgs that don't share a surveygroup with this user. # Admins need access to orgs outside their surveygroups though. if not policy.check('admin'): query = filter_surveygroups(session, query, user_session.user.id, [], [model.organisation_surveygroup]) # Filter down to just organisations in a particular survey group surveygroup_id = self.get_argument("surveyGroupId", None) if surveygroup_id: query = (query.join( model.SurveyGroup, model.Organisation.surveygroups).filter( model.SurveyGroup.id == surveygroup_id)) term = self.get_argument('term', None) if term is not None: query = query.filter( model.Organisation.name.ilike(r'%{}%'.format(term))) deleted = self.get_argument('deleted', '') if deleted != '': deleted = truthy(deleted) query = query.filter(model.Organisation.deleted == deleted) query = query.order_by(model.Organisation.name) query = self.paginate(query) to_son = ToSon( r'^/[0-9]+/id$', r'/name$', r'/deleted$', r'/locations$', r'/locations/0/description$', r'/meta$', r'/meta/asset_types.*$', # Descend into list r'/[0-9]+$') sons = to_son(query.all()) self.set_header("Content-Type", "application/json") self.write(json_encode(sons)) self.finish()
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 eval_list_comprehension(state, expr, emitters, acc): """ This evaluates the list comprehension, and mutates `acc`, which should be a python list. `expr` is the expression on the left hand side of the pipe in a list comprehension (like #[<expr> | ...]). Emitters is a python tuple of `emitters`, which are the things on the right hand side of a list comprehension, seperated by commas (like #[<expr> | <emitter1> , <emitter2>, ... <emitterN> ]. An emitter can either be `filter`, which restricts what goes into the list, or or an actual emitter, which is a pattern combined with an expression that evaluates to a list, and the pattern is pattern matched with each element of the list one at a time. """ emitter = emitters[0] last_emitter = len(emitters) <= 1 if emitter._type == 'filter': result = trampoline(emitter.content, state) if utils.truthy(result): if last_emitter: acc.append(trampoline(expr, state)) else: eval_list_comprehension(state, expr, emitters[1:], acc) elif emitter._type == 'emitter': pattern = emitter.content['pattern'] value = trampoline(emitter.content['expr'], state) if value._type == 'str': items = (Node('str', c) for c in value.content) elif value._type == 'list': items = utils.generate_items(value) elif value._type == 'tuple': items = value.content elif value._type == 'number': items = (Node('number', n) for n in range(1, value.content+1)) else: raise MalangError("Invalid emitter type ({}) in list comprehension".format(value._type), state) for item in items: temp_state = state.newenv(state.env.shallow_copy()) patternmatch(pattern, item, temp_state) if last_emitter: acc.append(trampoline(expr, temp_state)) else: eval_list_comprehension(temp_state, expr, emitters[1:], acc=acc)
def query(self): with model.session_scope() as session: user_session = self.get_user_session(session) policy = user_session.policy.derive({}) policy.verify('custom_query_browse') query = session.query(model.CustomQuery) term = self.get_argument('term', None) if term is not None: query = query.filter( model.CustomQuery.title.ilike(r'%{}%'.format(term))) deleted = self.get_argument('deleted', '') if deleted != '': deleted = truthy(deleted) query = query.filter(model.CustomQuery.deleted == deleted) order_by = self.get_argument('order', 'title') if order_by == 'title': order_field = model.CustomQuery.title elif order_by == 'modified': order_field = model.CustomQuery.modified else: order_field = None if order_field: if self.get_argument('desc', ''): order_field = order_field.desc() query = query.order_by(order_field) query = self.paginate(query) to_son = ToSon( r'^/[0-9]+/id$', r'/deleted$', r'/title$', r'/description$', r'/modified$', # Descend into list r'/[0-9]+$', ) sons = to_son(query.all()) self.set_header("Content-Type", "application/json") self.write(json_encode(sons)) self.finish()
def get(self, entity_id): ''' Get a list of programs that some entity belongs to. For example, a single survey may be present in multiple programs. ''' with model.session_scope() as session: user_session = self.get_user_session(session) query = (session.query(model.Program).join(self.mapper).filter( self.mapper.id == entity_id).order_by(model.Program.created)) policy = user_session.policy.derive({}) 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) programs = [ program for program in query.all() if user_session.policy.derive({ 'program': program, }).check('program_view') ] to_son = ToSon( r'/id$', r'/title$', r'/is_editable$', r'/created$', r'/deleted$', # Descend r'/[0-9]+$', ) sons = to_son(programs) self.set_header("Content-Type", "application/json") self.write(json_encode(sons)) self.finish()
def dev_mode(self): return truthy(tornado.options.options.dev)
def deploy_id(): if truthy(tornado.options.options.dev): return None else: return DEPLOY_ID
def query(self): ''' Get a list of users. ''' sons = [] with model.session_scope() as session: user_session = self.get_user_session(session) organisation_id = self.get_argument("organisationId", None) if organisation_id: org = session.query(model.Organisation).get(organisation_id) else: org = None policy = user_session.policy.derive({ 'org': org, }) policy.verify('user_browse') query = ( session.query(model.AppUser) .join(model.Organisation)) # Filter out users that don't share a surveygroup with the # requesting user. # Allow admins to access users outside their surveygroups though. if not policy.check('admin'): query = filter_surveygroups( session, query, user_session.user.id, [], [model.user_surveygroup]) # Get all users for an organisation if organisation_id: query = query.filter(model.Organisation.id == organisation_id) # Get all users for a survey group surveygroup_id = self.get_argument("surveyGroupId", None) if surveygroup_id: query = ( query.join(model.SurveyGroup, model.AppUser.surveygroups) .filter(model.SurveyGroup.id == surveygroup_id)) term = self.get_argument('term', None) if term is not None: query = query.filter( model.AppUser.name.ilike(r'%{}%'.format(term))) deleted = self.get_argument('deleted', None) if deleted is not None: deleted = truthy(deleted) # Filter deleted users. If organisation_id is not specified, users # inherit their organisation's deleted flag too. if deleted == True and not organisation_id: query = query.filter( (model.AppUser.deleted == True) | (model.Organisation.deleted == True)) elif deleted == False and not organisation_id: query = query.filter( (model.AppUser.deleted == False) & (model.Organisation.deleted == False)) elif deleted == True: query = query.filter(model.AppUser.deleted == True) elif deleted == False: query = query.filter(model.AppUser.deleted == False) query = query.order_by(model.AppUser.name) query = self.paginate(query) to_son = ToSon( r'/id$', r'/name$', r'/deleted$', # Descend into nested objects r'/[0-9]+$', r'/organisation$', # Exclude password from response. Not really necessary because # 1. it's hashed and 2. it's not in the list above. But just to # be safe. r'!password' ) sons = to_son(query.all()) self.set_header("Content-Type", "application/json") self.write(json_encode(sons)) self.finish()
def query(self): '''Get a list.''' program_id = self.get_argument('programId', '') if not program_id: raise errors.ModelError("Program ID required") organisation_id = self.get_argument('organisationId', '') with model.session_scope() as session: user_session = self.get_user_session(session) program = (session.query(model.Program).options( joinedload('surveygroups')).get(program_id)) policy = user_session.policy.derive({ 'program': program, 'surveygroups': program.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('program_view') query = (session.query(model.Survey).filter( model.Survey.program_id == program_id).order_by( model.Survey.title)) deleted = self.get_argument('deleted', '') if deleted != '': deleted = truthy(deleted) query = query.filter(model.Survey.deleted == deleted) surveys = query.all() to_son = ToSon( r'/id$', r'/title$', r'/deleted$', r'/n_measures$', r'^/[0-9]+/error$', # Descend r'/[0-9]+$') sons = to_son(surveys) # Add `purchased` metadata for nominated organisation if organisation_id: org = (session.query(model.Organisation).get(organisation_id)) policy = user_session.policy.derive({ 'org': org, 'surveygroups': org.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('org_view') for son, survey in zip(sons, surveys): son.purchased = survey in org.surveys self.set_header("Content-Type", "application/json") self.write(json_encode(sons)) self.finish()
def maval(expr, state): """ This is the evaluation function. I didn't want to shadow the python builtin function `eval', so I called it `maval' instead. `expr' is the abstract syntax tree that has been created with Python Lex-Yacc. `state.env' is an Env instance, storing the identifier bindings for malang. It will be mutated inside `maval'. `maval' can return: (1) A 'Node' instance, signifying a value, it could for example return Node('number', 3), which could maybe be the result of evaluating the expression Node('plus', (Node('number' 2), Node('number', 1))). (2) A thunk (i.e. a function meant to be called later, delaying some computation). This is used to implement tail call elimination; `maval' can't just naively recurse deeper because python itself has no tail call elimination, and python's call stack would explode. """ state = state.newinfonode(expr) T = expr._type if T in ('number', 'str', 'atom', 'function', 'builtin'): return expr elif T == 'uminus': op = trampoline(expr.content, state) if not op._type == 'number': raise MalangError("Invalid arithmetic expression", state) return Node('number', -op.content, infonode=expr) elif T in ('plus', 'minus', 'divide', 'times', 'modulo', 'pow'): op1, op2 = (trampoline(op, state) for op in expr.content) if op1._type == op2._type: _type = op1._type if _type == 'number': if T in ('modulo', 'divide') and op2.content == 0: raise MalangError("Division or modulo by zero", state) else: return Node('number', arithmetic_funcs[T](op1.content, op2.content), infonode=expr) elif _type == 'str' and T == 'plus': return Node('str', op1.content + op2.content, infonode=expr) elif _type == 'tuple' and T == 'plus': return Node('tuple', op1.content + op2.content, infonode=expr) elif _type == 'list' and T == 'plus': return utils.concat_malang_lists(op1, op2) elif T == 'times' and {op1._type, op2._type} == {'number', 'str'}: return Node('str', op1.content * op2.content) elif T == 'times' and {op1._type, op2._type} == {'number', 'tuple'}: return Node('tuple', op1.content * op2.content) elif T == 'times' and {op1._type, op2._type} == {'number', 'list'}: times, malanglist = (op1, op2) if op1._type == 'number' else (op2, op1) return utils.concat_malang_lists(*(malanglist for _ in range(times.content))) else: raise MalangError("Invalid arithmetic expression", state) elif T in ('eq', 'ne', 'gt', 'lt', 'ge', 'le'): op1, op2 = (trampoline(op, state) for op in expr.content) try: result = cmp_funcs[T](op1, op2) except TypeError: raise MalangError("Cannot compare values of types {} and {} with the {} operator" .format(op1._type, op2._type, T), state) return Node('atom', {True: 'yeah', False: 'nope'}[result], infonode=expr) elif T == 'tuple': return Node('tuple', tuple(trampoline(e, state) for e in expr.content), infonode=expr) elif T == 'cons': head, tail = (trampoline(op, state) for op in expr.content) if tail._type == 'list': return Node('list', (head, tail)) else: raise MalangError("The second operand to the cons operator has to be a list", state) elif T == 'list-literal': elems = tuple(trampoline(e, state) for e in expr.content) return utils.transform_list_literal(elems, expr) elif T == 'list_comprehension': result = [] leftside_expr, emitters = expr.content eval_list_comprehension(state, leftside_expr, emitters, acc=result) return utils.iter_to_malang_list(result) elif T == 'bind': pattern, e = expr.content val = trampoline(e, state) temp_env = state.env.shallow_copy() patternmatch(pattern, val, state.newenv(temp_env)) # copying over the new bindings, after the patternmatch is successful, to avoid # 'partial' pattern matching. If I didn't use `temp_evn` in the above call to `patternmatch` # then evaluating `{Same, Same} := {23, 11}.` in the REPL would leave the REPL env # in an 'inconsistent' state. Using the temporary state means that `Same` will be # unbound after that code has been evaluated, because the patternmatch was unsuccessful, # raising an exception, thus the code below never executes. for name in set(temp_env.bindings) - set(state.env.bindings): state.env.bind(name, temp_env.bindings[name]) return val elif T == 'id': return state.env.get(expr.content, state) elif T == 'module_access': module = trampoline(expr.content[0], state) if module._type != 'module': raise MalangError("You tried to use module access on something that wasn't a module", state) val = trampoline(expr.content[1], state.newenv(module.content).newreadonly(True)) return val elif T == 'composition': f1, f2 = (trampoline(e, state) for e in expr.content) utils.assert_type(f1, ('builtin', 'function'), state) utils.assert_type(f2, ('builtin', 'function'), state) code = Node('program', [ Node('program', [ Node('fncall', (f1, Node('fncall', (f2, Node('id', '@')), infonode=expr)), infonode=expr ) ]) ]) return Node('function', { 'code': code, 'filename': state.filename, 'docstring': None, 'parent_env': state.env }) elif T == 'fncall': func, arg = (trampoline(e, state) for e in expr.content) utils.assert_type(func, ('builtin', 'function'), state) if func._type == 'builtin': return func.content(arg, state) elif func._type == 'function': return thunk(maval, func.content['code'], state.newenv( Env(parent=func.content['parent_env'], bindings={'@': arg}) ).newfilename( func.content['filename'] )) elif T == 'func_def': maybe_docstring = expr.content[0].content[0].content[0] if maybe_docstring._type == "str": docstring = maybe_docstring.content else: docstring = None return Node('function', {'code': expr.content[0], 'filename': expr.content[1], 'docstring': docstring, 'parent_env': state.env}, infonode=expr) elif T == 'program': start = expr.content[:-1] last = expr.content[-1] for e in start: trampoline(e, state) return thunk(maval, last, state) elif T == 'case_of': val = trampoline(expr.content['matched_expr'], state) for arrow in expr.content['arrow_sequence']: temp_state = state.newenv(Env(parent=state.env)) try: patternmatch(arrow['pattern'], val, temp_state) except utils.InvalidMatch: continue return thunk(maval, arrow['expr'], temp_state) raise MalangError("No pattern matched in case_of expression", state) elif T == 'if': test_expr, if_true, if_false = expr.content temp_state = state.newenv(Env(parent=state.env)) if utils.truthy(trampoline(test_expr, temp_state)): return thunk(maval, if_true, temp_state) else: return thunk(maval, if_false, temp_state) elif T == 'catch': try: result = trampoline(expr.content, state) except MalangError as e: return e.args[0] return Node('tuple', (Node('atom', 'no_throw'), result)) elif T == 'throw': value = trampoline(expr.content, state) raise utils.Throw(value) elif T == 'classified': exposed, program = expr.content hidden_state = state.newenv(Env(parent=state.env)) result = trampoline(program, hidden_state) for ident in exposed: val = hidden_state.env.get(ident.content, state) state.env.bind(ident.content, val) return result raise MalangError("Unknown expression {!r}".format(utils.AST_to_str(expr)), state)
def query_by_level(self, level): level = int(level) program_id = self.get_argument('programId', '') survey_id = self.get_argument('surveyId', '') term = self.get_argument('term', '') parent_not = self.get_argument('parent__not', None) deleted = self.get_argument('deleted', '') if deleted != '': deleted = truthy(deleted) else: deleted = None if not survey_id: raise errors.ModelError("Survey ID required") 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))) policy = user_session.policy.derive({ 'program': survey.program, 'survey': survey, 'surveygroups': survey.program.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('qnode_view') # Use Postgres' WITH statement # http://www.postgresql.org/docs/9.1/static/queries-with.html # http://docs.sqlalchemy.org/en/rel_1_0/orm/query.html#sqlalchemy.orm.query.Query.cte # http://stackoverflow.com/a/28084743/320036 # Start by selecting root nodes QN1 = model.QuestionNode start = ( session.query( QN1, literal(0).label('level'), array([QN1.seq]).label('path'), (QN1.seq + 1).concat('.').label('pathstr'), (QN1.deleted).label('any_deleted')) .filter(QN1.parent_id == None, QN1.program_id == program_id, QN1.survey_id == survey_id) .cte(name='root', recursive=True)) # Now iterate down the tree to the desired level QN2 = aliased(model.QuestionNode, name='qnode2') recurse = ( session.query( QN2, (start.c.level + 1).label('level'), start.c.path.concat(QN2.seq).label('path'), start.c.pathstr.concat(QN2.seq + 1).concat('.').label( 'pathstr'), (start.c.any_deleted | QN2.deleted).label( 'any_deleted')) .filter(QN2.parent_id == start.c.id, QN2.program_id == start.c.program_id, QN2.survey_id == start.c.survey_id, start.c.level <= level)) # Combine iterated result with root cte = start.union_all(recurse) # Discard all but the lowest level subquery = ( session.query(cte.c.id, cte.c.pathstr, cte.c.any_deleted) .filter(cte.c.level == level) .order_by(cte.c.path) .subquery()) # Select again to get the actual qnodes query = ( session.query( model.QuestionNode, subquery.c.pathstr, subquery.c.any_deleted) .filter(model.QuestionNode.program_id == program_id) .join(subquery, model.QuestionNode.id == subquery.c.id)) if parent_not == '': query = query.filter( model.QuestionNode.parent_id != None) elif parent_not is not None: query = query.filter( model.QuestionNode.parent_id != parent_not) if term != '': query = query.filter( model.QuestionNode.title.ilike('%{}%'.format(term))) if deleted is not None: query = query.filter(subquery.c.any_deleted == deleted) query = self.paginate(query) to_son = ToSon( # Fields to match from any visited object r'/id$', r'/title$', r'/deleted$', r'/n_measures$' ) if truthy(self.get_argument('desc', False)): to_son.add(r'</description$') if user_session.user.role == 'clerk': to_son.exclude(r'/total_weight$') sons = [] for qnode, path, deleted in query.all(): son = to_son(qnode) son['path'] = path son['anyDeleted'] = deleted sons.append(son) self.set_header("Content-Type", "application/json") self.write(json_encode(sons)) self.finish()
def query(self): '''Get list.''' level = self.get_argument('level', '') if level: self.query_by_level(level) return program_id = self.get_argument('programId', '') survey_id = self.get_argument('surveyId', '') parent_id = self.get_argument('parentId', '') root = self.get_argument('root', None) term = self.get_argument('term', '') parent_not = self.get_argument('parent__not', '') deleted = self.get_argument('deleted', '') if root is not None and parent_id: raise errors.ModelError( "Can't specify parent ID when requesting roots") with model.session_scope() as session: user_session = self.get_user_session(session) if parent_id: parent = ( session.query(model.QuestionNode) .options(joinedload('survey')) .options(joinedload('program')) .options(joinedload('program.surveygroups')) .get((parent_id, program_id))) if not parent: raise errors.MissingDocError("No such parent category") if survey_id and survey_id != str(parent.survey_id): raise errors.ModelError("Category is not in that survey") survey = parent.survey elif survey_id: 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") else: raise errors.ModelError("Survey or parent ID required") policy = user_session.policy.derive({ 'program': survey.program, 'survey': survey, 'surveygroups': survey.program.surveygroups, }) policy.verify('surveygroup_interact') policy.verify('qnode_view') query = ( session.query(model.QuestionNode) .filter(model.QuestionNode.program_id == program_id)) if survey_id: query = query.filter_by(survey_id=survey_id) if parent_id: query = query.filter_by(parent_id=parent_id) elif root is not None: query = query.filter_by(parent_id=None) if term: query = query.filter( model.QuestionNode.title.ilike('%{}%'.format(term))) if parent_not: query = query.filter( model.QuestionNode.parent_id != parent_not) if deleted: deleted = truthy(deleted) query = query.filter(model.QuestionNode.deleted == deleted) query = query.order_by(model.QuestionNode.seq, model.QuestionNode.deleted.desc()) query = self.paginate(query, optional=True) to_son = ToSon( # Fields to match from any visited object r'/ob_type$', r'/id$', r'/title$', r'/group$', r'/seq$', r'/deleted$', r'/n_measures$', r'/total_weight$', r'^/[0-9]+/error$', r'/parent$', r'/survey$', r'/survey/structure.*$', r'/survey/program$', # Descend into nested objects r'/[0-9]+$', ) if truthy(self.get_argument('desc', False)): to_son.add(r'</description$') if user_session.user.role == 'clerk': to_son.exclude(r'/total_weight$') sons = to_son(query.all()) # test use session to keep status #status_session = self.get_secure_cookie('status') #status_ids = status_session.decode('utf8') #status_array = status_ids.split(',') #for son in sons: # if son.id in status_array: # son['hideDetail'] = True ##################################### for son in sons: question=0 ids = [] pIds=[son.id] for pId in pIds: sIds = ( session.query(model.QuestionNode) .filter(model.QuestionNode.parent_id == pId)) if not sIds.first(): ids.append(pId) else: for cid in sIds: pIds.append(cid.id) qnodeMeasures = ( session.query(model.Measure, model.QnodeMeasure) .filter(model.Measure.id == model.QnodeMeasure.measure_id) .filter(model.QnodeMeasure.qnode_id.in_(ids))) for qnodeMeasure in qnodeMeasures: rt = ( session.query(model.ResponseType) .filter(model.ResponseType.id == qnodeMeasure.Measure.response_type_id).first()) seq = 0 for p in rt.parts: if "submeasure_seq" in p and p["submeasure_seq"] > question: seq = p["submeasure_seq"] else: question += 1 question = question + seq son['nQuestion'] = question # if son.id in status_array: # son['hideDetail'] = True self.set_header("Content-Type", "application/json") self.write(json_encode(sons)) self.finish()
def query(self): '''Get a list.''' term = self.get_argument('term', '') program_id = self.get_argument('programId', '') survey_id = self.get_argument('surveyId', '') approval = self.get_argument('approval', '') tracking_id = self.get_argument('trackingId', '') deleted = self.get_argument('deleted', '') organisation_id = self.get_argument('organisationId', '') with model.session_scope() as session: user_session = self.get_user_session(session) if user_session.user.role in {'clerk', 'org_admin'}: if not organisation_id: organisation_id = user_session.org.id if organisation_id: 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('submission_browse') else: policy = user_session.policy.derive({}) policy.verify('submission_browse_any') query = session.query(model.Submission) if term: query = query.filter( model.Submission.title.ilike(r'%{}%'.format(term))) if program_id: query = query.filter(model.Submission.program_id == program_id) if survey_id: query = query.filter(model.Submission.survey_id == survey_id) if approval: approval_set = self.approval_set(approval) log.debug('Approval set: %s', approval_set) query = query.filter( model.Submission.approval.in_(approval_set)) if organisation_id: query = query.filter( model.Submission.organisation_id == organisation_id) if tracking_id: query = query.join(model.Program) query = query.filter(model.Program.tracking_id == tracking_id) if deleted: deleted = truthy(deleted) query = query.filter(model.Submission.deleted == deleted) if not policy.check('surveygroup_interact_all'): query = filter_surveygroups( session, query, user_session.user.id, [model.Organisation, model.Program], [ model.organisation_surveygroup, model.program_surveygroup ]) query = query.order_by(model.Submission.created.desc()) query = self.paginate(query) to_son = ToSon( r'/id$', r'/title$', r'/name$', r'/approval$', r'/created$', r'/deleted$', r'/program/tracking_id$', r'^/[0-9]+/error$', # Descend r'/[0-9]+$', r'/organisation$', r'/survey$', r'/program$') sons = to_son(query.all()) self.set_header("Content-Type", "application/json") self.write(json_encode(sons)) self.finish()
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)