def reorder_page(request): dbsession = DBSession() experiment = dbsession.query(Experiment).filter(Experiment.id == request.matchdict['eid']).first() if experiment: page = dbsession.query(Page).filter(and_(Page.id == request.matchdict['pid'], Page.experiment == experiment)).first() else: page = None if experiment and page: try: params = ReorderSchema().to_python(request.params, State(request=request)) with transaction.manager: visible_idx = 1 for idx, qid in enumerate(params['item']): question = dbsession.query(Question).filter(and_(Question.id == qid, Question.page == page)).first() if question is not None: dbsession.add(question.q_type) question.order = idx if question['frontend', 'visible'] and question['frontend', 'title']: question['frontend', 'question_number'] = visible_idx visible_idx = visible_idx + 1 return {'status': 'ok'} except formencode.Invalid: return {'status': 'error'} else: raise HTTPNotFound()
def save_file(request): """Handles the ``/parts/{pid}/files/id/{fid}/save`` URL, updating the :class:`~wte.models.Asset`'s content. Requires that the user has "view" rights on the :class:`~wte.models.Part`. It will also only update an :class:`~wte.models.Asset` belonging to the current :class:`~wte.models.User`. """ dbsession = DBSession() part = dbsession.query(Part).filter( Part.id == request.matchdict['pid']).first() if part: if part.allow('view', request.current_user): progress = get_user_part_progress(dbsession, request.current_user, part) for user_file in progress.files: if user_file.id == int(request.matchdict['fid']): if 'content' in request.params: with transaction.manager: dbsession.add(user_file) user_file.data = request.params['content'].encode( 'utf-8') user_file.etag = hashlib.sha512( user_file.data).hexdigest() return {'status': 'saved'} else: return {'status': 'no-changes'} raise HTTPNotFound() else: unauthorised_redirect(request) else: raise HTTPNotFound()
def add_transition(request): dbsession = DBSession() experiment = dbsession.query(Experiment).filter(Experiment.id == request.matchdict['eid']).first() if experiment: page = dbsession.query(Page).filter(and_(Page.id == request.matchdict['pid'], Page.experiment == experiment)).first() else: page = None if experiment and page: try: with transaction.manager: dbsession.add(page) if page.next: order = max([t.order for t in page.next]) + 1 else: order = 1 transition = Transition(source=page, order=order) dbsession.add(transition) dbsession.add(experiment) dbsession.add(page) dbsession.add(transition) raise HTTPFound(request.route_url('experiment.page.transition', eid=experiment.id, pid=page.id, _anchor='transition-%i' % transition.id)) except formencode.Invalid: raise HTTPFound(request.route_url('experiment.page.transition', eid=experiment.id, pid=page.id)) else: raise HTTPNotFound()
def edit_question(request): dbsession = DBSession() experiment = dbsession.query(Experiment).filter(Experiment.id == request.matchdict['eid']).first() if experiment: page = dbsession.query(Page).filter(and_(Page.id == request.matchdict['pid'], Page.experiment == experiment)).first() else: page = None if page: question = dbsession.query(Question).filter(and_(Question.id == request.matchdict['qid'], Question.page == page)).first() else: question = None if experiment and page and question: try: params = QuestionEditSchema(question['backend', 'fields']).to_python(request.params, State(request=request, dbsession=dbsession)) with transaction.manager: dbsession.add(question) # Update question parameters for key, value in params.items(): if isinstance(value, list): new_value = [] for sub_value in value: if isinstance(sub_value, dict): if len([v for v in sub_value.values() if v is None]) == 0: new_value.append(sub_value) else: if sub_value is not None: new_value.append(sub_value) value = new_value if key != 'csrf_token': question[key] = value # Update all question numbering dbsession.add(page) visible_idx = 1 for q in page.questions: if q['frontend', 'visible'] and q['frontend', 'title']: q['frontend', 'question_number'] = visible_idx visible_idx = visible_idx + 1 dbsession.add(experiment) dbsession.add(page) dbsession.add(question) data = render_to_response('ess:templates/page/edit_question.kajiki', {'experiment': experiment, 'page': page, 'question': question, 'participant': Participant(id=-1)}, request=request) return {'status': 'ok', 'fragment': data.body.decode('utf-8')} except formencode.Invalid as e: return {'status': 'error', 'errors': e.unpack_errors()} else: raise HTTPNotFound()
def action(request): """Handles the ``/users/action`` URL, applying the given action to the list of selected users. Requires that the current :class:`~wte.models.User` has the "admin.users.view" :class:`~wte.models.Permission`. """ dbsession = DBSession() try: query_params = [] for param in ['q', 'status', 'start']: if param in request.params and request.params[param]: query_params.append((param, request.params[param])) params = ActionSchema().to_python(request.params, State(request=request)) if params['action'] != 'delete' or params['confirm']: with transaction.manager: for user in dbsession.query(User).filter( User.id.in_(params['user_id'])): if params['action'] == 'validate': if user.status == 'unconfirmed' and user.allow( 'edit', request.current_user): user.status = 'active' elif params['action'] == 'delete': if user.allow('delete', request.current_user): dbsession.delete(user) elif params['action'] == 'password': if user.status == 'active' and user.allow( 'edit', request.current_user): token = TimeToken( user.id, 'reset_password', datetime.now() + timedelta(seconds=1200)) dbsession.add(token) dbsession.flush() if 'user.password_reset' in active_callbacks: active_callbacks['user.password_reset']( request, user, token) raise HTTPSeeOther(request.route_url('users', _query=query_params)) else: return { 'params': params, 'users': dbsession.query(User).filter(User.id.in_(params['user_id'])), 'query_params': query_params, 'crumbs': create_user_crumbs(request, [{ 'title': 'Confirm', 'url': request.current_route_url() }]) } except Invalid as e: print(e) request.session.flash( 'Please select the action you wish to apply and the users to apply it to', queue='error') raise HTTPSeeOther(request.route_url('users', _query=query_params))
def import_experiment(request): if request.method == 'POST': try: params = ImportExperimentSchema().to_python( request.params, State(request=request)) dbsession = DBSession() with transaction.manager: experiment, errors = ExperimentIOSchema(include_schemas=[s for s in all_io_schemas if s != ExperimentIOSchema]).\ loads(params['source'].file.read().decode('utf-8')) if errors: raise formencode.Invalid( '. '.join([ '%s: %s' % (e['source']['pointer'], e['detail']) for e in errors['errors'] ]), None, None) for page in experiment.pages: replace_questions(page, dbsession) experiment.owner = request.current_user experiment.external_id = uuid.uuid1().hex, experiment.status = 'develop' dbsession.add(experiment) dbsession.flush() for latin_square in experiment.latin_squares: fix_latinsquare(latin_square, experiment.data_sets) for page in experiment.pages: for transition in page.next: fix_transition(transition, experiment.pages) dbsession.add(experiment) raise HTTPFound( request.route_url('experiment.view', eid=experiment.id)) except formencode.Invalid as e: return { 'crumbs': [{ 'title': 'Experiments', 'url': request.route_url('dashboard') }, { 'title': 'Import', 'url': request.route_url('experiment.import') }], 'errors': { 'source': str(e) }, 'values': request.params } return { 'crumbs': [{ 'title': 'Experiments', 'url': request.route_url('dashboard') }, { 'title': 'Import', 'url': request.route_url('experiment.import') }] }
def delete_part_task(request): """Handles the ``parts/{pid}/timed-tasks/{tid}/delete`` URL, providing the UI and backend for deleting an existing :class:`~wte.models.TimedTask` that belongs to a :class:`~wte.models.Part`. Requires that the user has "edit" rights on the :class:`~wte.models.Part`. """ dbsession = DBSession() part = dbsession.query(Part).filter( Part.id == request.matchdict['pid']).first() task = dbsession.query(TimedTask).filter( TimedTask.id == request.matchdict['tid']).first() if part and task: if part.allow('edit', request.current_user): crumbs = create_part_crumbs( request, part, [{ 'title': 'Timed Actions', 'url': request.route_url('part.timed_task', pid=part.id) }, { 'title': 'Delete', 'url': request.current_route_url }]) if request.method == 'POST': try: CSRFSchema().to_python(request.params, State(request=request)) dbsession = DBSession() with transaction.manager: dbsession.delete(task) dbsession.add(part) raise HTTPSeeOther( request.route_url('part.timed_task', pid=part.id)) except formencode.Invalid as e: return { 'errors': e.error_dict, 'part': part, 'task': task, 'crumbs': crumbs, 'include_footer': True, 'help': ['user', 'teacher', 'timed_actions.html'] } return { 'part': part, 'task': task, 'crumbs': crumbs, 'include_footer': True, 'help': ['user', 'teacher', 'timed_actions.html'] } else: unauthorised_redirect(request) else: raise HTTPNotFound()
class DBTester(object): """The :class:`~ess_test.conftest.DBTester` provides functionality for interacting with the database.. """ def __init__(self): self._dbsession = DBSession() def get_model(self, name, query_params = None): """Retrieve a single instance of the model ``name``, filtered by the ``query``. :param name: The name of the model to get the instance for :type name: ``unicode`` :param query: The query to use for selecting the instance as a tuple of ``(field, comparator, value)`` :type query: ``tuple`` :return: The result of the query """ cls = getattr(models, name) query = self._dbsession.query(cls) if query_params: if query_params[1] == '==': query = query.filter(getattr(cls, query_params[0]) == query_params[2]) return query.first() def create_model(self, name, params): """Create a new instance of the model ``name`` with the given ``params``. :param name: The name of the model to create the instance of :type name: ``unicode`` :param params: The initial parameters to use for creating the instance :type params: ``dict`` :return: The new instance """ cls = getattr(models, name) loaded = list(self._dbsession.identity_map.values()) with transaction.manager: model = cls(**params) self._dbsession.add(model) self._dbsession.add(model) self._dbsession.add_all(loaded) return model def update(self, obj, **kwargs): """Update the given ``obj`` with the key/value parameters. :param obj: The obj to update :type obj: :class:`pywebtools.sqlalchemy.Base` """ loaded = list(self._dbsession.identity_map.values()) with transaction.manager: self._dbsession.add(obj) for key, value in kwargs.items(): if isinstance(value, Base): self._dbsession.add(value) setattr(obj, key, value) self._dbsession.add_all(loaded) def flush(self): """Flush the session.""" self._dbsession.flush()
def initialise_database(args): settings = get_appsettings(args.configuration) setup_logging(args.configuration) engine = engine_from_config(settings, 'sqlalchemy.') DBSession.configure(bind=engine) if args.drop_existing: Base.metadata.drop_all(engine) Base.metadata.create_all(engine) dbsession = DBSession() with transaction.manager: user = User(email='*****@*****.**', display_name='Admin', status='active') user.new_password('password') group = PermissionGroup(title='Site administrator') group.permissions.append( Permission(name='admin.users.view', title='View all users')) group.permissions.append( Permission(name='admin.users.edit', title='Edit all users')) group.permissions.append( Permission(name='admin.users.delete', title='Delete all users')) group.permissions.append( Permission(name='admin.users.permissions', title='Edit all user\'s permissions')) user.permission_groups.append(group) group = PermissionGroup(title='Developer') group.permissions.append( Permission(name='experiment.create', title='Create new experiments')) user.permission_groups.append(group) dbsession.add(user) group = PermissionGroup(title='Content administrator') group.permissions.append( Permission(name='experiment.view', title='View all experiments')) group.permissions.append( Permission(name='experiment.edit', title='Edit all experiments')) group.permissions.append( Permission(name='experiment.delete', title='Delete all experiments')) dbsession.add(group) question_types = load(QuestionTypeIOSchema(many=True), json.loads(resource_string('ess', 'scripts/templates/default_question_types.json').\ decode('utf-8'))) dbsession.add_all(question_types) alembic_config = config.Config(args.configuration, ini_section='app:main') alembic_config.set_section_option('app:main', 'script_location', 'ess:migrations') command.stamp(alembic_config, "head")
def delete(request): """Handles the ``/parts/{pid}/assets/{aid}/delete`` URL, providing the UI and backend for deleting :class:`~wte.models.Asset`. Requires that the user has "edit" rights on the current :class:`~wte.models.Part`. """ dbsession = DBSession() part = dbsession.query(Part).filter( Part.id == request.matchdict['pid']).first() asset = dbsession.query(Asset).join(Part.all_assets).\ filter(and_(Asset.id == request.matchdict['aid'], Part.id == request.matchdict['pid'])).first() if part and asset: if part.allow('edit', request.current_user): crumbs = create_part_crumbs(request, part, { 'title': 'Delete Asset', 'url': request.current_route_url() }) if request.method == 'POST': try: CSRFSchema().to_python(request.params, State(request=request)) dbsession = DBSession() with transaction.manager: dbsession.add(asset) asset.parts = [] dbsession.delete(asset) dbsession.add(part) raise HTTPSeeOther( request.route_url('part.view', pid=part.id)) except formencode.Invalid as e: return { 'errors': e.error_dict, 'part': part, 'asset': asset, 'crumbs': crumbs, 'help': ['user', 'teacher', 'asset', 'delete.html'] } return { 'part': part, 'asset': asset, 'crumbs': crumbs, 'help': ['user', 'teacher', 'asset', 'delete.html'] } else: unauthorised_redirect(request) else: raise HTTPNotFound()
def run_change_status(task_id): """Run the status change task for the :class:`~wte.models.TimedTask` with the id ``task_id``. It changes the :class:`~wte.models.TimedTask`\ s status to the value of the "target_status" key in the :attr:`~wte.models.TimedTask.options`. """ dbsession = DBSession() task = dbsession.query(TimedTask).filter(TimedTask.id == task_id).first() if task: part = dbsession.query(Part).filter(Part.id == task.part_id).first() if part and 'target_status' in task.options: with transaction.manager: dbsession.add(part) dbsession.add(task) part.status = task.options['target_status'] with transaction.manager: dbsession.add(task) task.status = 'completed'
def create(request): if request.method == 'POST': try: params = CreateExperimentSchema().to_python( request.params, State(request=request)) dbsession = DBSession() with transaction.manager: experiment = Experiment(title=params['title'], summary='', styles='', scripts='', status='develop', language='en', external_id=uuid.uuid1().hex, owned_by=request.current_user.id, public=False) dbsession.add(experiment) dbsession.add(experiment) raise HTTPFound( request.route_url('experiment.view', eid=experiment.id)) except formencode.Invalid as e: return { 'crumbs': [{ 'title': 'Experiments', 'url': request.route_url('dashboard') }, { 'title': 'Create', 'url': request.route_url('experiment.create') }], 'errors': e.error_dict, 'values': request.params } return { 'crumbs': [{ 'title': 'Experiments', 'url': request.route_url('dashboard') }, { 'title': 'Create', 'url': request.route_url('experiment.create') }] }
def set_answers(request): """Handles the "/parts/{pid}/quiz/set_answers" URL and stores the answers to a :class:`~wte.text_formatter.docutils_ext.QuizQuestion`. If no previous :class:`~wte.models.QuizAnswer` exists, creates one and sets the initial answers / correctness to the data supplied. If one exists, then sets the final answer / correctness and updates the attempts count. """ try: dbsession = DBSession() part = dbsession.query(Part).filter( Part.id == request.matchdict['pid']).first() if part and part.has_role('student', request.current_user): params = SetAnswersSchema().to_python(request.params, State(request=request)) with transaction.manager: quiz = dbsession.query(Quiz).filter( and_(Quiz.part_id == request.matchdict['pid'], Quiz.name == params['quiz'])).first() if quiz: answer = dbsession.query(QuizAnswer).filter( and_(QuizAnswer.user_id == request.current_user.id, QuizAnswer.quiz_id == quiz.id, QuizAnswer.question == params['question'])).first() if answer: if not answer.initial_correct and not answer.final_correct: answer.attempts = answer.attempts + 1 answer.final_answer = json.dumps(params['answer']) answer.final_correct = params['correct'] else: dbsession.add( QuizAnswer(user_id=request.current_user.id, quiz=quiz, question=params['question'], initial_answer=json.dumps( params['answer']), initial_correct=params['correct'], final_answer=None, final_correct=None, attempts=1)) return {} except Invalid as e: return {'errors': e.error_dict}
def add_question(request): dbsession = DBSession() experiment = dbsession.query(Experiment).filter(Experiment.id == request.matchdict['eid']).first() if experiment: page = dbsession.query(Page).filter(and_(Page.id == request.matchdict['pid'], Page.experiment == experiment)).first() else: page = None qtype = dbsession.query(QuestionType).filter(QuestionType.id == request.matchdict['qtid']).first() if experiment and page and qtype: try: params = AddQuestionSchema().to_python(request.params, State(request=request)) with transaction.manager: dbsession.add(page) if params['order'] is None: params['order'] = len(page.questions) question = Question(q_type=qtype, page=page, order=params['order']) if question['frontend', 'visible']: visible_idx = [q['frontend', 'question_number'] for q in page.questions if q['frontend', 'visible'] and q['frontend', 'title'] and q['frontend', 'question_number']] if visible_idx: visible_idx = max(visible_idx) + 1 else: visible_idx = 1 question['frontend', 'question_number'] = visible_idx dbsession.add(question) dbsession.add(experiment) dbsession.add(page) dbsession.add(question) raise HTTPFound(request.route_url('experiment.page.edit', eid=experiment.id, pid=page.id, _anchor='question-%i' % question.id)) except formencode.Invalid: raise HTTPFound(request.route_url('experiment.page.edit', eid=experiment.id, pid=page.id)) else: raise HTTPNotFound()
def data(request): dbsession = DBSession() experiment = dbsession.query(Experiment).filter(Experiment.id == request.matchdict['eid']).first() page = dbsession.query(Page).filter(and_(Page.id == request.matchdict['pid'], Page.experiment_id == request.matchdict['eid'])).first() if experiment and page: data_sets = dbsession.query(DataSet).filter(DataSet.experiment_id == experiment.id) if request.method == 'POST': try: schema = DataAttachmentSchema() mode = '' if 'data_set' in request.params and \ request.params['data_set'] in [str(ds.id) for ds in data_sets if ds.type == 'dataset']: schema.add_field('data_items', formencode.validators.Int(not_empty=True)) mode = 'dataset' params = schema.to_python(request.params, State(request=request, dbsession=dbsession, experiment=experiment)) with transaction.manager: dbsession.add(page) page.dataset_id = params['data_set'] if mode == 'dataset': page['data'] = {'type': 'dataset', 'item_count': params['data_items']} dbsession.add(experiment) dbsession.add(page) raise HTTPFound(request.route_url('experiment.page.data', eid=experiment.id, pid=page.id)) except formencode.Invalid as e: return {'experiment': experiment, 'page': page, 'data_sets': data_sets, 'crumbs': [{'title': 'Experiments', 'url': request.route_url('dashboard')}, {'title': experiment.title, 'url': request.route_url('experiment.view', eid=experiment.id)}, {'title': 'Pages', 'url': request.route_url('experiment.page', eid=experiment.id)}, {'title': '%s (%s)' % (page.title, page.name) if page.title else 'No title (%s)' % page.name, 'url': request.route_url('experiment.page.edit', eid=experiment.id, pid=page.id)}, {'title': 'Data', 'url': request.route_url('experiment.page.data', eid=experiment.id, pid=page.id)}], 'errors': e.error_dict, 'values': request.params} return {'experiment': experiment, 'page': page, 'data_sets': data_sets, 'crumbs': [{'title': 'Experiments', 'url': request.route_url('dashboard')}, {'title': experiment.title, 'url': request.route_url('experiment.view', eid=experiment.id)}, {'title': 'Pages', 'url': request.route_url('experiment.page', eid=experiment.id)}, {'title': '%s (%s)' % (page.title, page.name) if page.title else 'No title (%s)' % page.name, 'url': request.route_url('experiment.page.edit', eid=experiment.id, pid=page.id)}, {'title': 'Data', 'url': request.route_url('experiment.page.data', eid=experiment.id, pid=page.id)}]} else: raise HTTPNotFound()
def import_page(request): dbsession = DBSession() experiment = dbsession.query(Experiment).filter(Experiment.id == request.matchdict['eid']).first() if experiment: if request.method == 'POST': try: params = ImportPageSchema().to_python(request.params, State(request=request, dbsession=dbsession)) with transaction.manager: dbsession.add(experiment) page, errors = PageIOSchema(include_data=('questions', 'questions.q_type', 'questions.q_type.parent', 'questions.q_type.q_type_group', 'questions.q_type.q_type_group.parent', 'questions.q_type.parent.q_type_group', 'questions.q_type.parent.q_type_group.parent')).\ loads(params['source'].file.read().decode('utf-8')) if errors: raise formencode.Invalid('. '.join(['%s: %s' % (e['source']['pointer'], e['detail']) for e in errors['errors']]), None, None) dbsession.add(page) replace_questions(page, dbsession) page.experiment = experiment dbsession.add(experiment) dbsession.add(page) raise HTTPFound(request.route_url('experiment.page.edit', eid=experiment.id, pid=page.id)) except formencode.Invalid as e: return {'experiment': experiment, 'crumbs': [{'title': 'Experiments', 'url': request.route_url('dashboard')}, {'title': experiment.title, 'url': request.route_url('experiment.view', eid=experiment.id)}, {'title': 'Pages', 'url': request.route_url('experiment.page', eid=experiment.id)}, {'title': 'Import a Page', 'url': request.route_url('experiment.page.import', eid=experiment.id)}], 'errors': {'source': str(e)}, 'values': request.params} return {'experiment': experiment, 'crumbs': [{'title': 'Experiments', 'url': request.route_url('dashboard')}, {'title': experiment.title, 'url': request.route_url('experiment.view', eid=experiment.id)}, {'title': 'Pages', 'url': request.route_url('experiment.page', eid=experiment.id)}, {'title': 'Import a Page', 'url': request.route_url('experiment.page.import', eid=experiment.id)}]} else: raise HTTPNotFound()
def data_item_delete(request): dbsession = DBSession() experiment = dbsession.query(Experiment).filter( Experiment.id == request.matchdict['eid']).first() data_set = dbsession.query(DataSet).filter( and_(DataSet.id == request.matchdict['did'], DataSet.experiment_id == request.matchdict['eid'], DataSet.type == 'dataset')).first() data_item = dbsession.query(DataItem).filter( and_(DataItem.id == request.matchdict['diid'], DataItem.dataset_id == request.matchdict['did'])).first() if experiment and data_set and data_item: with transaction.manager: dbsession.delete(data_item) dbsession.add(experiment) dbsession.add(data_set) raise HTTPFound( request.route_url('experiment.data.view', eid=experiment.id, did=data_set.id)) else: raise HTTPNotFound()
def settings(request): dbsession = DBSession() experiment = dbsession.query(Experiment).filter(Experiment.id == request.matchdict['eid']).first() page = dbsession.query(Page).filter(and_(Page.id == request.matchdict['pid'], Page.experiment_id == request.matchdict['eid'])).first() if experiment and page: if request.method == 'POST': try: params = PageSettingsSchema().to_python(request.params, State(request=request, dbsession=dbsession, experiment=experiment, page_id=request.matchdict['pid'])) with transaction.manager: dbsession.add(page) page.name = params['name'] page.title = params['title'] page.styles = params['styles'] page.scripts = params['scripts'] page['number_questions'] = params['number_questions'] dbsession.add(page) dbsession.add(experiment) raise HTTPFound(request.route_url('experiment.page.settings', eid=experiment.id, pid=page.id)) except formencode.Invalid as e: return {'experiment': experiment, 'page': page, 'crumbs': [{'title': 'Experiments', 'url': request.route_url('dashboard')}, {'title': experiment.title, 'url': request.route_url('experiment.view', eid=experiment.id)}, {'title': 'Pages', 'url': request.route_url('experiment.page', eid=experiment.id)}, {'title': '%s (%s)' % (page.title, page.name) if page.title else 'No title (%s)' % page.name, 'url': request.route_url('experiment.page.edit', eid=experiment.id, pid=page.id)}, {'title': 'Settings', 'url': request.route_url('experiment.page.settings', eid=experiment.id, pid=page.id)}], 'values': request.params, 'errors': e.error_dict} return {'experiment': experiment, 'page': page, 'crumbs': [{'title': 'Experiments', 'url': request.route_url('dashboard')}, {'title': experiment.title, 'url': request.route_url('experiment.view', eid=experiment.id)}, {'title': 'Pages', 'url': request.route_url('experiment.page', eid=experiment.id)}, {'title': '%s (%s)' % (page.title, page.name) if page.title else 'No title (%s)' % page.name, 'url': request.route_url('experiment.page.edit', eid=experiment.id, pid=page.id)}, {'title': 'Settings', 'url': request.route_url('experiment.page.settings', eid=experiment.id, pid=page.id)}]} else: raise HTTPNotFound()
def initialise_database(args): """Initialises the database schema and adds the default :class:`~wte.models.Permission`, :class:`~wte.models.PermissionGroup`, and :class:`~wte.models.User` to the database. """ settings = get_appsettings(args.configuration) setup_logging(args.configuration) engine = engine_from_config(settings, 'sqlalchemy.') if args.drop_existing: Base.metadata.drop_all(engine) Base.metadata.create_all(engine) DBSession.configure(bind=engine) dbsession = DBSession() with transaction.manager: admin_user = User(email='*****@*****.**', display_name='Admin', password='******') dbsession.add(admin_user) admin_permission = Permission(name='admin', title='Administration Access') group = init_auth_permissions(dbsession) group.permissions.append(admin_permission) admin_user.permission_groups.append(group) group = PermissionGroup(title='Content Administration') dbsession.add(group) group.permissions.append(admin_permission) create_module_perm = Permission(name='modules.create', title='Create a new module') group.permissions.append(create_module_perm) group.permissions.append( Permission(name='admin.modules.view', title='View all modules')) group.permissions.append( Permission(name='admin.modules.edit', title='Edit all modules')) group.permissions.append( Permission(name='admin.modules.delete', title='Delete all modules')) admin_user.permission_groups.append(group) group = PermissionGroup(title='Teacher') dbsession.add(group) group.permissions.append(create_module_perm) group = PermissionGroup(title='Student') dbsession.add(group)
def new_part_task(request): """Handles the ``parts/{pid}/timed-tasks/new`` URL, providing the UI and backend for creating a new :class:`~wte.models.TimedTask`\ s for a given :class:`~wte.models.Part`. Requires that the user has "edit" rights on the :class:`~wte.models.Part`. """ dbsession = DBSession() part = dbsession.query(Part).filter( Part.id == request.matchdict['pid']).first() if part: if part.allow('edit', request.current_user): crumbs = create_part_crumbs(request, part, { 'title': 'Timed Actions', 'url': request.current_route_url() }) available_tasks = [('change_status', 'Change Status')] if request.method == 'POST': try: params = NewTimedTaskSchema().to_python( request.params, State(request=request)) with transaction.manager: title = 'Unknown Task' if params['name'] == 'change_status': title = 'Change Status' new_task = TimedTask(name=params['name'], part_id=part.id, title=title, status='new') dbsession.add(new_task) dbsession.add(part) dbsession.add(new_task) raise HTTPSeeOther( request.route_url('part.timed_task.edit', pid=part.id, tid=new_task.id)) except formencode.Invalid as e: return { 'errors': e.error_dict, 'values': request.params, 'part': part, 'crumbs': crumbs, 'available_tasks': available_tasks, 'include_footer': True, 'help': ['user', 'teacher', 'timed_actions.html'] } return { 'part': part, 'crumbs': crumbs, 'available_tasks': available_tasks, 'include_footer': True, 'help': ['user', 'teacher', 'timed_actions.html'] } else: unauthorised_redirect(request) else: raise HTTPNotFound()
def delete_transition(request): dbsession = DBSession() experiment = dbsession.query(Experiment).filter(Experiment.id == request.matchdict['eid']).first() if experiment: page = dbsession.query(Page).filter(and_(Page.id == request.matchdict['pid'], Page.experiment == experiment)).first() else: page = None if page: transition = dbsession.query(Transition).filter(and_(Transition.id == request.matchdict['tid'], Transition.source == page)).first() else: transition = None if experiment and page and transition: try: with transaction.manager: dbsession.delete(transition) dbsession.add(experiment) dbsession.add(page) raise HTTPFound(request.route_url('experiment.page.transition', eid=experiment.id, pid=page.id)) except formencode.Invalid: raise HTTPFound(request.route_url('experiment.page.transition', eid=experiment.id, pid=page.id)) else: raise HTTPNotFound()
def data_delete(request): mode = 'latinsquare' if 'latinsquare' in request.matched_route.name else 'dataset' dbsession = DBSession() experiment = dbsession.query(Experiment).filter( Experiment.id == request.matchdict['eid']).first() data_set = dbsession.query(DataSet).filter( and_(DataSet.id == request.matchdict['did'], DataSet.experiment_id == request.matchdict['eid'], DataSet.type == mode)).first() if experiment and data_set: try: CSRFSchema().to_python(request.params, State(request=request)) with transaction.manager: dbsession.delete(data_set) dbsession.add(experiment) raise HTTPFound( request.route_url('experiment.%s' % mode, eid=experiment.id)) except formencode.Invalid: raise HTTPFound( request.route_url('experiment.%s.view' % mode, eid=experiment.id, did=data_set.id)) else: raise HTTPNotFound()
def create(request): dbsession = DBSession() experiment = dbsession.query(Experiment).filter(Experiment.id == request.matchdict['eid']).first() if experiment: if request.method == 'POST': try: params = CreatePageSchema().to_python(request.params, State(request=request, dbsession=dbsession, experiment=experiment)) with transaction.manager: page = Page(experiment=experiment, name=params['name'], title=params['title'], styles='', scripts='') dbsession.add(page) dbsession.add(experiment) if experiment.start is None: experiment.start = page dbsession.add(experiment) dbsession.add(page) raise HTTPFound(request.route_url('experiment.page.edit', eid=experiment.id, pid=page.id)) except formencode.Invalid as e: return {'experiment': experiment, 'crumbs': [{'title': 'Experiments', 'url': request.route_url('dashboard')}, {'title': experiment.title, 'url': request.route_url('experiment.view', eid=experiment.id)}, {'title': 'Pages', 'url': request.route_url('experiment.page', eid=experiment.id)}, {'title': 'Add a Page', 'url': request.route_url('experiment.page.create', eid=experiment.id)}], 'errors': e.error_dict, 'values': request.params} return {'experiment': experiment, 'crumbs': [{'title': 'Experiments', 'url': request.route_url('dashboard')}, {'title': experiment.title, 'url': request.route_url('experiment.view', eid=experiment.id)}, {'title': 'Pages', 'url': request.route_url('experiment.page', eid=experiment.id)}, {'title': 'Add a Page', 'url': request.route_url('experiment.page.create', eid=experiment.id)}]} else: raise HTTPNotFound()
def delete(request): dbsession = DBSession() experiment = dbsession.query(Experiment).filter(Experiment.id == request.matchdict['eid']).first() page = dbsession.query(Page).filter(and_(Page.id == request.matchdict['pid'], Page.experiment_id == request.matchdict['eid'])).first() if experiment and page: if request.method == 'POST': try: DeleteSchema().to_python(request.params, State(request=request, dbsession=dbsession)) with transaction.manager: dbsession.add(experiment) dbsession.add(page) if experiment.start == page: experiment.start = None dbsession.delete(page) dbsession.add(experiment) raise HTTPFound(request.route_url('experiment.page', eid=experiment.id)) except formencode.Invalid as e: return {'experiment': experiment, 'page': page, 'crumbs': [{'title': 'Experiments', 'url': request.route_url('dashboard')}, {'title': experiment.title, 'url': request.route_url('experiment.view', eid=experiment.id)}, {'title': 'Pages', 'url': request.route_url('experiment.page', eid=experiment.id)}, {'title': '%s (%s)' % (page.title, page.name) if page.title else 'No title (%s)' % page.name, 'url': request.route_url('experiment.page.edit', eid=experiment.id, pid=page.id)}, {'title': 'Delete', 'url': request.route_url('experiment.page.delete', eid=experiment.id, pid=page.id)}], 'values': request.params, 'errors': e.error_dict} return {'experiment': experiment, 'page': page, 'crumbs': [{'title': 'Experiments', 'url': request.route_url('dashboard')}, {'title': experiment.title, 'url': request.route_url('experiment.view', eid=experiment.id)}, {'title': 'Pages', 'url': request.route_url('experiment.page', eid=experiment.id)}, {'title': '%s (%s)' % (page.title, page.name) if page.title else 'No title (%s)' % page.name, 'url': request.route_url('experiment.page.edit', eid=experiment.id, pid=page.id)}, {'title': 'Delete', 'url': request.route_url('experiment.page.delete', eid=experiment.id, pid=page.id)}]} else: raise HTTPNotFound()
def data_item_add(request): dbsession = DBSession() experiment = dbsession.query(Experiment).filter( Experiment.id == request.matchdict['eid']).first() data_set = dbsession.query(DataSet).filter( and_(DataSet.id == request.matchdict['did'], DataSet.experiment_id == request.matchdict['eid'], DataSet.type == 'dataset')).first() if experiment and data_set: try: schema = DynamicSchema() schema.add_field('csrf_token', CSRFValidator()) for column in data_set['columns']: schema.add_field( column, formencode.validators.UnicodeString(if_empty='', if_missing='')) params = schema.to_python(request.params, State(request=request)) with transaction.manager: dbsession.add(data_set) order = dbsession.query(func.max(DataItem.order)).filter( DataItem.dataset_id == data_set.id).first() if order[0] is None: order = 1 else: order = order[0] + 1 data_item = DataItem(dataset_id=data_set.id, order=order) data = {} for column in data_set['columns']: data[column] = params[column] data_item['values'] = data dbsession.add(data_item) dbsession.add(data_item) return { 'status': 'ok', 'id': data_item.id, 'order': data_item.order, 'values': data_item['values'] } except formencode.Invalid as e: return {'status': 'error', 'errors': e.unpack_errors()} else: raise HTTPNotFound()
def reset(request): dbsession = DBSession() experiment = dbsession.query(Experiment).filter( Experiment.id == request.matchdict['eid']).first() if experiment: if request.method == 'POST': try: ResetSchema().to_python(request.params, State(request=request)) with transaction.manager: dbsession.add(experiment) experiment.participants = [] dbsession.add(experiment) raise HTTPFound( request.route_url('experiment.results', eid=experiment.id)) except formencode.Invalid as e: return { 'experiment': experiment, 'values': request.params, 'errors': e.error_dict, 'crumbs': [{ 'title': 'Experiments', 'url': request.route_url('dashboard') }, { 'title': experiment.title, 'url': request.route_url('experiment.view', eid=experiment.id) }, { 'title': 'Results', 'url': request.route_url('experiment.results', eid=experiment.id) }, { 'title': 'Reset', 'url': request.route_url('experiment.results.reset', eid=experiment.id) }] } return { 'experiment': experiment, 'crumbs': [{ 'title': 'Experiments', 'url': request.route_url('dashboard') }, { 'title': experiment.title, 'url': request.route_url('experiment.view', eid=experiment.id) }, { 'title': 'Results', 'url': request.route_url('experiment.results', eid=experiment.id) }, { 'title': 'Clear', 'url': request.route_url('experiment.results.reset', eid=experiment.id) }] } else: raise HTTPNotFound()
def edit_part_task(request): """Handles the ``parts/{pid}/timed-tasks/{tid}/edit`` URL, providing the UI and backend for editing an existing :class:`~wte.models.TimedTask` that belongs to a :class:`~wte.models.Part`. Requires that the user has "edit" rights on the :class:`~wte.models.Part`. """ dbsession = DBSession() part = dbsession.query(Part).filter( Part.id == request.matchdict['pid']).first() task = dbsession.query(TimedTask).filter( TimedTask.id == request.matchdict['tid']).first() if part and task: if part.allow('edit', request.current_user): crumbs = create_part_crumbs( request, part, [{ 'title': 'Timed Actions', 'url': request.route_url('part.timed_task', pid=part.id) }, { 'title': 'Edit', 'url': request.current_route_url }]) if request.method == 'POST': try: options = [] if task.name == 'change_status': status = ['available', 'unavailable'] if part.type == 'module': status.append('archived') options.append(('target_status', formencode.validators.OneOf(status))) params = EditTimedTaskSchema(options).to_python( request.params, State(request=request)) dbsession = DBSession() with transaction.manager: dbsession.add(task) task.timestamp = datetime.combine( params['date'], params['time']) if 'options' in params and params['options']: task.options = params['options'] task.status = 'ready' dbsession.add(part) raise HTTPSeeOther( request.route_url('part.timed_task', pid=part.id)) except formencode.Invalid as e: return { 'errors': e.error_dict, 'values': request.params, 'part': part, 'task': task, 'crumbs': crumbs, 'include_footer': True, 'help': ['user', 'teacher', 'timed_actions.html'] } return { 'part': part, 'task': task, 'crumbs': crumbs, 'include_footer': True, 'help': ['user', 'teacher', 'timed_actions.html'] } else: unauthorised_redirect(request) else: raise HTTPNotFound()
def database(): """The :func:`~ess_test.conftest.database` fixture initialises the database specified in the "testing.ini", removes any existing data, creates the standard permissions, and four test users: * admin - user with full administrative permissions * developer - user with full experiment development permissions * content - user with full editing permissions * general - user with no permissions """ global dbsession_initialised # Load settings settings = get_appsettings('testing.ini') setup_logging('testing.ini') # Init the DB engine = engine_from_config(settings, 'sqlalchemy.') if not dbsession_initialised: DBSession.configure(bind=engine) dbsession_initialised = True Base.metadata.drop_all(engine) Base.metadata.create_all(engine) dbsession = DBSession() # Create Test Users with transaction.manager: admin_user = User(email='*****@*****.**', display_name='Admin', password='******') developer_user = User(email='*****@*****.**', display_name='Developer', password='******') content_user = User(email='*****@*****.**', display_name='Content', password='******') general_user = User(email='*****@*****.**', display_name='General', password='******') dbsession.add(general_user) group = PermissionGroup(title='Site administrator') group.permissions.append(Permission(name='admin.users', title='Administer the users')) group.permissions.append(Permission(name='admin.groups', title='Administer the permission groups')) group.permissions.append(Permission(name='admin.question_types', title='Administer the question types')) admin_user.permission_groups.append(group) group = PermissionGroup(title='Developer') group.permissions.append(Permission(name='survey.new', title='Create new experiments')) admin_user.permission_groups.append(group) developer_user.permission_groups.append(group) group = PermissionGroup(title='Content administrator') group.permissions.append(Permission(name='survey.view-all', title='View all experiments')) group.permissions.append(Permission(name='survey.edit-all', title='Edit all experiments')) group.permissions.append(Permission(name='survey.delete-all', title='Delete all experiments')) content_user.permission_groups.append(group) dbsession.add(admin_user) dbsession.add(developer_user) dbsession.add(content_user) question_types = QuestionTypeIOSchema(include_schemas=(QuestionTypeGroupIOSchema,), many=True).\ loads(resource_string('ess','scripts/templates/default_question_types.json')) dbsession.add_all(question_types.data) # Alembic Stamp alembic_config = config.Config('testing.ini', ini_section='app:main') alembic_config.set_section_option('app:main', 'script_location', 'ess:migrations') command.stamp(alembic_config, "head") dbsession.close() DBSession.close_all() yield DBSession
def permissions(request): """Handles the "/users/{uid}/permissions" URL, providing the form and backend functionality for setting the :class:`~wte.models.Permission` and :class:`~wte.models.PermissionGroup` that the :class:`~wte.models.User` belongs to. """ dbsession = DBSession() user = dbsession.query(User).filter( User.id == request.matchdict['uid']).first() if user: permission_groups = dbsession.query(PermissionGroup).order_by( PermissionGroup.title) permissions = dbsession.query(Permission).order_by(Permission.title) if request.method == 'POST': try: CSRFSchema(allow_extra_fields=True).to_python( request.params, State(request=request)) with transaction.manager: dbsession.add(user) ids = request.params.getall('permission_group') if ids: user.permission_groups = dbsession.query(PermissionGroup).\ filter(PermissionGroup.id.in_(ids)).all() else: user.permission_groups = [] ids = request.params.getall('permission') if ids: user.permissions = dbsession.query(Permission).filter( Permission.id.in_(ids)).all() else: user.permissions = [] dbsession.add(user) dbsession.add(request.current_user) if request.current_user.has_permission('admin.users.view'): raise HTTPSeeOther(request.route_url('users')) else: raise HTTPSeeOther( request.route_url('user.view', uid=user.id)) except Invalid as e: return { 'errors': e.error_dict, 'user': user, 'permission_groups': permission_groups, 'permissions': permissions, 'crumbs': create_user_crumbs( request, [{ 'title': user.display_name, 'url': request.route_url('user.view', uid=user.id) }, { 'title': 'Permissions', 'url': request.route_url('user.permissions', uid=user.id) }]) } return { 'user': user, 'permission_groups': permission_groups, 'permissions': permissions, 'crumbs': create_user_crumbs( request, [{ 'title': user.display_name, 'url': request.route_url('user.view', uid=user.id) }, { 'title': 'Permissions', 'url': request.route_url('user.permissions', uid=user.id) }]) } else: raise HTTPNotFound()
def edit(request): """Handles the "/users/{uid}/edit" URL, providing the form and backend functionality to update the user's profile. """ dbsession = DBSession() user = dbsession.query(User).filter( User.id == request.matchdict['uid']).first() if user: if user.allow('edit', request.current_user): crumbs = create_user_crumbs( request, [{ 'title': user.display_name, 'url': request.route_url('user.view', uid=user.id) }, { 'title': 'Edit', 'url': request.route_url('user.edit', uid=user.id) }]) if request.method == 'POST': try: params = EditSchema().to_python( request.params, State(dbsession=dbsession, userid=user.id, email_domains=get_config_setting( request, key='registration.domains', target_type='list', default=None), user_class=User, request=request)) with transaction.manager: dbsession.add(user) user.email = params['email'] user.display_name = params['display_name'] if params['password']: user.new_password(params['password']) options = {} if params['option']: for key, value in params['option'].items(): if isinstance(value, list): options[key] = value[-1] else: options[key] = value user.options = options raise HTTPSeeOther( request.route_url('user.view', uid=request.matchdict['uid'])) except Invalid as e: print(e) return { 'e': e.error_dict, 'values': request.params, 'user': user, 'crumbs': crumbs, 'help': ['user', 'user', 'profile.html'] } return { 'user': user, 'crumbs': crumbs, 'help': ['user', 'user', 'profile.html'] } else: unauthorised_redirect(request) else: raise HTTPNotFound()