Ejemplo n.º 1
0
def test_empty_first_name_incorrect_email_employees(browser):
    page = start_page(browser)
    num_populated_rows = randint(2, page.MAX_ROWS)
    row_empty_first_name, row_incorrect_email = randint(1, num_populated_rows), randint(1, num_populated_rows)

    for row in range(1, num_populated_rows + 1):
        first_name = utilities.generate_random_string(size=randint(5, 60))
        last_name = utilities.generate_random_string(size=randint(10, 70))

        if row != row_empty_first_name:
            page.fill_field(text=first_name,
                            ordinal_number=row,
                            part_of_locator=page.INPUT_FIRST_NAME)
        page.fill_field(text=last_name,
                        ordinal_number=row,
                        part_of_locator=page.INPUT_LAST_NAME)
        if row != row_incorrect_email:
            page.fill_field(text=f'{first_name.lower()}.{last_name.lower()}@{data.DOMAIN}',
                            ordinal_number=row,
                            part_of_locator=page.INPUT_EMAIL)
        else:
            page.fill_field(text=utilities.generate_random_string(randint(5, 40), punc=True),
                            ordinal_number=row,
                            part_of_locator=page.INPUT_EMAIL)

    page.click_save()

    status_message_has_text = False

    if row_empty_first_name <= row_incorrect_email:
        status_message_has_text = page.check_status_message(page.STATUS_MANDATORY_FIELD)
    else:
        status_message_has_text = page.check_status_message(page.STATUS_INCORRECT_EMAIL)

    assert status_message_has_text is True
Ejemplo n.º 2
0
def test_empty_first_name_employees(browser):
    page = start_page(browser)
    number_of_populated_rows = randint(2, page.MAX_ROWS)
    row_with_empty_first_name = randint(1, number_of_populated_rows)

    for row in range(1, number_of_populated_rows + 1):
        first_name = utilities.generate_random_string(size=randint(10, 50))
        last_name = utilities.generate_random_string(size=randint(10, 80))

        if row != row_with_empty_first_name:
            page.fill_field(text=first_name,
                            ordinal_number=row,
                            part_of_locator=page.INPUT_FIRST_NAME)

        page.fill_field(text=last_name,
                        ordinal_number=row,
                        part_of_locator=page.INPUT_LAST_NAME)
        page.fill_field(text=f'{first_name.lower()}.{last_name.lower()}@{data.DOMAIN}',
                        ordinal_number=row,
                        part_of_locator=page.INPUT_EMAIL)

    page.click_save()

    status_message_has_text = page.check_status_message(page.STATUS_MANDATORY_FIELD)

    assert status_message_has_text is True
Ejemplo n.º 3
0
def test_add_employees(browser):
    page = start_page(browser)
    number_of_populated_rows = randint(2, page.MAX_ROWS)
    list_of_names = []

    for row in range(1, number_of_populated_rows + 1):
        first_name = utilities.generate_random_string(randint(5, 25))
        last_name = utilities.generate_random_string(randint(5, 35))
        list_of_names.append(f'{first_name} {last_name}')

        page.fill_field(text=first_name,
                        ordinal_number=row,
                        part_of_locator=page.INPUT_FIRST_NAME)
        page.fill_field(text=last_name,
                        ordinal_number=row,
                        part_of_locator=page.INPUT_LAST_NAME)
        page.fill_field(text=f'{first_name.lower()}.{last_name.lower()}@{data.DOMAIN}',
                        ordinal_number=row,
                        part_of_locator=page.INPUT_EMAIL)

    page.click_save()
    time.sleep(5)

    result_page = AddEmployeesResultPage(browser)
    results = result_page.check_results(list_of_names)

    assert results is True
Ejemplo n.º 4
0
def seed():
    ''' adds a default admin to the system based on your settings
    usage:
        $ . /path/to/venv/bin/activate
        (venv)$ python
        >> import application
        >> application.controllers.seed()
        user bruce@wayneindustries created with specified password
    '''
    # initialize the mongoengine connection
    # repeated to ensure any config changes from testing are pulled in
    mongo_config = app.config['MONGO_CONFIG']
    mongodb_uri = 'mongodb://'
    if mongo_config['dbuser'] and mongo_config['dbpassword']:
        mongodb_uri += '%s:%s@' % (mongo_config['dbuser']
                , mongo_config['dbpassword'])
    mongodb_uri += '%s/%s' % (mongo_config['hosts'], mongo_config['db_name'])
    connect(mongo_config['db_name'], host=mongodb_uri)

    users = User.objects()
    if not users:
        app.logger.info('no users in the db, so we will create one')
        initial_user = app.config['INITIAL_USER']

        default_admin = User(
            admin_rights = True
            , api_id = 'ID' + utilities.generate_random_string(32)
            , api_key = utilities.generate_random_string(34)
            , email = initial_user['email']
            , email_confirmation_code = utilities.generate_random_string(34)
            , email_confirmed = False
            , last_login_time = datetime.datetime.utcnow()
            , name = initial_user['name']
            , organization = initial_user['organization']
            , password_hash = bcrypt.generate_password_hash(
                initial_user['password'])
            , registration_time = datetime.datetime.utcnow()
            , verified = True
        )
        try:
            default_admin.save()
            app.logger.info('user %s created with specified password' % \
                default_admin['email'])
        except:
            app.logger.error('user %s could not be saved; check for dupes' % \
                default_admin['email'])
    else:
        app.logger.info('not seeding db as there are already users present')
Ejemplo n.º 5
0
def upload_file():
    if request.method == 'POST':
        # check if the post request has the file part
        if 'file' not in request.files:
            flash('No file part')
            return redirect(request.url)
        file = request.files['file']
        # if user does not select file, browser also
        # submit an empty part without filename
        if file.filename == '':
            flash('No selected file')
            return redirect(request.url)
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            random_string = utilities.generate_random_string(30)
            # create upload object and save to db
            upload_obj = Upload(utilities.get_user_ip(), filename,
                                random_string,
                                datetime.now().strftime('%Y-%m-%d %H:%M'),
                                utilities.get_date_time_now())
            upload_obj.save_upload_to_db()
            print("saved")
            return "Your link is: " + random_string + "\n\n" + "Expires: " + utilities.get_date_time_now(
            )

    return '''
Ejemplo n.º 6
0
def home():
    ''' GET to view the dream-input controls
    POST to create a new dream-tage
    '''
    if request.method == 'GET':
        return render_template('home.html')

    if request.method == 'POST':
        # check CSRF token..

        # save a new dream instance in the db
        new_dream = Dream(
            # might want to do something to insure uniqueness
            slug = generate_random_string(5)
            , description = request.form.get('description', '')
            , created = datetime.datetime.utcnow()
        )
        new_dream.save()

        # enqueue the processing for keyword extraction and sourcing clips

        configs = {
            'mongo': mongo_config
            , 'vimeo': app.config['VIMEO']
            , 'aws': app.config['AWS']
            , 'twitter': app.config['TWITTER']
        }
        queue.enqueue_call(func=process_dream
            , args=(new_dream.slug, configs,)
            , timeout=600)

        # redirect to the invidiual dream page
        return redirect(url_for('dream', dream_slug = new_dream.slug))
Ejemplo n.º 7
0
def test_incorrect_email(browser):
    page = start_page(browser)
    ordinal_num = page.choose_ordinal_number()

    page.fill_field(text=utilities.generate_random_string(size=randint(10, 60)),
                    ordinal_number=ordinal_num,
                    part_of_locator=page.INPUT_FIRST_NAME)
    page.fill_field(text=utilities.generate_random_string(size=randint(8, 30)),
                    ordinal_number=ordinal_num,
                    part_of_locator=page.INPUT_LAST_NAME)
    page.fill_field(text=utilities.generate_random_string(size=randint(3, 50), punc=True),
                    ordinal_number=ordinal_num,
                    part_of_locator=page.INPUT_EMAIL)
    page.click_save()
    status_message_has_text = page.check_status_message(page.STATUS_INCORRECT_EMAIL)

    assert status_message_has_text is True
Ejemplo n.º 8
0
def test_remove_row(browser):
    page = start_page(browser)
    ordinal_num = page.choose_ordinal_number()

    page.fill_field(text=utilities.generate_random_string(size=randint(6, 30)),
                    ordinal_number=ordinal_num,
                    part_of_locator=page.INPUT_FIRST_NAME)
    page.fill_field(text=utilities.generate_random_string(size=randint(10, 40)),
                    ordinal_number=ordinal_num,
                    part_of_locator=page.INPUT_LAST_NAME)
    page.fill_field(text=utilities.generate_random_email(data.LOGIN_EMAIL),
                    ordinal_number=ordinal_num,
                    part_of_locator=page.INPUT_EMAIL)

    page.click_remove(ordinal_num)
    empty_row = page.check_empty_row(ordinal_num)

    assert empty_row is True
Ejemplo n.º 9
0
def seed():
    ''' adds a default admin to the system based on your settings
    usage:
        $ . /path/to/venv/bin/activate
        (venv)$ python
        >> import ivrhub 
        >> ivrhub.views.seed()
        user bruce@wayneindustries created with specified password
    '''
    # initialize the mongoengine connection
    # repeated to ensure any config changes from testing are pulled in
    connect(app.config['MONGO_CONFIG']['db_name'],
            host=app.config['MONGO_CONFIG']['host'],
            port=int(app.config['MONGO_CONFIG']['port']))

    users = User.objects()
    if not users:
        app.logger.info('no users in the db, so we will create one')
        initial_user = app.config['INITIAL_USER']

        default_admin = User(
            admin_rights=True,
            api_id='ID' + utilities.generate_random_string(32),
            api_key=utilities.generate_random_string(34),
            email=initial_user['email'],
            email_confirmation_code=utilities.generate_random_string(34),
            email_confirmed=False,
            last_login_time=datetime.datetime.utcnow(),
            name=initial_user['name'],
            organization=initial_user['organization'],
            password_hash=bcrypt.generate_password_hash(
                initial_user['password']),
            registration_time=datetime.datetime.utcnow(),
            verified=True)
        try:
            default_admin.save()
            app.logger.info('user %s created with specified password' % \
                default_admin['email'])
        except:
            app.logger.error('user %s could not be saved; check for dupes' % \
                default_admin['email'])
    else:
        app.logger.info('not seeding db as there are already users present')
Ejemplo n.º 10
0
def forgot(code):
    ''' take input email address
    send password reset link
    '''
    if request.method == 'POST':
        if code:
            user = User.objects(forgot_password_code=code)
            if not user:
                abort(404)
            user = user[0]

            user.password_hash = bcrypt.generate_password_hash(
                request.form['password'])
            user.save()

            app.logger.info('%s changed his password' % user.email)
            flash('password successfully changed; you may now login'
                , 'success')
            return redirect(url_for('home'))


        user = User.objects(email=request.form['email'])
        if not user:
            app.logger.info(
                'password reset of %s attempted but that email is not known' % 
                request.form['email'])
            flash('email not found :/', 'error')
            return redirect(url_for('forgot'))
        
        user = user[0]
        user.forgot_password_code = utilities.generate_random_string(34)
        user.save()
        #user.reload()

        utilities.send_forgot_password_link(user)
        
        app.logger.info('%s requested a forgotten password code' % \
            request.form['email'])

        flash('We\'ve sent an email to %s with information on how to \
            reset your account\'s password.' % user.email)
        return redirect(url_for('home'))

    elif request.method == 'GET':
        if code:
            user = User.objects(forgot_password_code=code)
            if not user:
                app.logger.warning('someone tried a bad password reset code')
                abort(404)
            user = user[0]
            return render_template('forgot_password_create_new.html'
                , user=user)
        else:
            return render_template('forgot.html')
Ejemplo n.º 11
0
def forgot(code):
    ''' take input email address
    send password reset link
    '''
    if request.method == 'POST':
        if code:
            user = User.objects(forgot_password_code=code)
            if not user:
                abort(404)
            user = user[0]

            user.password_hash = bcrypt.generate_password_hash(
                request.form['password'])
            user.save()

            app.logger.info('%s changed his password' % user.email)
            flash('password successfully changed; you may now login',
                  'success')
            return redirect(url_for('home'))

        user = User.objects(email=request.form['email'])
        if not user:
            app.logger.info(
                'password reset of %s attempted but that email is not known' %
                request.form['email'])
            flash('email not found :/', 'error')
            return redirect(url_for('forgot'))

        user = user[0]
        user.forgot_password_code = utilities.generate_random_string(34)
        user.save()
        #user.reload()

        utilities.send_forgot_password_link(user)

        app.logger.info('%s requested a forgotten password code' % \
            request.form['email'])

        flash('We\'ve sent an email to %s with information on how to \
            reset your account\'s password.' % user.email)
        return redirect(url_for('home'))

    elif request.method == 'GET':
        if code:
            user = User.objects(forgot_password_code=code)
            if not user:
                app.logger.warning('someone tried a bad password reset code')
                abort(404)
            user = user[0]
            return render_template('forgot_password_create_new.html',
                                   user=user)
        else:
            return render_template('forgot.html')
Ejemplo n.º 12
0
def register():
    ''' displays registration page
    sends confirmation email to registrant
    sends notification email to admin
    '''
    if request.method == 'GET':
        return render_template('register.html')

    elif request.method == 'POST':
        # check that passwords match
        if request.form['password'] != \
            request.form['retype_password']:
            flash('submitted passwords did not match', 'error')
            return redirect(url_for('register'))

        # check that user email is unique
        duplicates = User.objects(email=request.form['email'])
        if duplicates:
            flash('This email address has been registered already.'
                , 'error')
            return redirect(url_for('register'))

        # create the new user
        try:
            new_user = User(
                admin_rights = False
                , email = request.form['email']
                , email_confirmation_code = \
                    utilities.generate_random_string(34)
                , email_confirmed = False
                , last_login_time = datetime.datetime.utcnow()
                , name = request.form['name']
                , password_hash = bcrypt.generate_password_hash(
                    request.form['password'])
                , registration_time = datetime.datetime.utcnow()
                , verified = False
                , verified_by = None)
            new_user.save() 
        except:
            app.logger.error('user registration failed for %s' % \
                request.form['email'])
            flash('There was an error in the form, sorry :/', 'error')
            return redirect(url_for('register'))
        
        # seek email confirmation
        utilities.send_confirmation_email(new_user)

        # log the user in  
        session['email'] = new_user.email
        session['admin_rights'] = new_user.admin_rights

        # redirect to a holding area
        return redirect(url_for('verification_status'))
Ejemplo n.º 13
0
def register():
    ''' displays registration page
    sends confirmation email to registrant
    sends notification email to admin
    '''
    if request.method == 'GET':
        return render_template('register.html')

    elif request.method == 'POST':
        # check that passwords match
        if request.form['password'] != \
            request.form['retype_password']:
            flash('submitted passwords did not match', 'error')
            return redirect(url_for('register'))

        # check that user email is unique
        duplicates = User.objects(email=request.form['email'])
        if duplicates:
            flash('This email address has been registered already.', 'error')
            return redirect(url_for('register'))

        # create the new user
        try:
            new_user = User(
                admin_rights = False
                , email = request.form['email']
                , email_confirmation_code = \
                    utilities.generate_random_string(34)
                , email_confirmed = False
                , last_login_time = datetime.datetime.utcnow()
                , name = request.form['name']
                , password_hash = bcrypt.generate_password_hash(
                    request.form['password'])
                , registration_time = datetime.datetime.utcnow()
                , verified = False
                , verified_by = None)
            new_user.save()
        except:
            app.logger.error('user registration failed for %s' % \
                request.form['email'])
            flash('There was an error in the form, sorry :/', 'error')
            return redirect(url_for('register'))

        # seek email confirmation
        utilities.send_confirmation_email(new_user)

        # log the user in
        session['email'] = new_user.email
        session['admin_rights'] = new_user.admin_rights

        # redirect to a holding area
        return redirect(url_for('verification_status'))
Ejemplo n.º 14
0
def test_empty_first_name(browser):
    page = start_page(browser)
    ordinal_num = page.choose_ordinal_number()

    page.fill_field(text=utilities.generate_random_string(),
                    ordinal_number=ordinal_num,
                    part_of_locator=page.INPUT_LAST_NAME)
    page.fill_field(text=utilities.generate_random_email(data.LOGIN_EMAIL),
                    ordinal_number=ordinal_num,
                    part_of_locator=page.INPUT_EMAIL)
    page.click_save()
    status_message_has_text = page.check_status_message(page.STATUS_MANDATORY_FIELD)

    assert status_message_has_text is True
Ejemplo n.º 15
0
def test_add_employee(browser):
    page = start_page(browser)
    ordinal_num = page.choose_ordinal_number()

    first_name = utilities.generate_random_string(size=randint(6, 30))
    last_name = utilities.generate_random_string(size=randint(6, 30))

    page.fill_field(text=first_name,
                    ordinal_number=ordinal_num,
                    part_of_locator=page.INPUT_FIRST_NAME)
    page.fill_field(text=last_name,
                    ordinal_number=ordinal_num,
                    part_of_locator=page.INPUT_LAST_NAME)
    page.fill_field(text=utilities.generate_random_email(data.LOGIN_EMAIL),
                    ordinal_number=ordinal_num,
                    part_of_locator=page.INPUT_EMAIL)
    page.click_save()

    time.sleep(5)

    result_page = AddEmployeesResultPage(browser)
    results = result_page.check_results([f'{first_name} {last_name}'])

    assert results is True
Ejemplo n.º 16
0
def _generate_csrf_token():
    ''' create a signed token
    signature is made with app's secret and user api key
    signed tokens (rather than per-request tokens) work across tabs
    they also play more nicely with AJAX
    still vulnerable to an attack from a verified user with an API key
    '''
    # create a random value
    salt = utilities.generate_random_string(24)

    # get the user for their API key
    user = User.objects(email=session['email'])[0]

    # hash the app's secret with these values
    m = hashlib.md5()
    target = salt + user.api_key + base64.b64encode(app.config['SECRET_KEY'])
    m.update(target)

    # create a token by combining the salt and hash
    token = salt + '-' + m.hexdigest()

    return token
Ejemplo n.º 17
0
def _generate_csrf_token():
    ''' create a signed token
    signature is made with app's secret and user api key
    signed tokens (rather than per-request tokens) work across tabs
    they also play more nicely with AJAX
    still vulnerable to an attack from a verified user with an API key
    '''
    # create a random value
    salt = utilities.generate_random_string(24)

    # get the user for their API key
    user = User.objects(email=session['email'])[0]

    # hash the app's secret with these values
    m = hashlib.md5()
    target = salt + user.api_key + base64.b64encode(app.config['SECRET_KEY'])
    m.update(target)

    # create a token by combining the salt and hash
    token = salt + '-' + m.hexdigest()

    return token
Ejemplo n.º 18
0
def render_report(rendering_id, url):
    ''' create a pdf with wkhtmltopdf
    '''
    renderings = Rendering.objects(id=rendering_id)
    if not renderings:
        return False
    rendering = renderings[0]

    absolute_filename = str('/tmp/%s' % rendering.filename)

    # start the pdf generation
    envoy.run('xvfb-run wkhtmltopdf --use-xserver %s %s' % (
        url, absolute_filename))

    # save to s3
    connection = boto.connect_s3(
        aws_access_key_id=app.config['AWS']['access_key_id']
        , aws_secret_access_key=app.config['AWS']['secret_access_key'])
    
    bucket_name = '%s-%s' % (app.config['RENDERINGS_BUCKET']
        , app.config['AWS']['access_key_id'])
    bucket = connection.create_bucket(bucket_name.lower())

    s3_key = S3_Key(bucket)
    s3_key.key = '%s-%s' % (rendering.filename
        , utilities.generate_random_string(6))
    s3_key.set_contents_from_filename(absolute_filename)

    # remove the uploaded file from the local temp location
    os.unlink(absolute_filename)

    # update the rendering to indicate process completion
    rendering.s3_key = s3_key.key
    rendering.save()
    
    return True
Ejemplo n.º 19
0
def forms(org_label, form_label):
    ''' show the forms
    if there's a label included in the route, render that form alone
    '''
    user = User.objects(email=session['email'])[0]
    
    # find the relevant organization
    orgs = Organization.objects(label=org_label)
    if not orgs:
        app.logger.error('%s tried to access an organization that does not \
            exist' % session['email'])
        flash('Organization "%s" not found, sorry!' % org_label, 'warning')
        return redirect(url_for('organizations'))
    org = orgs[0]

    # permission-check
    if org not in user.organizations and not user.admin_rights:
        app.logger.error('%s tried to access an organization but was denied \
            for want of admin rights' % session['email'])
        abort(404)

    if request.method == 'POST':
        if not form_label:
            abort(404)

        forms = Form.objects(label=form_label, organization=org)
        if not forms:
            app.logger.error('%s tried to access a form that does not \
                exist' % session['email'])
            flash('Form "%s" does not exist, sorry!' % form_label, 'warning')
            return redirect(url_for('forms', org_label=org_label))
        form = forms[0]

        form_type = request.form.get('form_type', '')

        if form_type == 'info':
            name = request.form.get('name', '')
            form.name = name
            form.label = str(escape(name).replace(' ', '-')).lower()

            form.description = request.form.get('description', '')

        elif form_type == 'admin':
            # blow away the form itself
            name = form.name
            utilities.delete_form(form)
            app.logger.info('%s deleted %s' % (session['email'], name))
            flash('form "%s" was deleted' % name, 'success')
            return redirect(url_for('organizations', org_label=org.label))
        
        else:
            # bad 'form_type'
            abort(404)
       
        try:
            form.save()
            flash('Changes to this form were saved successfully', 'success')
            return redirect(url_for('forms', org_label=org_label
                , form_label=form.label))
        except:
            form.reload()
            app.logger.error('%s experienced an error saving info about form \
                "%s"' % (session['email'], request.form['name']))
            flash('Error saving changes, sorry.  Is the name unique?', 'error')
            return redirect(url_for('forms', org_label=org_label
                , form_label=form_label))
    
    if request.method == 'GET':
        if form_label:
            forms = Form.objects(label=form_label, organization=org)
            if not forms:
                app.logger.error('%s tried to access a form that does not \
                    exist' % session['email'])
                flash('Form "%s" does not exist, sorry!' % form_label
                    , 'warning')
                return redirect(url_for('organizations', org_label=org_label))
            form = forms[0]

            if request.args.get('edit', '') == 'true':
                return render_template('form_edit.html', form=form)
            
            else:
                # count the number of entities
                question_count = Question.objects(form=form).count()
                response_count = Response.objects(form=form).count()
                return render_template('form.html', form=form
                    , question_count=question_count
                    , response_count=response_count)

        if request.args.get('create', '') == 'true':
            # create a new form
            try:
                form_name = 'form-%s' % utilities.generate_random_string(6)
                new_form = Form(
                    calling_code = utilities.generate_calling_code(6)
                    , label = form_name.lower()
                    , organization = org
                    , name = form_name
                )
                new_form.save() 
                app.logger.info('form created by %s' % session['email'])
                flash('Form created; please change the defaults', 
                    'success')
                # redirect to the editing screen
                return redirect(url_for('forms', org_label=org_label
                    , form_label=new_form.label, edit='true'))
            except:
                app.logger.error('form creation failed for %s' % \
                    session['email'])
                flash('There was an error in the form, sorry :/', 'error')
                return redirect(url_for('forms', org_label=org_label))
            
        
        # nobody in particular was specified; punt for now
        abort(404)
        '''
Ejemplo n.º 20
0
def graphs(org_label, project_label, graph_label):
    ''' graphin things
    /organizations/aquaya/projects/water-quality/graphs
        : view a list of all graphs for the project
    /organizations/aquaya/projects/water-quality/graphs?create=true
        : create a new graph config, immediately redirect to editing
    /organizations/aquaya/projects/water-quality/graphs/ph-vs-time
        : view a graph
    /organizations/aquaya/projects/water-quality/graphs/ph-vs-time?edit=true
        : edit a graph; accepts GET or POST
    '''
    user = User.objects(email=session['email'])[0]
    
    orgs = Organization.objects(label=org_label)
    if not orgs:
        flash('Organization "%s" not found, sorry!' % org_label, 'warning')
        return redirect(url_for('organizations'))
    org = orgs[0]

    # permission-check
    if org not in user.organizations and not user.admin_rights:
        app.logger.error('%s tried to view a project but was \
            denied for want of admin rights' % session['email'])
        abort(404)
    
    # find the project
    projects = Project.objects(label=project_label, organization=org) 
    if not projects:
        flash('Project "%s" not found, sorry!' % project_label, 'warning')
        return redirect(url_for('organizations', org_label=org.label))
    project = projects[0]

    if request.method == 'POST':
        # we have a graph_label
        graphs = Graph.objects(label=graph_label, project=project)
        if not graphs:
            abort(404)
        graph = graphs[0]

        form_type = request.form.get('form_type', '')
        if form_type == 'info':
            if graph.name != request.form.get('name', ''):
                name = request.form.get('name', '')
                graph.update(set__name = name)

                graphs = Graph.objects(project=project).only('label')
                labels = [g.label for g in graphs]
                graph.update(set__label = utilities.generate_label(name
                    , labels))

                # reload to catch the name change
                graph.reload()

            graph.update(set__description = 
                    request.form.get('description', ''))
            graph.update(set__graph_type = request.form.get('graph_type', ''))

            # axes specify a header and come of the form 'header_id__4abcd001'
            xaxis = request.form.get('xaxis', '')
            if xaxis:
                xaxis = xaxis.split('header_id__')[1]
                header = Header.objects(id=xaxis)[0]
                graph.update(set__xaxis = header)

            yaxis = request.form.get('yaxis', '')
            if yaxis:
                yaxis = yaxis.split('header_id__')[1]
                header = Header.objects(id=yaxis)[0]
                graph.update(set__yaxis = header)

            # pie chart headers are similar to axes..
            pie_header = request.form.get('pie_header', '')
            if pie_header:
                pie_header = pie_header.split('header_id__')[1]
                header = Header.objects(id=pie_header)[0]
                graph.update(set__pie_header = header)

        elif form_type == 'filters':
            # extract the 'any filters' vs 'all' distinction
            filter_settings = request.form.get('apply_any_filters', '')
            if filter_settings == 'true':
                graph.update(set__apply_any_filters = True)
            else:
                graph.update(set__apply_any_filters = False)

            # attach filter to graph
            requested_filter_ids = request.form.getlist('filters')
            attached_filters = []
            for requested_id in requested_filter_ids:
                prefix, filter_id = requested_id.split('__')
                filters = Filter.objects(id=filter_id)
                if not filters:
                    abort(404)
                attached_filters.append(filters[0])

            graph.update(set__filters = attached_filters)
                
        elif form_type == 'admin':
            # delete the graph
            name = graph.name
            utilities.delete_graph(graph, session['email'])
            flash('graph "%s" was deleted successfully' % name, 'success')
            return redirect(url_for('graphs', org_label=org.label
                , project_label=project.label))
        
        else:
            # bad 'form_type'
            abort(404)
       
        flash('changes saved successfully', 'success')
        return redirect(url_for('graphs', org_label=org.label
            , project_label=project.label, graph_label=graph.label))
    
    if request.method == 'GET':
        if graph_label:
            graphs = Graph.objects(label=graph_label, project=project)
            if not graphs:
                app.logger.error('%s tried to access a graph that does not \
                    exist' % session['email'])
                flash('Graph "%s" not found, sorry!' % graph_label
                    , 'warning')
                return redirect(url_for('projects'), org_label=org.label
                    , project_label=project.label)
            graph = graphs[0]

            if request.args.get('edit', '') == 'true':
                # valid graph types
                graph_types = ['line', 'scatter', 'bar', 'chart', 'pie']
                
                available_filters = Filter.objects(project=project)
                    
                return render_template('graph_edit.html', graph=graph
                    , graph_types = graph_types
                    , allowed_graph_types = constants.graph_types
                    , available_filters = available_filters)

            else:
                # render a graph
                data = []
                project_count = None
                filtered_count = None
                if graph.graph_type == 'line':
                    if graph.xaxis and graph.yaxis:
                        data, project_count = (
                                utilities.generate_line_graph_data(graph))
                        filtered_count = len(data)
                    else:
                        flash('define an x-axis and y-axis for plotting'
                            , 'warning')

                elif graph.graph_type == 'pie':
                    if graph.pie_header:
                        data, project_count = (
                                utilities.generate_pie_chart_data(graph))
                        filtered_count = sum([i['data'] for i in json.loads(data)])
                    else:
                        flash('define a column to create this pie chart'
                                , 'warning')

                return render_template('graph.html', graph=graph, data=data
                    , project_count=project_count
                    , filtered_count = filtered_count)

        if request.args.get('create', '') == 'true':
            # create a new graph

            # CSRF validation
            token = request.args.get('token', '')
            if not verify_token(token):
                abort(403)

            try:
                graph_name = 'graph-%s' % utilities.generate_random_string(6)
                new_graph = Graph(
                    creation_time = datetime.datetime.utcnow()
                    , creator = user
                    , label = graph_name.lower()
                    , project = project
                    , name = graph_name
                )
                new_graph.save() 
                app.logger.info('graph created by %s' % session['email'])
                flash('graph created; please change the defaults', 'success')
            except:
                app.logger.error('graph creation failed for %s' % \
                    session['email'])
                flash('There was an error, sorry :/', 'error')
                return redirect(url_for('projects', org_label=org.label
                    , project=project.label))
            
            # redirect to the editing screen
            return redirect(url_for('graphs', org_label=org.label
                , project_label=project.label, graph_label=new_graph.label
                , edit='true'))
        
        # no graph in particular was specified
        graphs = Graph.objects(project=project)
        return render_template('project_graphs.html', project=project
            , graphs=graphs)
Ejemplo n.º 21
0
def uploads(o_label, p_label, u_label):
    ''' uploadin
    /organizations/aquaya/projects/water-quality/uploads?create=true
        : create a new upload, immediately redirect to editing
    /organizations/aquaya/projects/water-quality/uploads/may-2012
        : view an upload
    /organizations/aquaya/projects/water-quality/uploads/may-2012?edit=true
        : edit an upload; accepts GET or POST
    '''
    # can't wrap the line through a route or spaces are injected
    # hence this silliness:
    org_label = o_label
    project_label = p_label
    upload_label = u_label

    user = User.objects(email=session['email'])[0]

    orgs = Organization.objects(label=org_label)
    if not orgs:
        flash('Organization "%s" not found, sorry!' % org_label, 'warning')
        return redirect(url_for('organizations'))
    org = orgs[0]
    
    # permission-check
    if org not in user.organizations and not user.admin_rights:
        app.logger.error('%s tried to view a project but was \
            denied for want of admin rights' % session['email'])
        abort(404)
    
    projects = Project.objects(label=project_label, organization=org) 
    if not projects:
        flash('Project "%s" not found, sorry!' % project_label, 'warning')
        return redirect(url_for('organizations', org_label=org.label))
    project = projects[0]

    if request.method == 'GET':
        if not upload_label and request.args.get('create', '') == 'true':
            # create a new upload

            # CSRF validation
            token = request.args.get('token', '')
            if not verify_token(token):
                abort(403)

            try:
                name = 'upl-%s' % utilities.generate_random_string(6)
                new_upload = Upload(
                    label = name.lower()
                    , name = name
                    , project = project
                )
                new_upload.save()

                project.update(push__uploads=new_upload)
                app.logger.info('upload created by %s' % session['email'])
                flash('please select a file and make other edits', 'warning')
            except:
                app.logger.error('upload creation failed for %s' % \
                    session['email'])
                flash('There was an error, sorry :/', 'error')
                return redirect(url_for('projects', org_label=org.label
                    , project_label=project.label))
            
            # redirect to the editing screen
            return redirect(url_for('uploads', o_label=org.label
                , p_label=project.label, u_label=new_upload.label
                , edit='true'))

        elif not upload_label:
            # could show projects uploads but punting for now
            unique_entries = Entry.objects(project=project, unique=True
                , visible=True)
            duplicate_entries = Entry.objects(project=project
                , unique=False)
            hidden_entries = Entry.objects(project=project, visible=False
                , unique=True)
            
            upload_entry_counts = {}
            for upload in project.uploads:
                upload_entry_counts[upload.id] = Entry.objects(upload=upload).count()
            
            return render_template('project_uploads.html', project=project
                , unique_entries=unique_entries
                , duplicate_entries=duplicate_entries
                , upload_entry_counts=upload_entry_counts
                , hidden_entries=hidden_entries)
        
        else:
            # we have an upload label
            uploads = Upload.objects(label=upload_label, project=project) 
            if not uploads:
                flash('Upload "%s" not found, sorry!' % upload_label
                    , 'warning')
                return redirect(url_for('projects', org_label=org.label
                    , project_label=project.label))
            upload = uploads[0]

            if request.args.get('edit', '') == 'true':
                # show the editor

                # queue up any flashed messages
                # these get set during the worker's processing of a file
                worker_messages = upload.worker_messages
                for message in worker_messages:
                    flash(message['message'], message['status'])
                upload.update(set__worker_messages = [])

                return render_template('upload_edit.html', upload=upload
                        , number_of_pages=0)

            else:
                if not upload.filename:
                    # need to add one so redirect to editing
                    flash('please specify a file for this upload', 'warning')
                    return redirect(url_for('uploads', o_label=org.label
                        , p_label=project.label, u_label=upload.label
                        , edit='true'))
                else:

                    # things are still in progress
                    if not upload.s3_key:
                        flash('Your file is being processed in the '
                            'background.  Refresh the page to see updates.'
                            , 'info')
                        return render_template('upload.html', upload=upload
                                , number_of_pages=0)

                    # otherwise upload is complete, show the data
                    # pagination
                    total_entries = Entry.objects(project=project
                        , upload=upload).count()
                    entries_per_page = 10
                    pages = utilities.calculate_pages(total_entries
                        , entries_per_page=entries_per_page)

                    # validate the requested page
                    current_page = utilities.validate_page_request(
                        request.args.get('page', 1), pages)

                    # get the sorted entries
                    entries = utilities.query_entries(project
                        , upload = upload
                        , skip = (entries_per_page * (current_page - 1))
                        , limit = entries_per_page)
                    
                    hidden_entries_count = Entry.objects(upload=upload
                        , visible=False).count()

                    # queue up any flashed messages
                    # these get set during the worker's processing of a file
                    worker_messages = upload.worker_messages
                    for message in worker_messages:
                        flash(message['message'], message['status'])
                    upload.update(set__worker_messages = [])

                    return render_template('upload.html'
                        , upload=upload
                        , entries=entries
                        , total_entries = total_entries
                        , hidden_entries_count=hidden_entries_count
                        , current_page = current_page
                        , number_of_pages = pages)

    elif request.method == 'POST':
        # we have an upload label
        uploads = Upload.objects(label=upload_label, project=project)
        if not uploads:
            abort(404)
        upload = uploads[0]

        form_type = request.form.get('form_type', '')
        if form_type == 'info':

            if upload.name != request.form.get('name', ''):
                name = request.form.get('name', '')
                upload.update(set__name = name)

                uploads = Upload.objects(project=project).only('label')
                labels = [u.label for u in uploads]
                upload.update(set__label = 
                        utilities.generate_label(name, labels))

                upload.project.update(set__update_time =
                        datetime.datetime.utcnow())
                upload.reload()
            
            if upload.description != request.form.get('description', ''):
                upload.update(set__description =
                        request.form.get('description', ''))
                upload.project.update(set__update_time =
                        datetime.datetime.utcnow())

            if request.files.get('data_file', ''):
                data_file = request.files.get('data_file')
                
                try:
                    filename = uploaded_data.save(data_file)
                    if '..' in filename or filename.startswith('/'):
                        app.logger.info('%s tried to upload a malicious file \
                            "%s"' % (session['email'], filename))
                        flash('bad filename, sorry :/', 'error')
                        return redirect(url_for('uploads'
                            , o_label=org.label, p_label=project.label
                            , u_label=upload.label, edit='true'))

                    absolute_filename = uploaded_data.path(filename)
                
                except UploadNotAllowed:
                    app.logger.info('%s tried to upload an unsupported file'
                        % session['email'])
                    flash('we currently only support .xls files, sorry :/'
                        , 'error')
                    return redirect(url_for('uploads'
                        , o_label=org.label, p_label=project.label
                        , u_label=upload.label, edit='true'))
                
                upload.update(set__filename = 
                        os.path.basename(absolute_filename))
                upload.update(set__uploaded_by = user)

                # enqueue upload-processing
                redis_config = app.config['REDIS_CONFIG']
                use_connection(Redis(redis_config['host'], redis_config['port']
                        , password=redis_config['password']))
                queue = Queue()
                queue.enqueue(process_uploaded_file, upload.id,
                        absolute_filename)
                    
        elif form_type == 'admin':
            # delete the upload
            name = upload.name
            # pull out the upload from the parent project first
            project.update(pull__uploads=upload)
            
            # delete associated entries, remove from system, remove from s3
            deleted_entries_count = utilities.delete_upload(upload
                , session['email'])
            flash('upload "%s" was deleted successfully; %s entries deleted \
                as well' % (name, deleted_entries_count), 'success')
            
            # see if, given this deletion, the dupes are still dupes
            unduped_count = utilities.recheck_duplicate_entries(project)
            if unduped_count:
                flash('%s entries were automatically added to the project \
                    as they are no longer duplicates' % unduped_count, 'info')

            return redirect(url_for('uploads', o_label=org.label
                , p_label=project.label))
        
        else:
            # bad 'form_type'
            abort(404)
       
        try:
            upload.save()
            project.save()
        except:
            app.logger.error('%s experienced an error saving info about the \
                upload %s' % (session['email'], request.form['name']))
            flash('error, make sure names are unique', 'error')
            return redirect(url_for('projects'
                , org_label=upload.project.organization.label
                , project_label=upload.project.label))
        
        return redirect(url_for('uploads'
            , o_label=upload.project.organization.label, p_label=project.label
            , u_label=upload.label))
Ejemplo n.º 22
0
def organizations(org_label):
    ''' show the organizations
    if there's a label included in the route, render that organization alone
    '''
    user = User.objects(email=session['email'])[0]

    if request.method == 'POST':
        orgs = Organization.objects(label=org_label)
        if not orgs:
            abort(404)
        org = orgs[0]
        
        # permission-check
        if org not in user.organizations and not user.admin_rights:
            app.logger.error('%s tried to edit an organization but was \
                denied for want of admin rights' % session['email'])
            abort(404)

        form_type = request.form.get('form_type', '')
        if form_type == 'info':
            if org.name != request.form.get('name', ''):
                name = request.form.get('name', '')
                org.name = name

                orgs = Organization.objects().only('label')
                labels = [o.label for o in orgs]
                org.label = utilities.generate_label(name, labels)
           
            if org.description != request.form.get('description', ''):
                app.logger.info('%s edited the description of %s to %s' % (
                    session['email'], org.name
                    , request.form.get('description', '')))
                org.description = request.form.get('description', '')
            
            if org.location != request.form.get('location', ''):
                app.logger.info('%s edited the location of %s to %s' % (
                    session['email'], org.name
                    , request.form.get('location', '')))
                org.location = request.form.get('location', '')

        elif form_type == 'add_members':
            # push membership
            target = request.form.get('add_member_email', '')
            new_members = User.objects(email=target)
            if not new_members:
                flash('we cannot find "%s", has it been registered?' % \
                    target, 'error')
                return redirect(url_for('organizations', org_label=org.label))

            new_member = new_members[0]
            # already a member?
            if org in new_member.organizations:
                flash('"%s" is already a member of "%s"' % (target, org.name)
                    , 'warning')
                return redirect(url_for('organizations', org_label=org.label))
            
            else:
                # add them
                new_member.update(push__organizations=org)
                flash('successfully added "%s" to "%s"' % (target, org.name)
                    , 'success')
                return redirect(url_for('organizations', org_label=org.label))
        
        elif form_type == 'remove_members':
            # push/pull membership
            target = request.form.get('remove_member_email', '')
            old_members = User.objects(email=target)
            if not old_members:
                flash('we cannot find "%s", has it been registered?' % \
                    target, 'error')
                return redirect(url_for('organizations', org_label=org.label))
            
            old_member = old_members[0]
            # not yet a member?
            if org not in old_member.organizations:
                flash('"%s" is not yet a member of "%s"' % (target, org.name)
                    , 'warning')
                return redirect(url_for('organizations', org_label=org.label))
            else:
                # drop 'em
                old_member.update(pull__organizations=org)
                flash('successfully removed "%s" from %s' % (target, org.name)
                    , 'info')
                return redirect(url_for('organizations', org_label=org.label))

        elif form_type == 'admin':
            # delete the organization; permission-check first
            if not user.admin_rights:
                app.logger.error('%s tried to delete %s but was denied for \
                    want of admin rights' % (session['email'], org.name))
                abort(404)

            # revoke membership first
            members = User.objects(organizations=org)
            for member in members:
                member.update(pull__organizations=org)
            
            # blow away the org itself
            name = org.name
            org.delete()
            app.logger.info('%s deleted %s' % (session['email'], name))
            flash('organization "%s" was deleted' % name, 'success')
            return redirect(url_for('organizations'))
        
        else:
            # bad 'form_type'
            abort(404)
       
        try:
            org.save()
            flash('changes saved successfully', 'success')
        except:
            org.reload()
            app.logger.error('%s experienced an error saving info about %s' % (
                session['email'], request.form['name']))
            flash('Error saving changes, is the name unique?', 'error')
        
        return redirect(url_for('organizations', org_label=org.label
            , edit='true'))

    
    if request.method == 'GET':
        if org_label:
            orgs = Organization.objects(label=org_label)
            if not orgs:
                app.logger.error('%s tried to access an organization that \
                    does not exist' % session['email'])
                flash('Organization "%s" not found, sorry!' % org_label
                    , 'warning')
                return redirect(url_for('organizations'))
            org = orgs[0]

            # permission-check
            if org not in user.organizations and not user.admin_rights:
                app.logger.error('%s tried to access an organization but was \
                    denied for want of admin rights' % session['email'])
                abort(404)

            if request.args.get('edit', '') == 'true':
                users = User.objects(organizations=org)
                return render_template('organization_edit.html'
                    , organization=org, users=users)
            else:
                # get all the members
                users = User.objects(organizations=org)
                return render_template('organization.html', organization=org
                    , users=users)

        if request.args.get('create', '') == 'true':
            # create a new form
            # permissions-check 
            if not user.admin_rights:
                app.logger.error('%s tried to create an organization but was \
                    denied for want of admin rights' % session['email'])
                abort(404)

            # CSRF validation
            token = request.args.get('token', '')
            if not verify_token(token):
                abort(403)

            try:
                org_name = 'org-%s' % utilities.generate_random_string(6)
                new_org = Organization(
                    label = org_name.lower()
                    , name = org_name
                )
                new_org.save() 
                app.logger.info('organization created by %s' % \
                    session['email'])
                flash('organization created; please change the defaults', 
                    'success')
            except:
                app.logger.error('organization creation failed for %s' % \
                    session['email'])
                flash('There was an error in the form, sorry :/', 'error')
                return redirect(url_for('organizations'))
            
            # redirect to the editing screen
            return redirect(url_for('organizations', org_label=new_org.label
                , edit='true'))
        
        # nobody in particular was specified; show em all
        if user.admin_rights:
            orgs = Organization.objects()
        else:
            orgs = user.organizations

        # find the users for each org
        users = {}
        for org in orgs:
            users[org.name] = User.objects(organizations=org)

        return render_template('organizations.html', organizations=orgs
            , users=users)
Ejemplo n.º 23
0
def find_clip(clip, configs):
    ''' find a relevant archival video based on the word's search term
    '''
    # login to vimeo
    client = vimeo.Client(key=configs['vimeo']['consumer_key']
        , secret = configs['vimeo']['consumer_secret']
        , callback = configs['vimeo']['callback_url'])

    # sorting categories; note that 'newest' seemed spammy
    sorting = random.choice(['oldest', 'relevant', 'most_played'
        , 'most_commented', 'most_liked', 'oldest', 'oldest', 'oldest'])

    try:
        # may fail if certain unicode chars come back from vimeo
        # \u2019 (right quotation mark), for instance
        result = json.loads(client.get(
                    'vimeo.videos.search'
                    , query=clip.keyword
                    , page=1
                    , per_page=50
                    , full_response=1
                    , sort=sorting
                ))
    except:
        print ' skipping; vimeo search exception'
        clip.update(set__mp4_url = None)
        return None

    videos = result['videos']['video']
    if not videos:
        print ' skipping; no videos for "%s" in "%s" search' % (clip.keyword
                , sorting)
        clip.update(set__mp4_url = None)
        return None

    # select a video with a short but at least 3 second duration
    durations = [int(v['duration']) for v in videos if int(v['duration']) >= 3]
    if len(durations) > 5:
        durations.sort()
        durations = durations[0:5]

    selected_duration = durations[random.choice(range(0, len(durations)))]

    for v in videos:
        if int(v['duration']) == selected_duration:
            video = v
            break
    print '  %s with sorting "%s" --> vimeo.com/%s' % (clip.keyword, sorting
        , video['id'])

    # pull the vimeo mp4
    vimeo_mp4_url = Scraper.get_vimeo(video['id'])
    if not vimeo_mp4_url:
        print ' skipping; scraper was unable to retrieve an mp4'
        clip.update(set__mp4_url = None)
        return None

    # download the file
    print 'downloading local copy'
    r = requests.get(vimeo_mp4_url)
    tmp_path = '%s/redream-%s.mp4' % (tempfile.gettempdir()
            , generate_random_string(10))
    with open(tmp_path, 'wb') as video_file:
        video_file.write(r.content)

    if int(video['duration']) > 20:
        # crop the video - start at 20-70% and take 5-15% of total
        print 'cropping with ffmpeg'
        start_fraction = (random.random()*(0.7-0.2)) + 0.2
        start = time.strftime('%H:%M:%S'
                , time.gmtime(int(video['duration'])*start_fraction))
        length_fraction = (random.random()*(0.15-0.05)) + 0.05
        length = time.strftime('%H:%M:%S'
                , time.gmtime(int(video['duration'])*length_fraction))
        out_path = '%s/redream-%s.mp4' % (tempfile.gettempdir()
                , generate_random_string(10))
        # envoy command from http://askubuntu.com/a/35645/68373
        try:
            envoy.run('ffmpeg -acodec copy -vcodec copy -ss %s -t %s -i %s %s'
                    % (start, length, tmp_path, out_path))
        except AttributeError:
            print 'skipping; ffmpeg attr error (bad save path?)'
            return None
    else:
        # short source vid, don't crop
        out_path = tmp_path

    # rehost the file on s3
    print 'moving to s3'
    connection = boto.connect_s3(
        aws_access_key_id=configs['aws']['access_key_id']
        , aws_secret_access_key=configs['aws']['secret_access_key'])

    # create bucket if it doesn't exist
    if not connection.lookup(configs['aws']['s3_bucket']):
        bucket = connection.create_bucket(configs['aws']['s3_bucket'])
    else:
        bucket = connection.get_bucket(configs['aws']['s3_bucket'])

    s3_key = S3_Key(bucket)
    s3_key.key = '%s.mp4' % generate_random_string(30)
    s3_key.set_contents_from_filename(out_path)
    s3_key.make_public()

    s3_url = 'http://s3.amazonaws.com/%s/%s' % (configs['aws']['s3_bucket']
        , s3_key.key)

    # delete local copies
    os.unlink(tmp_path)
    if out_path != tmp_path:
        os.unlink(out_path)

    # save into db
    print 'saving to db'
    clip.update(set__mp4_url = s3_url)
    clip.update(set__archive_name = 'vimeo')
    clip.update(set__source_id = video['id'])
    clip.update(set__source_title = video['title'])
    clip.update(set__source_description = video['description'])
    clip.update(set__source_url = video['urls']['url'][0]['_content'])
    clip.update(set__source_owner = video['owner']['username'])
    clip.update(set__source_thumbnail_url = 
            video['thumbnails']['thumbnail'][0]['_content'])

    return True
Ejemplo n.º 24
0
def projects(org_label, project_label):
    ''' project wrangling
    /organizations/aquaya/projects?create=true 
        : create a new project, immediately redirect to editing
    /organizations/aquaya/projects/water-quality?edit=true
        : edit a project; accepts GET or POST
    /organizations/aquaya/projects/water-quality 
        : view a project
    '''
    user = User.objects(email=session['email'])[0]

    orgs = Organization.objects(label=org_label)
    if not orgs:
        flash('Organization "%s" not found, sorry!' % org_label, 'warning')
        return redirect(url_for('organizations'))
    org = orgs[0]

    # permission-check
    if org not in user.organizations and not user.admin_rights:
        app.logger.error('%s tried to view a project but was \
            denied for want of admin rights' % session['email'])
        abort(404)

    if request.method == 'GET':
        if not project_label and request.args.get('create', '') == 'true':
            # create a new project

            # CSRF validation
            token = request.args.get('token', '')
            if not verify_token(token):
                abort(403)

            try:
                name = 'prj-%s' % utilities.generate_random_string(6)
                new_project = Project(
                    creation_time = datetime.datetime.utcnow()
                    , label = name.lower()
                    , name = name
                    , organization = org
                    , update_time = datetime.datetime.utcnow()
                )
                new_project.save()

                org.update(push__projects=new_project)
                app.logger.info('project created by %s' % session['email'])
                flash('project created; please change the defaults', 'success')
            except:
                app.logger.error('project creation failed for %s' % \
                    session['email'])
                flash('There was an error in the form, sorry :/', 'error')
                return redirect(url_for('organizations', org_label=org.label))
            
            # redirect to the editing screen
            return redirect(url_for('projects', org_label=org.label
                , project_label=new_project.label, edit='true'))

        elif not project_label:
            # could show org's projects here but that's visible on the org page
            abort(404)
        
        else:
            # we have a project label
            projects = Project.objects(label=project_label, organization=org)
            if not projects:
                flash('Project "%s" not found, sorry!' % project_label
                    , 'warning')
                return redirect(url_for('organizations', org_label=org.label))
            project = projects[0]

            if request.args.get('edit', '') == 'true':
                # show the editor
                return render_template('project_edit.html', project=project)

            else:
                upload_count = Upload.objects(project=project).count()
                entry_count = Entry.objects(project=project, visible=True
                    , unique=True).count()
                filter_count = Filter.objects(project=project).count()
                graph_count = Graph.objects(project=project).count()
                statistic_count = Statistic.objects(project=project).count()
                report_count = Report.objects(project=project).count()
                schedule_count = Schedule.objects(project=project).count()
                connection_count = Connection.objects(project=project).count()
                comment_count = Comment.objects(project=project).count()
                
                if not entry_count:
                    flash('To get started, connect this project to a data \
                        or manually add some files.', 'info')
                
                return render_template('project.html', project=project
                    , upload_count=upload_count
                    , entry_count=entry_count
                    , filter_count=filter_count
                    , graph_count=graph_count
                    , statistic_count=statistic_count
                    , report_count=report_count
                    , schedule_count=schedule_count
                    , connection_count=connection_count
                    , comment_count=comment_count)

        
    elif request.method == 'POST':
        # we have a project label
        projects = Project.objects(label=project_label, organization=org)
        if not projects:
            abort(404)
        project = projects[0]
        
        form_type = request.form.get('form_type', '')
        if form_type == 'info':
            if project.name != request.form.get('name', ''):
                name = request.form.get('name', '')
                project.name = name

                projects = Project.objects(organization=org).only('label')
                labels = [p.label for p in projects]
                project.label = utilities.generate_label(name, labels)

                project.update_time = datetime.datetime.utcnow()
            
            if project.description != request.form.get('description', ''):
                app.logger.info('%s edited the description of %s to %s' % (
                    session['email'], project.name
                    , request.form.get('description', '')))
                project.description = request.form.get('description', '')
                project.update_time = datetime.datetime.utcnow()
        
        elif form_type == 'admin':
            # delete the project
            # permission-check first
            name = project.name
            (upload_count, entry_count) = utilities.delete_project(project
                , session['email'])
            
            flash('Project "%s" was deleted successfully.  %s uploads and \
                %s entries were deleted as well' % (name, upload_count, 
                    entry_count), 'success')
            return redirect(url_for('organizations', org_label=org.label))
        
        else:
            # bad 'form_type'
            abort(404)
       
        try:
            project.save()
            flash('changes saved successfully', 'success')
        except:
            app.logger.error('%s experienced an error saving info about the \
                project %s' % (session['email'], request.form['name']))
            flash('error, make sure names are unique', 'error')
            return redirect(url_for('organizations', org_label=org.label))
        
        return redirect(url_for('projects', org_label=org.label
            , project_label=project.label))
Ejemplo n.º 25
0
def process_uploaded_file(upload_id, absolute_filename):
    ''' parse uploaded files
    validate schema if data exists in the project
    save entries to mongodb
    save file itself to s3
    delete local copy
    pass status and errors to the upload itself
    '''
    # get handles for some objects
    upload = Upload.objects(id=upload_id)[0]
    project = upload.project
    filename = os.path.basename(absolute_filename)

    # begin parsing the file
    spreadsheet = xlrd.open_workbook(absolute_filename)
    worksheet = spreadsheet.sheet_by_index(0)

    # capture headers at row 0
    original_headers = []
    for col in range(worksheet.ncols):
        original_headers.append(worksheet.cell(0, col).value)

    # convert invalid characters
    upload_header_names = []
    for header in original_headers:
        header = header.replace('.', '__')
        header = header.replace('$', '__')
        upload_header_names.append(header)

    if upload_header_names != original_headers:
        message = {
            'status': 'info'
            , 'message': 'Invalid characters such as "$" and "." were '
            'converted to "__".'
        }
        upload.update(push__worker_messages = message)

    # check if imported file schema is subset of project schema
    # suppose project.ordered_schema is [1,2,3]
    # an upload with schema [1,2] is accepted
    # .. [5,6,7] is rejected
    # .. [1,2,3,4] is accepted with a warning that data from '4' 
    # will be discarded
    if project.ordered_schema:
        existing_header_names = [header.name for header in
            project.ordered_schema]

        discarded_header_names = [h for h in upload_header_names
            if h not in existing_header_names]

        if len(upload_header_names) == len(discarded_header_names):
            # all of the headers are going to be discarded
            # reject this upload
            message = {
                'status': 'error'
                , 'message': 'The file "%s" has nothing in common with the '
                    'existing project data and cannot be uploaded' 
                    % filename
            }
            upload.update(push__worker_messages = message)

            # clear these values to show the upload failed
            upload.update(set__filename = None)
            upload.update(set__uploaded_by = None)

            return False

        if discarded_header_names:
            # some headers will be ignored but the upload can 
            # proceed
            discards = ', '.join('"%s"' % h for h in \
                discarded_header_names)
            message = {
                'status': 'warning'
                , 'message': 'The data in "%s" has some column headings '
                'that are not currently in the project.  Data for the '
                'following headings will not be added to the project: %s.' 
                % (filename, discards)
            }
            upload.update(push__worker_messages = message)
    else:
        # no project.ordered_schema exists yet
        discarded_header_names = []
        existing_header_names = list(upload_header_names)

    # capture entries
    for row in range(worksheet.nrows):
        values = {}
        for col in range(worksheet.ncols):
            if row == 0:
                # skip headers
                continue
            
            # skip headers that were not in the existing schema
            header = upload_header_names[col]
            if header in discarded_header_names:
                continue

            cell = worksheet.cell(row, col)
            # attempt to convert dates to datetime or float
            value = utilities.smart_cast(cell.value)

            values[header] = value

        if values:
            new_entry = Entry(
                project = project
                , upload = upload
                , values = values
            )
            new_entry.save()

    # create a hash of the entry values for later de-duping
    for entry in Entry.objects(upload=upload):
        m = hashlib.md5()
        sorted_headers = list(existing_header_names)
        sorted_headers.sort()
        for header in sorted_headers: 
            if header not in upload_header_names:
                continue
            value = entry.values[header]
            if type(value) == type(u'unicode'):
                m.update(value.encode('utf8'))
            else:
                m.update(str(value))
        entry.update(set__value_hash = m.hexdigest())
    
    if project.ordered_schema:
        # not first upload so check project for duplicate entries
        uniques = Entry.objects(project=project
                , unique=True).only('value_hash')
        unique_hashes = [u['value_hash'] for u in uniques]
        
        # also see if any newly-uploaded entries should be hidden
        hidden_entries = Entry.objects(project=project
                , visible=False).only('value_hash')
        hidden_hashes = [h['value_hash'] for h in hidden_entries]

        upload_entries = Entry.objects(upload=upload)
        dupe_count, hidden_count = 0, 0
        for entry in upload_entries:
            if entry['value_hash'] not in unique_hashes:
                entry.update(set__unique = True)
            else:
                entry.update(set__unique = False)
                dupe_count += 1
            
            if entry['value_hash'] in hidden_hashes:
                entry.update(set__visible = False)
                hidden_count += 1

        if dupe_count:
            message = {
                'status': 'warning'
                , 'message': 'Of the %s entries in the uploaded file, '
                    '%s were duplicates.' % (len(upload_entries), dupe_count)
            }
            upload.update(push__worker_messages = message)

        if hidden_count:
            message = {
                'status': 'warning'
                , 'message': 'Of the %s entries in the uploaded file, '
                    '%s were automatically hidden based on prior edits.' 
                    % (len(upload_entries), hidden_count)
            }
            upload.update(push__worker_messages = message)

    else:
        # no schema, so this is the first upload to the project

        # all entries /should/ be unique..
        # unless there are dupes within the upload
        upload_hashes = []
        dupe_count = 0
        for entry in Entry.objects(upload=upload):
            if entry['value_hash'] not in upload_hashes:
                entry.update(set__unique = True)
            else:
                dupe_count += 1 
                entry.update(set__unique = False)

            upload_hashes.append(entry['value_hash'])

        if dupe_count:
            message = {
                'status': 'warning'
                , 'message': 'There were %s duplicate entries in the'
                    ' uploaded file.' % dupe_count
            }
            upload.update(push__worker_messages = message)
    
        # define a default schema for the project
        ordered_schema = []
        # try to guess the data type for each header
        # take the first entry and base guesses off of this data
        entry = Entry.objects(upload=upload)[0]
        for header in upload_header_names:
            value = utilities.smart_cast(entry.values[header])

            # compare result of casting to other types
            if type(value) == datetime.datetime:
                data_type = 'datetime'
            elif type(value) == float:
                data_type = 'number'
            else:
                data_type = 'string'

            new_header = Header(
                data_type = data_type
                , display = True
                , name = header
                , project = project
                , label = header
            )
            new_header.save()
            ordered_schema.append(new_header)
        
        # save results
        project.update(set__ordered_schema = ordered_schema)
        message = {
            'status': 'warning'
            , 'message': "Please verify the data types in this project's "
                "schema."
        }
        upload.update(push__worker_messages = message)
    
    # send the uploaded file to s3
    connection = boto.connect_s3(
        aws_access_key_id = app.config['AWS']['access_key_id']
        , aws_secret_access_key = app.config['AWS']['secret_access_key'])
    
    bucket_name = '%s-%s' % (app.config['UPLOADS_BUCKET']
        , app.config['AWS']['access_key_id'])
    bucket = connection.create_bucket(bucket_name.lower())

    s3_key = S3_Key(bucket)
    s3_key.key = '%s-%s' % (filename, utilities.generate_random_string(6))
    s3_key.set_contents_from_filename(absolute_filename)

    # save the data
    upload.update(set__extension = filename.rsplit('.', 1)[1])
    upload.update(set__s3_key = s3_key.key)
    upload.update(set__upload_time = datetime.datetime.utcnow())

    upload.update(set__headers = existing_header_names)

    # remove the uploaded file from the local temp location
    os.unlink(absolute_filename)
Ejemplo n.º 26
0
def members(internal_id):
    ''' show the users and their verification/confirmation status
    if there's an email included in the route, render that profile for editing
    '''
    if request.method == 'POST':
        users = User.objects(id=internal_id)
        if not users:
            abort(404)
        user = users[0]

        profile_form_type = request.form.get('profile_form_type', '')
        if profile_form_type == 'info':
            if user.name != request.form.get('name', ''):
                app.logger.info('%s edited the name of %s to %s' % (
                    session['email'], request.form['email']
                    , request.form.get('name', '')))
                user.name = request.form.get('name', '')

            if user.email != request.form.get('email', ''):
                app.logger.info('%s edited the email of %s to %s' % (
                    session['email'], request.form['email']
                    , request.form.get('email', '')))
                user.email = request.form.get('email', '')
            
            if request.form['verification'] == 'verified':
                # check to see if the verification status has changed
                if not user.verified:
                    app.logger.info('%s verified %s' % (session['email']
                        , request.form['email']))
                    
                    # send email to user that they've been verified
                    utilities.send_notification_of_verification(user
                        , request.form.get('email', ''))
                    user.verified = True
                    
                    # create API credentials for the user
                    user.api_id = 'ID' + utilities.generate_random_string(32)
                    user.api_key = utilities.generate_random_string(34)

            elif request.form['verification'] == 'unverified':
                if user.verified:
                    app.logger.info('%s unverified %s' % (session['email']
                        , request.form['email']))
                user.verified = False
           
            if request.form['admin'] == 'admin':
                if not user.admin_rights:
                    app.logger.info('%s gave admin privileges to %s' % (
                        session['email'], request.form['email']))
                user.admin_rights = True

            elif request.form['admin'] == 'normal':
                if user.admin_rights:
                    app.logger.info('%s removed admin privileges from %s' % (
                        session['email'], request.form['email']))
                user.admin_rights = False
        
        elif profile_form_type == 'account':
            # delete the user
            # first pull out the user from the relevant orgs
            orgs = user.organizations
            for org in orgs:
                org.update(pull__users=user)
            
            # delete the user itself
            user_email = user.email
            user.delete()
            app.logger.info('%s deleted %s' % (session['email'], user_email))
            flash('user deleted', 'success')
            return redirect(url_for('members'))
        
        else:
            # bad 'profile_form_type'
            abort(404)
       
        try:
            user.save()
            flash('changes saved successfully', 'success')
        except:
            app.logger.error('%s experienced an error saving info about %s' % (
                session['email'], request.form['email']))
            flash('error saving changes, sorry /:')
        
        return redirect(url_for('members', internal_id=user.id))

    
    if request.method == 'GET':
        if internal_id:
            user = User.objects(id=internal_id)
            if not user:
                abort(404)
            user = user[0]
            return render_template('members_edit.html'
                , user=user)
        
        # nobody in particular was specified; show em all
        users = User.objects()
        return render_template('members.html', users=users)
Ejemplo n.º 27
0
def statistics(org_label, project_label, statistic_label):
    ''' creating statistics for various purposes
    /organizations/aquaya/projects/water-quality/statistics
        : viewing a list of this project's statistics
    /organizations/aquaya/projects/water-quality/statistics?create=true
        : create a new statistic config, immediately redirect to editing
    /organizations/aquaya/projects/water-quality/statistics/mean-pH
        : view a statistic
    /organizations/aquaya/projects/water-quality/statistics/mean-pH?edit=true
        : edit a statistic; accepts GET or POST
    '''
    user = User.objects(email=session['email'])[0]
    
    orgs = Organization.objects(label=org_label)
    if not orgs:
        flash('Organization "%s" not found, sorry!' % org_label, 'warning')
        return redirect(url_for('organizations'))
    org = orgs[0]

    # permission-check
    if org not in user.organizations and not user.admin_rights:
        app.logger.error('%s tried to view a project but was \
            denied for want of admin rights' % session['email'])
        abort(404)
    
    # find the project
    projects = Project.objects(label=project_label, organization=org) 
    if not projects:
        flash('Project "%s" not found, sorry!' % project_label, 'warning')
        return redirect(url_for('organizations', org_label=org.label))
    project = projects[0]

    if request.method == 'POST':
        # we have a statistic_label
        statistics = Statistic.objects(label=statistic_label, project=project)
        if not statistics:
            abort(404)
        statistic = statistics[0]

        form_type = request.form.get('form_type', '')
        if form_type == 'info':
            if statistic.name != request.form.get('name', ''):
                name = request.form.get('name', '')
                statistic.name = name

                statistics = Statistic.objects(project=project).only('label')
                labels = [s.label for s in statistics]
                statistic.label = utilities.generate_label(name, labels)

            statistic.description = request.form.get('description', '')
            statistic.statistic_type = request.form.get('statistic_type', '')

            # header is of the form 'header_id__4abcd0012'
            header = request.form.get('header', '')
            header_id = header.split('header_id__')[1]
            header = Header.objects(id=header_id)[0]
            statistic.header = header


        elif form_type == 'create-table':
            compute_multiple = request.form.get('compute_multiple', '')
            if compute_multiple == 'true':
                statistic.pivot = True
            else:
                statistic.pivot = False

            header_id = request.form.get('compute_multiple_header', '')
            headers = Header.objects(project=project, id=header_id)
            if not headers:
                abort(404)
            statistic.pivot_header = headers[0]
            
            # the name of the select with the values is based on header id
            values = request.form.getlist('header_unique_values')
            escaped_values = [bleach.clean(v) for v in values]
            statistic.pivot_values = escaped_values

            statistic.save()
            
            flash('Table settings saved successfully', 'success')
            return redirect(url_for('statistics'
                , org_label=org.label
                , project_label=project.label
                , statistic_label=statistic.label
                , edit='true'))

        
        elif form_type == 'filters':
            # extract the 'any filters' vs 'all' distinction
            filter_settings = request.form.get('apply_any_filters', '')
            if filter_settings == 'true':
                statistic.apply_any_filters = True
            else:
                statistic.apply_any_filters = False
            statistic.save()
            
            # attach filter to statistic
            requested_filter_ids = request.form.getlist('filters')
            attached_filters = []
            for requested_id in requested_filter_ids:
                prefix, filter_id = requested_id.split('__')
                filters = Filter.objects(id=filter_id)
                if not filters:
                    abort(404)
                attached_filters.append(filters[0])

            statistic.update(set__filters = attached_filters)

            return redirect(url_for('statistics', org_label=org.label
                , project_label=project.label
                , statistic_label=statistic.label, edit='true'))
       

        elif form_type == 'admin':
            # delete the statistic
            name = statistic.name
            utilities.delete_statistic(statistic, session['email'])
            flash('statistic "%s" was deleted successfully' % name, 'success')
            return redirect(url_for('statistics', org_label=org.label
                , project_label=project.label))
        
        else:
            # bad 'form_type'
            abort(404)
       
        try:
            statistic.save()
            flash('Changes saved successfully', 'success')
            return redirect(url_for('statistics', org_label=org.label
                , project_label=project.label
                , statistic_label=statistic.label, edit='true'))
        except:
            app.logger.error('%s experienced an error saving info about %s' % (
                session['email'], request.form['name']))
            flash('Error saving changes -- make sure statistic names are \
                unique.', 'error')
            return redirect(url_for('statistics', org_label=org.label
                , project_label=project.label, statistic_label=statistic_label
                , edit='true'))
        
    
    if request.method == 'GET':
        if statistic_label:
            statistics = Statistic.objects(label=statistic_label
                , project=project)
            if not statistics:
                app.logger.error('%s tried to access a statistic that does \
                    not exist' % session['email'])
                flash('Statistic "%s" not found, sorry!' % statistic_label
                    , 'warning')
                return redirect(url_for('projects', org_label=org.label
                    , project_label=project.label))
            statistic = statistics[0]

            if request.args.get('edit', '') == 'true':
                # valid statistic types
                statistic_types = constants.statistic_types

                available_filters = Filter.objects(project=project)

                # find all string-type headers
                # these are available for compute_multiple
                string_type_headers = []
                for header in project.ordered_schema:
                    if header.display and header.data_type == 'string':
                        string_type_headers.append(header)

                return render_template('statistic_edit.html'
                    , statistic = statistic
                    , statistic_types = statistic_types
                    , available_filters = available_filters
                    , string_type_headers = string_type_headers)
            else:
                # apply the filters and show matches
                conditions = {
                    'project': project
                    , 'unique': True
                    , 'visible': True
                }
                entries = Entry.objects(**conditions)
                
                # sort the entries based on project defaults
                entries = utilities.sort_entries(project, entries)

                project_count = len(entries)
                
                for filter in statistic.filters:
                    if not filter.comparison:
                        flash('filter "%s" does not have a complete \
                            definition' % filter.name, 'warning')
                        return render_template('statistic.html'
                            , statistic=statistic
                            , entries=entries
                            , project_count=project_count)
                
                # apply relevant filters and then computes the statistic
                if statistic.pivot:
                    # returns dict keyed by the statistic's pivot_values
                    result = utilities.compute_pivot_statistic(statistic
                        , entries)
                else:
                    # returns a single result
                    result = utilities.compute_statistic(statistic, entries)

                # find matches to the applied filters
                matches = utilities.apply_filters(statistic.filters, entries
                    , statistic.apply_any_filters)
                total_matches = len(matches)
                
                # pagination
                entries_per_page = 10
                pages = utilities.calculate_pages(len(matches)
                    , entries_per_page=entries_per_page)

                # validate the requested page
                current_page = utilities.validate_page_request(
                    request.args.get('page', 1), pages)

                # manually paginate
                start_index = entries_per_page * (current_page - 1)
                end_index = start_index + entries_per_page
                paginated_matches = matches[start_index:end_index]

                return render_template('statistic.html'
                    , statistic=statistic
                    , entries=paginated_matches
                    , total_matches = total_matches
                    , project_count=project_count
                    , result=result
                    , current_page = current_page
                    , number_of_pages = pages)


        if request.args.get('create', '') == 'true':
            # create a new statistic

            # CSRF validation
            token = request.args.get('token', '')
            if not verify_token(token):
                abort(403)

            try:
                statistic_name = 'stat-%s' \
                    % utilities.generate_random_string(6)
                new_statistic= Statistic(
                    creation_time = datetime.datetime.utcnow()
                    , creator = user
                    , label = statistic_name.lower()
                    , project = project
                    , name = statistic_name
                )
                new_statistic.save() 
                app.logger.info('statistic created by %s' % session['email'])
                flash('statistic created; please change the defaults'
                    , 'success')
            except:
                app.logger.error('statistic creation failed for %s' % \
                    session['email'])
                flash('There was an error, sorry :/', 'error')
                return redirect(url_for('projects', org_label=org.label
                    , project=project.label))
            
            # redirect to the editing screen
            return redirect(url_for('statistics', org_label=org.label
                , project_label=project.label
                , statistic_label=new_statistic.label, edit='true'))
        
        # no statistic in particular was specified; show em all
        statistics = Statistic.objects(project=project)

        # calculate the results of these statistics
        statistic_results = {}
        unique_entries = Entry.objects(project=project, unique=True
            , visible=True)
        for statistic in statistics:
            statistic_results[statistic.name] = \
                utilities.compute_statistic(statistic, unique_entries)

        return render_template('project_statistics.html', project=project
            , statistics=statistics, statistic_results=statistic_results)
Ejemplo n.º 28
0
def questions(org_label, form_label, question_label):
    ''' show the questions for a given form
    if there's a label included in the route, render that question alone
    '''
    user = User.objects(email=session['email'])[0]

    # find the relevant organization
    orgs = Organization.objects(label=org_label)
    if not orgs:
        app.logger.error('%s tried to access an organization that does not \
            exist' % session['email'])
        flash('Organization "%s" not found, sorry!' % org_label, 'warning')
        return redirect(url_for('organizations'))
    org = orgs[0]

    # permission-check
    if org not in user.organizations and not user.admin_rights:
        app.logger.error('%s tried to access an organization but was denied \
            for want of admin rights' % session['email'])
        abort(404)

    forms = Form.objects(label=form_label, organization=org)
    if not forms:
        app.logger.error('%s tried to access a form that does not \
            exist' % session['email'])
        flash('Form "%s" does not exist, sorry!' % form_label, 'warning')
        return redirect(url_for('organizations', org_label=org_label))
    form = forms[0]

    if request.method == 'POST':
        # capture the type of modifications
        form_type = request.form.get('form_type', '')

        if not question_label and form_type == 'question_ordering':
            # reordering the list of questions
            # get the json-encoded question id list
            sorted_question_ids = json.loads(
                request.form.get('sorted_questions', ''))

            # get the full objects as the model wants references
            full_questions = []
            for question_id in sorted_question_ids:
                question = Question.objects(id=question_id)[0]
                full_questions.append(question)

            form.update(set__questions=full_questions)

            return jsonify({'status': 'success'})

        elif not question_label:
            abort(404)

        questions = Question.objects(label=question_label, form=form)
        if not questions:
            abort(404)
        question = questions[0]

        if form_type == 'info':
            name = request.form.get('name', '')
            question.name = name
            question.label = str(escape(name).replace(' ', '-')).lower()
            question.description = request.form.get('description', '')

            # returns to question editing, unlike other form types
            try:
                question.save()
                flash('Question info saved successfully', 'success')
            except:
                question.reload()
                app.logger.error('%s experienced an error saving info \
                    about question "%s"' %
                                 (session['email'], request.form['name']))
                flash('Error saving changes, are the names unique?', 'error')

            return redirect(
                url_for('questions',
                        org_label=org.label,
                        form_label=form.label,
                        question_label=question.label,
                        edit='true'))

        elif form_type == 'prompt':
            # save response type
            question.response_type = request.form.get('response_type', '')

            # edit the prompt
            prompt_type = request.form.get('specify_prompt_type', '')
            question.prompt_type = prompt_type

            # check if a file has already been attached to this question
            audio_file_exists = request.form.get('audio_file_exists', '')

            if prompt_type == 'text_prompt':
                question.text_prompt = \
                    request.form.get('text_prompt', '').strip()
                question.text_prompt_language = \
                    request.form.get('text_prompt_language', '')

            elif prompt_type == 'audio_file' and audio_file_exists != 'true':
                # store in local dir then move to S3 and save key
                audio_file = request.files.get('audio_file')
                if not audio_file:
                    flash('Please specify a file.', 'error')
                    return redirect(
                        url_for('questions',
                                org_label=org.label,
                                form_label=form.label,
                                question_label=question.label,
                                edit='true'))
                try:
                    filename = uploaded_data.save(audio_file)
                    absolute_filename = uploaded_data.path(filename)
                except UploadNotAllowed:
                    flash('This file type is not allowed, sorry.', 'error')
                    return redirect(
                        url_for('questions',
                                org_label=org.label,
                                form_label=form.label,
                                question_label=question.label,
                                edit='true'))

                else:
                    # send to S3
                    access_key = app.config['AWS']['access_key_id']
                    secret = app.config['AWS']['secret_access_key']
                    connection = boto.connect_s3(aws_access_key_id=access_key,
                                                 aws_secret_access_key=secret)

                    bucket_name = 'ivrhub-prompts-%s' % \
                        app.config['AWS']['access_key_id']
                    # creates bucket if it doesn't already exist
                    bucket = connection.create_bucket(bucket_name.lower())

                    s3_key = S3_Key(bucket)
                    # twilio requires the .mp3 suffix
                    s3_key.key = '%s.mp3' % \
                        utilities.generate_random_string(24)
                    s3_key.set_contents_from_filename(absolute_filename)
                    s3_key.make_public()
                    # generate public url with a long expiry
                    s3_url = s3_key.generate_url(5 * 365 * 24 * 60 * 60,
                                                 query_auth=False)

                    # save the key and url
                    question.audio_filename = filename
                    question.s3_key = s3_key.key
                    question.s3_url = s3_url

                    # remove from local filesystem
                    os.unlink(absolute_filename)

            elif prompt_type == 'audio_url':
                question.audio_url = request.form.get('audio_url', '').strip()

        elif form_type == 'admin':
            # blow away the question itself
            name = question.name
            utilities.delete_question(question)
            app.logger.info('%s deleted %s' % (session['email'], name))
            flash('Question "%s" was deleted.' % name, 'success')
            return redirect(
                url_for('questions',
                        org_label=org.label,
                        form_label=form.label))

        else:
            # bad 'form_type'
            abort(404)

        try:
            question.save()
            flash('Changes to this question were saved successfully',
                  'success')
        except:
            question.reload()
            app.logger.error('%s experienced an error saving info \
                about question "%s"' %
                             (session['email'], request.form['name']))
            flash('Error saving changes, are the names unique?', 'error')

        return redirect(
            url_for('questions', org_label=org.label, form_label=form.label))

    if request.method == 'GET':
        if question_label:
            questions = Question.objects(label=question_label, form=form)
            if not questions:
                abort(404)
            question = questions[0]

            if request.args.get('edit', '') == 'true':
                return render_template('question_edit.html', question=question)
            else:
                return render_template('question.html', question=question)

        if request.args.get('create', '') == 'true':
            # create a new question

            # CSRF validation:
            token = request.args.get('token', '')
            if not verify_token(token):
                app.logger.error('organization-creation CSRF attempt on %s' %
                                 session['email'])
                abort(403)

            try:
                question_name = 'qst-%s' % utilities.generate_random_string(6)
                new_question = Question(label=question_name.lower(),
                                        form=form,
                                        name=question_name)
                new_question.save()

                # attach to form
                form.update(push__questions=new_question)

                app.logger.info('question created by %s' % session['email'])
                flash('Question created; please change the defaults', 'info')
                # redirect to the editing screen
                return redirect(
                    url_for('questions',
                            org_label=org_label,
                            form_label=form.label,
                            question_label=new_question.label,
                            edit='true'))
            except:
                app.logger.error('question creation failed for %s' % \
                    session['email'])
                flash('There was an error in the form, sorry :/', 'error')
                return redirect(
                    url_for('questions',
                            org_label=org_label,
                            form_label=form.label))

        else:
            # no question in particular was specified; show em all
            return render_template('form_questions.html', form=form)
Ejemplo n.º 29
0
def forms(org_label, form_label):
    ''' show the forms
    if there's a label included in the route, render that form alone
    '''
    user = User.objects(email=session['email'])[0]

    # find the relevant organization
    orgs = Organization.objects(label=org_label)
    if not orgs:
        app.logger.error('%s tried to access an organization that does not \
            exist' % session['email'])
        flash('Organization "%s" not found, sorry!' % org_label, 'warning')
        return redirect(url_for('organizations'))
    org = orgs[0]

    # permission-check
    if org not in user.organizations and not user.admin_rights:
        app.logger.error('%s tried to access an organization but was denied \
            for want of admin rights' % session['email'])
        abort(404)

    if request.method == 'POST':
        if not form_label:
            abort(404)

        forms = Form.objects(label=form_label, organization=org)
        if not forms:
            app.logger.error('%s tried to access a form that does not \
                exist' % session['email'])
            flash('Form "%s" does not exist, sorry!' % form_label, 'warning')
            return redirect(url_for('forms', org_label=org_label))
        form = forms[0]

        form_type = request.form.get('form_type', '')

        if form_type == 'info':
            name = request.form.get('name', '')
            form.name = name
            form.label = str(escape(name).replace(' ', '-')).lower()

            form.description = request.form.get('description', '')

        elif form_type == 'admin':
            # blow away the form itself
            name = form.name
            utilities.delete_form(form)
            app.logger.info('%s deleted %s' % (session['email'], name))
            flash('form "%s" was deleted' % name, 'success')
            return redirect(url_for('organizations', org_label=org.label))

        else:
            # bad 'form_type'
            abort(404)

        try:
            form.save()
            flash('Changes to this form were saved successfully', 'success')
            return redirect(
                url_for('forms', org_label=org_label, form_label=form.label))
        except:
            form.reload()
            app.logger.error('%s experienced an error saving info about form \
                "%s"' % (session['email'], request.form['name']))
            flash('Error saving changes, sorry.  Is the name unique?', 'error')
            return redirect(
                url_for('forms', org_label=org_label, form_label=form_label))

    if request.method == 'GET':
        if form_label:
            forms = Form.objects(label=form_label, organization=org)
            if not forms:
                app.logger.error('%s tried to access a form that does not \
                    exist' % session['email'])
                flash('Form "%s" does not exist, sorry!' % form_label,
                      'warning')
                return redirect(url_for('organizations', org_label=org_label))
            form = forms[0]

            if request.args.get('edit', '') == 'true':
                return render_template('form_edit.html', form=form)

            else:
                # count the number of entities
                question_count = Question.objects(form=form).count()
                response_count = Response.objects(form=form).count()
                return render_template('form.html',
                                       form=form,
                                       question_count=question_count,
                                       response_count=response_count)

        if request.args.get('create', '') == 'true':
            # create a new form

            # CSRF validation:
            token = request.args.get('token', '')
            if not verify_token(token):
                app.logger.error('organization-creation CSRF attempt on %s' %
                                 session['email'])
                abort(403)

            try:
                form_name = 'form-%s' % utilities.generate_random_string(6)
                new_form = Form(
                    calling_code=utilities.generate_calling_code(6),
                    label=form_name.lower(),
                    organization=org,
                    name=form_name)
                new_form.save()
                app.logger.info('form created by %s' % session['email'])
                flash('Form created; please change the defaults', 'success')
                # redirect to the editing screen
                return redirect(
                    url_for('forms',
                            org_label=org_label,
                            form_label=new_form.label,
                            edit='true'))
            except:
                app.logger.error('form creation failed for %s' % \
                    session['email'])
                flash('There was an error in the form, sorry :/', 'error')
                return redirect(url_for('forms', org_label=org_label))

        # nobody in particular was specified; punt for now
        abort(404)
        '''
Ejemplo n.º 30
0
def reports(org_label, project_label, report_label):
    ''' creating reports
    /organizations/aquaya/projects/water-quality/reports
        : viewing a list of this project's reports
    /organizations/aquaya/projects/water-quality/reports?create=true
        : create a new report config, immediately redirect to editing
    /organizations/aquaya/projects/water-quality/reports/submitters
        : view a report
    /organizations/aquaya/projects/water-quality/reports/submitters?edit=true
        : edit a report; accepts GET or POST
    '''
    user = User.objects(email=session['email'])[0]
    
    orgs = Organization.objects(label=org_label)
    if not orgs:
        flash('Organization "%s" not found, sorry!' % org_label, 'warning')
        return redirect(url_for('organizations'))
    org = orgs[0]

    # permission-check
    if org not in user.organizations and not user.admin_rights:
        app.logger.error('%s tried to view a project but was \
            denied for want of admin rights' % session['email'])
        abort(404)
    
    # find the project
    projects = Project.objects(label=project_label, organization=org) 
    if not projects:
        flash('Project "%s" not found, sorry!' % project_label, 'warning')
        return redirect(url_for('organizations', org_label=org.label))
    project = projects[0]

    if request.method == 'POST':
        # we have a report_label
        reports = Report.objects(label=report_label, project=project)
        if not reports:
            abort(404)
        report = reports[0]

        form_type = request.form.get('form_type', '')
        if form_type == 'info':
            if report.name != request.form.get('name', ''):
                name = request.form.get('name', '')
                report.update(set__name = name)

                reports = Report.objects(project=project).only('label')
                labels = [r.label for r in reports]
                report.update(set__label = utilities.generate_label(name
                    , labels))

            report.update(set__description = request.form.get('description'
                , ''))

            report.reload()

            flash('Report details saved successfully.', 'success')
            return redirect(url_for('reports', org_label=org.label
                , project_label=project.label, report_label=report.label
                , edit='true'))
        
        elif form_type == 'components':
            # attach components to report
            # 'items' is of the form graph_id__4abcd00123 or statistic_id..
            # convert these to full objects
            items = request.form.getlist('items')
            components = []
            for item in items:
                prefix, item_id = item.split('__')
                item_type = prefix.split('_')[0]

                if item_type == 'graph':
                    graphs = Graph.objects(id=item_id)
                    if not graphs:
                        abort(404)
                    components.append(graphs[0])
                
                elif item_type == 'statistic':
                    statistics = Statistic.objects(id=item_id)
                    if not statistics:
                        abort(404)
                    components.append(statistics[0])
    
            report.update(set__components=components)

            flash('Report components saved successfully.', 'success')
            
            url = url_for('reports', org_label=org.label
                , project_label=project.label, report_label=report.label
                , edit='true')
            return redirect(url + '#components')
       

        elif form_type == 'admin':
            # delete the report
            name = report.name
            report.delete()
            app.logger.info('%s deleted report "%s"' % \
                (session['email'], name))
            flash('report "%s" was deleted successfully' % name, 'success')
            return redirect(url_for('reports', org_label=org.label
                , project_label=project.label))
        
        else:
            # bad 'form_type'
            abort(404)
       
        
    
    if request.method == 'GET':
        if report_label:
            reports = Report.objects(label=report_label, project=project)
            if not reports:
                app.logger.error('%s tried to access a report that does \
                    not exist' % session['email'])
                flash('Report "%s" not found, sorry!' % report_label
                    , 'warning')
                return redirect(url_for('projects', org_label=org.label
                    , project_label=project.label))
            report = reports[0]

            if request.args.get('edit', '') == 'true':
                # extract the component names for js typeahead
                available_graphs = Graph.objects(project=project)
                available_statistics = Statistic.objects(project=project)

                return render_template('report_edit.html'
                    , report=report
                    , available_graphs = available_graphs
                    , available_statistics = available_statistics)


            elif request.args.get('preview', '') == 'true':
                # create pdf preview of the report
                # calculate all statistics and graph datasets
                items = _generate_report_items(report)
                
                # get the current time
                current_time = datetime.datetime.utcnow()

                return render_template('report_preview.html', report=report
                    , items=items, current_time=current_time)

            
            elif request.args.get('render', '') == 'true':
                ''' create PDF versions of the preview
                also send them to s3
                '''

                # CSRF validation
                token = request.args.get('token', '')
                if not verify_token(token):
                    abort(403)

                utilities.create_rendering(report)
                
                flash('The report is now being rendered, this process may \
                    take a few moments.  Check the table below for \
                    updates', 'info')
                return redirect(url_for('reports', org_label=org.label
                    , project_label=project.label, report_label=report.label))

            else:
                # find all relevant renderings
                renderings = Rendering.objects(report=report).order_by(
                    '-creation_time')

                return render_template('report.html', report=report
                    , renderings=renderings)

        if request.args.get('create', '') == 'true':
            # create a new report

            # CSRF validation
            token = request.args.get('token', '')
            if not verify_token(token):
                abort(403)

            try:
                report_name = 'report-%s' \
                    % utilities.generate_random_string(6)
                new_report= Report(
                    creation_time = datetime.datetime.utcnow()
                    , creator = user
                    , label = report_name.lower()
                    , project = project
                    , name = report_name
                    , public_label = utilities.generate_random_string(24)
                )
                new_report.save() 
                app.logger.info('report created by %s' % session['email'])
                flash('Report created, please change the default values.'
                    , 'success')
            except:
                app.logger.error('report creation failed for %s' % \
                    session['email'])
                flash('There was an error, sorry :/', 'error')
                return redirect(url_for('projects', org_label=org.label
                    , project=project.label))
            
            # redirect to the editing screen
            return redirect(url_for('reports', org_label=org.label
                , project_label=project.label
                , report_label=new_report.label, edit='true'))
        
        # no statistic in particular was specified; show em all
        reports = Report.objects(project=project)

        return render_template('project_reports.html', project=project
            , reports=reports)
Ejemplo n.º 31
0
def questions(org_label, form_label, question_label):
    ''' show the questions for a given form
    if there's a label included in the route, render that question alone
    '''
    user = User.objects(email=session['email'])[0]
    
    # find the relevant organization
    orgs = Organization.objects(label=org_label)
    if not orgs:
        app.logger.error('%s tried to access an organization that does not \
            exist' % session['email'])
        flash('Organization "%s" not found, sorry!' % org_label, 'warning')
        return redirect(url_for('organizations'))
    org = orgs[0]

    # permission-check
    if org not in user.organizations and not user.admin_rights:
        app.logger.error('%s tried to access an organization but was denied \
            for want of admin rights' % session['email'])
        abort(404)
    
    forms = Form.objects(label=form_label, organization=org)
    if not forms:
        app.logger.error('%s tried to access a form that does not \
            exist' % session['email'])
        flash('Form "%s" does not exist, sorry!' % form_label
            , 'warning')
        return redirect(url_for('organizations', org_label=org_label))
    form = forms[0]

    if request.method == 'POST':
        # capture the type of modifications
        form_type = request.form.get('form_type', '')

        if not question_label and form_type == 'question_ordering':
            # reordering the list of questions
            # get the json-encoded question id list
            sorted_question_ids = json.loads(
                request.form.get('sorted_questions', ''))
            
            # get the full objects as the model wants references
            full_questions = []
            for question_id in sorted_question_ids:
                question = Question.objects(id=question_id)[0]
                full_questions.append(question)
            
            form.update(set__questions=full_questions)

            return jsonify({'status': 'success'})

        elif not question_label:
            abort(404)

        questions = Question.objects(label=question_label, form=form)
        if not questions:
            abort(404)
        question = questions[0]

        if form_type == 'info':
            name = request.form.get('name', '')
            question.name = name
            question.label = str(escape(name).replace(' ', '-')).lower()
            question.description = request.form.get('description', '')
            
            # returns to question editing, unlike other form types
            try:
                question.save()
                flash('Question info saved successfully', 'success')
            except:
                question.reload()
                app.logger.error('%s experienced an error saving info \
                    about question "%s"' % (
                    session['email'], request.form['name']))
                flash('Error saving changes, are the names unique?', 'error')
            
            return redirect(url_for('questions', org_label=org.label
                , form_label=form.label, question_label=question.label
                , edit='true'))

        elif form_type == 'prompt':
            # save response type
            question.response_type = request.form.get('response_type', '')

            # edit the prompt
            prompt_type = request.form.get('specify_prompt_type', '')
            question.prompt_type = prompt_type
            
            # check if a file has already been attached to this question
            audio_file_exists = request.form.get('audio_file_exists', '')

            if prompt_type == 'text_prompt':
                question.text_prompt = \
                    request.form.get('text_prompt', '').strip()
                question.text_prompt_language = \
                    request.form.get('text_prompt_language', '')

            elif prompt_type == 'audio_file' and audio_file_exists != 'true':
                # store in local dir then move to S3 and save key
                audio_file = request.files.get('audio_file')
                if not audio_file:
                    flash('Please specify a file.', 'error')
                    return redirect(url_for('questions', org_label=org.label
                        , form_label=form.label, question_label=question.label
                        , edit='true'))
                try:
                    filename = uploaded_data.save(audio_file)
                    absolute_filename = uploaded_data.path(filename)
                except UploadNotAllowed:
                    flash('This file type is not allowed, sorry.', 'error')
                    return redirect(url_for('questions', org_label=org.label
                        , form_label=form.label, question_label=question.label
                        , edit='true'))
                
                else:
                    # send to S3
                    access_key = app.config['AWS']['access_key_id']
                    secret = app.config['AWS']['secret_access_key']
                    connection = boto.connect_s3(
                        aws_access_key_id=access_key
                        , aws_secret_access_key=secret)

                    bucket_name = 'ivrhub-prompts-%s' % \
                        app.config['AWS']['access_key_id']
                    # creates bucket if it doesn't already exist
                    bucket = connection.create_bucket(bucket_name.lower())

                    s3_key = S3_Key(bucket)
                    # twilio requires the .mp3 suffix
                    s3_key.key = '%s.mp3' % \
                        utilities.generate_random_string(24)
                    s3_key.set_contents_from_filename(absolute_filename)
                    s3_key.make_public()
                    # generate public url with a long expiry
                    s3_url = s3_key.generate_url(5*365*24*60*60
                        , query_auth=False)

                    # save the key and url
                    question.audio_filename = filename
                    question.s3_key = s3_key.key
                    question.s3_url = s3_url

                    # remove from local filesystem
                    os.unlink(absolute_filename)

            elif prompt_type == 'audio_url':
                question.audio_url = request.form.get('audio_url', '').strip()
                

        elif form_type == 'admin':
            # blow away the question itself
            name = question.name
            utilities.delete_question(question)
            app.logger.info('%s deleted %s' % (session['email'], name))
            flash('Question "%s" was deleted.' % name, 'success')
            return redirect(url_for('questions', org_label=org.label
                , form_label=form.label))
        
        else:
            # bad 'form_type'
            abort(404)
        
        try:
            question.save()
            flash('Changes to this question were saved successfully'
                , 'success')
        except:
            question.reload()
            app.logger.error('%s experienced an error saving info \
                about question "%s"' % (
                session['email'], request.form['name']))
            flash('Error saving changes, are the names unique?', 'error')
        
        return redirect(url_for('questions', org_label=org.label
            , form_label=form.label))
       
    
    if request.method == 'GET':
        if question_label:
            questions = Question.objects(label=question_label, form=form)
            if not questions:
                abort(404)
            question = questions[0]

            if request.args.get('edit', '') == 'true':
                return render_template('question_edit.html', question=question)
            else:
                return render_template('question.html', question=question)

        if request.args.get('create', '') == 'true':
            # create a new question

            # CSRF validation:
            token = request.args.get('token', '')
            if not verify_token(token):
                app.logger.error('organization-creation CSRF attempt on %s' %
                        session['email'])
                abort(403)

            try:
                question_name = 'qst-%s' % utilities.generate_random_string(6)
                new_question = Question(
                    label = question_name.lower()
                    , form = form
                    , name = question_name
                )
                new_question.save()
                
                # attach to form
                form.update(push__questions=new_question)


                app.logger.info('question created by %s' % session['email'])
                flash('Question created; please change the defaults', 'info')
                # redirect to the editing screen
                return redirect(url_for('questions', org_label=org_label
                    , form_label=form.label, question_label=new_question.label
                    , edit='true'))
            except:
                app.logger.error('question creation failed for %s' % \
                    session['email'])
                flash('There was an error in the form, sorry :/', 'error')
                return redirect(url_for('questions', org_label=org_label
                    , form_label=form.label))
        
        else:
            # no question in particular was specified; show em all
            return render_template('form_questions.html', form=form)
Ejemplo n.º 32
0
def organizations(org_label):
    ''' show the organizations
    if there's a label included in the route, render that organization alone
    '''
    user = User.objects(email=session['email'])[0]

    if request.method == 'POST':
        orgs = Organization.objects(label=org_label)
        if not orgs:
            abort(404)
        org = orgs[0]

        # permission-check
        if org not in user.organizations and not user.admin_rights:
            app.logger.error('%s tried to edit an organization but was \
                denied for want of admin rights' % session['email'])
            abort(404)

        form_type = request.form.get('form_type', '')
        if form_type == 'info':
            if org.name != request.form.get('name', ''):
                app.logger.info(
                    '%s edited the name of %s to %s' %
                    (session['email'], org.name, request.form.get('name', '')))
                name = request.form.get('name', '')
                org.name = name
                org.label = str(escape(name).replace(' ', '-')).lower()

            if org.description != request.form.get('description', ''):
                app.logger.info('%s edited the description of %s to %s' %
                                (session['email'], org.name,
                                 request.form.get('description', '')))
                org.description = request.form.get('description', '')

            if org.location != request.form.get('location', ''):
                app.logger.info('%s edited the location of %s to %s' %
                                (session['email'], org.name,
                                 request.form.get('location', '')))
                org.location = request.form.get('location', '')

        elif form_type == 'add_members':
            # push membership
            target = request.form.get('add_member_email', '')
            new_members = User.objects(email=target)
            if not new_members:
                flash('we cannot find "%s", has it been registered?' % \
                    target, 'error')
                return redirect(url_for('organizations', org_label=org.label))

            new_member = new_members[0]
            # already a member?
            if org in new_member.organizations:
                flash('"%s" is already a member of "%s"' % (target, org.name),
                      'warning')
                return redirect(url_for('organizations', org_label=org.label))

            else:
                # add them
                new_member.update(push__organizations=org)
                flash('successfully added "%s" to "%s"' % (target, org.name),
                      'success')
                return redirect(url_for('organizations', org_label=org.label))

        elif form_type == 'remove_members':
            # push/pull membership
            target = request.form.get('remove_member_email', '')
            old_members = User.objects(email=target)
            if not old_members:
                flash('we cannot find "%s", has it been registered?' % \
                    target, 'error')
                return redirect(url_for('organizations', org_label=org.label))

            old_member = old_members[0]
            # not yet a member?
            if org not in old_member.organizations:
                flash('"%s" is not yet a member of "%s"' % (target, org.name),
                      'warning')
                return redirect(url_for('organizations', org_label=org.label))
            else:
                # drop 'em
                old_member.update(pull__organizations=org)
                flash('successfully removed "%s" from %s' % (target, org.name),
                      'info')
                return redirect(url_for('organizations', org_label=org.label))

        elif form_type == 'admin':
            # delete the organization; permission-check first
            if not user.admin_rights:
                app.logger.error('%s tried to delete %s but was denied for \
                    want of admin rights' % (session['email'], org.name))
                abort(404)

            # revoke membership first
            members = User.objects(organizations=org)
            for member in members:
                member.update(pull__organizations=org)

            # delete all associated forms
            forms = Form.objects(organization=org)
            for form in forms:
                utilities.delete_form(form)

            # blow away the org itself
            name = org.name
            org.delete()
            app.logger.info('%s deleted %s' % (session['email'], name))
            flash('organization "%s" was deleted' % name, 'success')
            return redirect(url_for('organizations'))

        else:
            # bad 'form_type'
            abort(404)

        try:
            org.save()
            flash('changes saved successfully', 'success')
        except:
            org.reload()
            app.logger.error('%s experienced an error saving info about %s' %
                             (session['email'], request.form['name']))
            flash('Error saving changes, is the name unique?', 'error')

        return redirect(
            url_for('organizations', org_label=org.label, edit='true'))

    if request.method == 'GET':
        if org_label:
            orgs = Organization.objects(label=org_label)
            if not orgs:
                app.logger.error('%s tried to access an organization that \
                    does not exist' % session['email'])
                flash('Organization "%s" not found, sorry!' % org_label,
                      'warning')
                return redirect(url_for('organizations'))
            org = orgs[0]

            # permission-check
            if org not in user.organizations and not user.admin_rights:
                app.logger.error('%s tried to access an organization but was \
                    denied for want of admin rights' % session['email'])
                abort(404)

            if request.args.get('edit', '') == 'true':
                users = User.objects(organizations=org)
                return render_template('organization_edit.html',
                                       organization=org,
                                       users=users)
            else:
                # get all the members
                users = User.objects(organizations=org)
                # get all relevant forms
                forms = Form.objects(organization=org)
                return render_template('organization.html',
                                       organization=org,
                                       users=users,
                                       forms=forms)

        if request.args.get('create', '') == 'true':
            # create a new form
            # permissions-check
            if not user.admin_rights:
                app.logger.error('%s tried to create an organization but was \
                    denied for want of admin rights' % session['email'])
                abort(404)

            # CSRF validation:
            token = request.args.get('token', '')
            if not verify_token(token):
                app.logger.error('organization-creation CSRF attempt on %s' %
                                 session['email'])
                abort(403)

            try:
                org_name = 'org-%s' % utilities.generate_random_string(6)
                new_org = Organization(label=org_name.lower(), name=org_name)
                new_org.save()
                app.logger.info('organization created by %s' % \
                    session['email'])
                flash('organization created; please change the defaults',
                      'success')
            except:
                app.logger.error('organization creation failed for %s' % \
                    session['email'])
                flash('There was an error in the form, sorry :/', 'error')
                return redirect(url_for('organizations'))

            # redirect to the editing screen
            return redirect(
                url_for('organizations', org_label=new_org.label, edit='true'))

        # nobody in particular was specified; show em all
        if user.admin_rights:
            orgs = Organization.objects()
        else:
            orgs = user.organizations

        # find the users for each org
        users = {}
        for org in orgs:
            users[org.name] = User.objects(organizations=org)

        return render_template('organizations.html',
                               organizations=orgs,
                               users=users)
Ejemplo n.º 33
0
def schedules(org_label, project_label, schedule_label):
    ''' schedule configuration management
    /organizations/aquaya/projects/water-quality/schedules
        : viewing a list of this project's schedules
    /organizations/aquaya/projects/water-quality/schedules?create=true
        : create a new schedule config, immediately redirect to editing
    /organizations/aquaya/projects/water-quality/schedules/weekly
        : view the 'weekly' schedule config
    /organizations/aquaya/projects/water-quality/schedules/weekly?edit=true
        : edit a schedule config; accepts GET or POST
    '''
    user = User.objects(email=session['email'])[0]
    
    orgs = Organization.objects(label=org_label)
    if not orgs:
        flash('Organization "%s" not found, sorry!' % org_label, 'warning')
        return redirect(url_for('organizations'))
    org = orgs[0]

    # permission-check
    if org not in user.organizations and not user.admin_rights:
        app.logger.error('%s tried to view a project but was \
            denied for want of admin rights' % session['email'])
        abort(404)
    
    # find the project
    projects = Project.objects(label=project_label, organization=org) 
    if not projects:
        flash('Project "%s" not found, sorry!' % project_label, 'warning')
        return redirect(url_for('organizations', org_label=org.label))
    project = projects[0]

    if request.method == 'POST':
        # we have a schedule_label
        schedules = Schedule.objects(label=schedule_label, project=project)
        if not schedules:
            abort(404)
        schedule = schedules[0]

        form_type = request.form.get('form_type', '')
        if form_type == 'info':
            if schedule.name != request.form.get('name', ''):
                name = request.form.get('name', '')
                schedule.name = name

                schedules = Schedule.objects(project=project).only('label')
                labels = [s.label for s in schedules]
                schedule.label = utilities.generate_label(name, labels)

            schedule.description = request.form.get('description', '')
       
        elif form_type == 'items':
            # modify schedule attachments

            # 'items' is of the form report_id__4abcd00123 or statistic_id..
            # convert these to real objects
            items = request.form.getlist('items')
            attached_reports, attached_statistics = [], []
            for item in items:
                prefix, item_id = item.split('__')
                item_type = prefix.split('_')[0]

                if item_type == 'report':
                    reports = Report.objects(id=item_id)
                    if not reports:
                        abort(404)
                    attached_reports.append(reports[0])
                
                elif item_type == 'statistic':
                    statistics = Statistic.objects(id=item_id)
                    if not statistics:
                        abort(404)
                    attached_statistics.append(statistics[0])
    
            schedule.update(set__reports=attached_reports)
            schedule.update(set__statistics=attached_statistics)

            # whether we're also sending project data
            send_data = request.form.get('send_project_data', '')
            if send_data == 'true':
                schedule.update(set__send_project_data = True)
            else:
                schedule.update(set__send_project_data = False)

            # save the list of project-data-filters
            # filters are formatted like 'filter_id__4abcd123'
            filter_ids = request.form.getlist('filters')
            attached_filters = []
            for filter_id in filter_ids:
                filters = Filter.objects(id=filter_id.split('__')[1])
                if not filters:
                    abort(404)
                attached_filters.append(filters[0])

            schedule.update(set__data_filters = attached_filters)
            
            # how to apply the filters
            apply_any_filters = request.form.get('apply_any_filters', '')
            if apply_any_filters == 'true':
                schedule.update(set__apply_any_filters= True)
            else:
                schedule.update(set__apply_any_filters= False)

            flash('Schedules attachments saved.', 'success')
            url = url_for('schedules', org_label=org.label
                , project_label=project.label
                , schedule_label=schedule.label, edit='true')
            return redirect(url + '#items')

        elif form_type == 'schedule':
            old_interval = schedule.interval
            new_interval = {
                'type': request.form.get('schedule_type', '')
                , 'at': request.form.get('send_at', '')
                , 'on_day': request.form.get('send_on_day', '')}

            if old_interval != new_interval:
                schedule.interval = new_interval
                schedule.save()
                schedule.reload()
                # delete any old jobs and schedule the new ones
                update_scheduled_send(schedule.id)
            
            flash('Scheduling interval saved successfully.', 'success')
            url = url_for('schedules', org_label=org.label
                , project_label=project.label
                , schedule_label=schedule.label, edit='true')
            return redirect(url + '#schedule')
            
        
        elif form_type == 'message':
            message_type = request.form.get('message_type', '')
            schedule.message_type = message_type

            if message_type == 'email':
                # attach email info to schedule
                schedule.email_subject = request.form.get('email_subject', '')
                schedule.email_body = request.form.get('email_body', '')

                add_email = request.form.get('add_email_recipient_email', '')
                if add_email:
                    # is the email already in place? (shouldn't be)
                    for recipient in schedule.email_recipients:
                        if recipient['email'] == add_email:
                            flash('"%s" is already included in this \
                                schedule' % add_email, 'warning')
                            url = url_for('schedules', org_label=org.label
                                , project_label=project.label
                                , schedule_label=schedule.label, edit='true')
                            return redirect(url + '#message')
                    
                    else:
                        # add the recipient to the schedule
                        add_name = request.form.get(
                            'add_email_recipient_name', '')
                        recipient = {'email': add_email, 'name': add_name}

                        schedule.update(push__email_recipients=recipient)
                        flash('Successfully added "%s" to this schedule' \
                            % add_email, 'success')

                
                remove_email = request.form.get('remove_email_recipient', '')
                if remove_email:
                    # is the email already attached? (should be)
                    emails = [r['email'] for r in schedule.email_recipients]
                    if remove_email not in emails:
                        flash('"%s" is not included in this schedule and \
                            cannot be removed' % remove_email, 'warning')
                        url = url_for('schedules', org_label=org.label
                            , project_label=project.label
                            , schedule_label=schedule.label, edit='true')
                        return redirect(url + '#message')

                    else:
                        # remove it
                        for recipient in schedule.email_recipients:
                            if recipient['email'] == remove_email:
                                schedule.update(
                                    pull__email_recipients=recipient)
                                flash('Successfully removed the address "%s" \
                                    from this schedule' % remove_email
                                    , 'success')
            
            elif message_type == 'sms':
                # attach sms info to schedule
                add_number = request.form.get(
                    'add_sms_recipient_phone_number', '')
                if add_number:
                    # is the number already in place? (shouldn't be)
                    for recipient in schedule.sms_recipients:
                        if recipient['phone_number'] == add_number:
                            flash('"%s" is already included in this \
                                schedule' % add_number, 'warning')
                            url = url_for('schedules', org_label=org.label
                                , project_label=project.label
                                , schedule_label=schedule.label, edit='true')
                            return redirect(url + '#message')
                    
                    else:
                        # add the recipient to the schedule
                        add_name = request.form.get(
                            'add_sms_recipient_name', '')
                        recipient = {'phone_number': add_number
                            , 'name': add_name}

                        schedule.update(push__sms_recipients=recipient)
                        flash('Successfully added "%s" to this schedule' \
                            % add_number, 'success')

                
                remove_number = request.form.get('remove_sms_recipient', '')
                if remove_number:
                    # is the number already attached? (should be)
                    numbers = [r['phone_number'] for r in \
                        schedule.sms_recipients]
                    if remove_number not in numbers:
                        flash('"%s" is not included in this SMS schedule and \
                            cannot be removed' % remove_number, 'warning')
                        url = url_for('schedules', org_label=org.label
                            , project_label=project.label
                            , schedule_label=schedule.label, edit='true')
                        return redirect(url + '#message')

                    else:
                        # remove it
                        for recipient in schedule.sms_recipients:
                            if recipient['phone_number'] == remove_number:
                                schedule.update(
                                    pull__sms_recipients=recipient)
                                flash('Successfully removed the address "%s" \
                                    from this schedule' % remove_number
                                    , 'success')
                                break
            
            schedule.save()

            flash('Message settings changed successfully.', 'success')

            url = url_for('schedules', org_label=org.label
                , project_label=project.label, schedule_label=schedule.label
                , edit='true')
            return redirect(url + '#message')
       

        elif form_type == 'admin':
            # delete the schedule
            # keeping the associated messages as they're an important log
            # related items in the queue will not fire sans schedule 
            name = schedule.name

            # cancel any outstanding jobs
            if schedule.next_task_id:
                redis_config = app.config['REDIS_CONFIG']
                use_connection(Redis(redis_config['host'], redis_config['port']
                        , password=redis_config['password']))
                scheduler = Scheduler()
                job = Job(id=schedule.next_task_id)
                scheduler.cancel(job)

            # delete the job itself
            schedule.delete()
            app.logger.info('%s deleted schdedule "%s"' % \
                (session['email'], name))
            flash('schedule "%s" was deleted successfully' % name, 'success')
            return redirect(url_for('schedules', org_label=org.label
                , project_label=project.label))
        
        else:
            # bad 'form_type'
            abort(404)
       
        try:
            schedule.save()
            flash('Changes saved successfully.', 'success')
            return redirect(url_for('schedules', org_label=org.label
                , project_label=project.label
                , schedule_label=schedule.label, edit='true'))
        except:
            app.logger.error('%s experienced an error saving info about %s' % (
                session['email'], request.form['name']))
            flash('Error saving changes - make sure schedule names are \
                unique.', 'error')
            return redirect(url_for('schedules', org_label=org.label
                , project_label=project.label, schedule_label=schedule_label
                , edit='true'))
        
    
    if request.method == 'GET':
        if schedule_label:
            schedules = Schedule.objects(label=schedule_label, project=project)
            if not schedules:
                app.logger.error('%s tried to access a schedule that does \
                    not exist' % session['email'])
                flash('schedule "%s" not found, sorry!' % schedule_label
                    , 'warning')
                return redirect(url_for('schedules', org_label=org.label
                    , project_label=project.label))
            schedule = schedules[0]

            if request.args.get('edit', '') == 'true':
                
                available_reports = Report.objects(project=project)
                available_filters = Filter.objects(project=project)
                available_statistics = Statistic.objects(project=project)
                
                emails = json.dumps(schedule.email_recipients)

                current_time = datetime.datetime.utcnow()
                
                return render_template('schedule_edit.html', schedule=schedule
                        , available_reports=available_reports
                        , available_filters=available_filters
                        , available_statistics=available_statistics
                        , email_recipients=emails
                        , current_time = current_time)

            elif request.args.get('fire', '') == 'true':
                if ready_to_send(schedule):
                    # immediately enqueue the job with rq
                    # countdown briefly to ensure next_task_id can be updated?
                    redis_config = app.config['REDIS_CONFIG']
                    use_connection(Redis(redis_config['host']
                            , redis_config['port']
                            , password=redis_config['password']))
                    queue = Queue()
                    job = queue.enqueue(send_scheduled_report, schedule.id)
                    
                    # save the id
                    # race condition with enqueued func above?
                    # that's why we used to have a countdown
                    schedule.update(set__next_task_id = job.id)

                    flash('Sending message, this may take a few \
                        moments.  Check the message log for updates.', 'info')
                else:
                    flash('This schedule is not properly configured.', 'error')

                return redirect(url_for('schedules', org_label=org.label
                    , project_label=project.label
                    , schedule_label=schedule_label))

            else:
                # get all messages attached to this schedule
                messages = Message.objects(schedule=schedule).order_by(
                    '-creation_time')

                return render_template('schedule.html', schedule=schedule
                    , messages=messages)

        if request.args.get('create', '') == 'true':
            # create a new schedule

            # CSRF validation
            token = request.args.get('token', '')
            if not verify_token(token):
                abort(403)

            try:
                schedule_name = 'schd-%s' \
                    % utilities.generate_random_string(6)
                new_schedule = Schedule(
                    label = schedule_name.lower()
                    , project = project
                    , name = schedule_name
                )
                new_schedule.save() 
                app.logger.info('schedule created by %s' % session['email'])
                flash('schedule created.  Please change the default values.'
                    , 'success')
            except:
                app.logger.error('schedule creation failed for %s' % \
                    session['email'])
                flash('There was an error, sorry :/', 'error')
                return redirect(url_for('projects', org_label=org.label
                    , project=project.label))
            
            # redirect to the editing screen
            return redirect(url_for('schedules', org_label=org.label
                , project_label=project.label
                , schedule_label=new_schedule.label, edit='true'))
        
        # no statistic in particular was specified; show em all
        schedules = Schedule.objects(project=project)

        return render_template('project_schedules.html', project=project
            , schedules=schedules)
Ejemplo n.º 34
0
def filters(org_label, project_label, filter_label):
    ''' creating filters for various purposes
    /organizations/aquaya/projects/water-quality/filters?create=true
        : create a new filter config, immediately redirect to editing
    /organizations/aquaya/projects/water-quality/filters/big-cities
        : view a filter
    /organizations/aquaya/projects/water-quality/filters/big-cities?edit=true
        : edit a filter; accepts GET or POST
    '''
    user = User.objects(email=session['email'])[0]
    
    orgs = Organization.objects(label=org_label)
    if not orgs:
        flash('Organization "%s" not found, sorry!' % org_label, 'warning')
        return redirect(url_for('organizations'))
    org = orgs[0]

    # permission-check
    if org not in user.organizations and not user.admin_rights:
        app.logger.error('%s tried to view a project but was \
            denied for want of admin rights' % session['email'])
        abort(404)
    
    # find the project
    projects = Project.objects(label=project_label, organization=org) 
    if not projects:
        flash('Project "%s" not found, sorry!' % project_label, 'warning')
        return redirect(url_for('organizations', org_label=org.label))
    project = projects[0]

    if request.method == 'POST':
        # we have a filter_label
        filters= Filter.objects(label=filter_label, project=project)
        if not filters:
            abort(404)
        # should try to avoid overriding the builtin..
        filter = filters[0]

        form_type = request.form.get('form_type', '')
        if form_type == 'info':
            if filter.name != request.form.get('name', ''):
                name = request.form.get('name', '')
                filter.name = name

                filters = Filter.objects(project=project).only('label')
                labels = [f.label for f in filters]
                filter.label = utilities.generate_label(name, labels)

            filter.description = request.form.get('description', '')
            filter.comparison = request.form.get('comparison', '')
            
            # header of the format header_id__4abcd0001
            header_id = request.form.get('header', '')
            header_id = header_id.split('header_id__')[1]
            header = Header.objects(id=header_id)[0]
            filter.header = header

            if header.data_type == 'datetime':
                # check if relative or absolute datetime comparison
                if filter.comparison in \
                    constants.filter_comparisons['datetime_absolute']:
                    compare_to = '%s %s' % (
                        request.form.get('date_compare_to', '')
                        , request.form.get('time_compare_to', ''))
                    try:
                        dt = datetime.datetime.strptime(
                            compare_to, '%B %d, %Y %I:%M %p')
                        filter.compare_to = {'value': dt}
                    except:
                        flash("didn't understand that date formatting; make \
                            sure it looks like 'June 21, 2012' and \
                            '02:45 PM'", 'error')
                        return redirect(url_for('filters', org_label=org.label
                            , project_label=project.label
                            , filter_label=filter.label
                            , edit='true'))
                elif filter.comparison in \
                    constants.filter_comparisons['datetime_relative']:

                    inputs = [request.form.get('relative_years', '')
                            , request.form.get('relative_months', '')
                            , request.form.get('relative_weeks', '')
                            , request.form.get('relative_days', '')
                            , request.form.get('relative_hours', '')
                            , request.form.get('relative_minutes', '')]

                    parsed_inputs = []
                    for i in inputs:
                        if i:
                            parsed_inputs.append(i)
                        else:
                            parsed_inputs.append(0)

                    # verify that we have numbers
                    try:
                        values = [int(i) for i in parsed_inputs]
                    except:
                        flash('input times must be whole numbers', 'error')
                        return redirect(url_for('filters', org_label=org.label
                            , project_label=project.label
                            , filter_label=filter.label
                            , edit='true'))

                    filter.compare_to = {'value': values}

                else:
                    app.logger.info('unknown comparison "%s" for project %s' \
                        % (comparison, project.name))
                    abort(404)

            else:
                # not datetime; cast the value we're comparing against
                compare_to = request.form.get('string_compare_to', '')
                filter.compare_to = {'value': utilities.smart_cast(compare_to)}

        elif form_type == 'admin':
            # delete the filter
            name = filter.name

            # pull filters out of the statistics
            statistics = Statistic.objects(filters=filter)
            for statistic in statistics:
                statistic.update(pull__filters=filter)

            # pull filters out of the graphs
            graphs = Graph.objects(filters=filter)
            for graph in graphs:
                graph.update(pull__filters=filter)

            filter.delete()
            app.logger.info('%s deleted filter "%s"' % \
                (session['email'], name))
            flash('filter "%s" was deleted successfully' % name, 'success')
            return redirect(url_for('filters', org_label=org.label
                , project_label=project.label))
        
        else:
            # bad 'form_type'
            abort(404)
       
        try:
            filter.save()
            flash('changes saved successfully', 'success')
            return redirect(url_for('filters', org_label=org.label
                , project_label=project.label, filter_label=filter.label))
        except:
            app.logger.error('%s experienced an error saving info about %s' % (
                session['email'], request.form['name']))
            flash('Error saving changes -- make sure filter names are unique.'
                , 'error')
            return redirect(url_for('filters', org_label=org.label
                , project_label=project.label, filter_label=filter_label
                , edit='true'))
        
    
    if request.method == 'GET':
        if filter_label:
            filters = Filter.objects(label=filter_label, project=project)
            if not filters:
                app.logger.error('%s tried to access a filter that does not \
                    exist' % session['email'])
                flash('Filter "%s" not found, sorry!' % filter_label
                    , 'warning')
                return redirect(url_for('projects', org_label=org.label
                    , project_label=project.label))
            filter = filters[0]

            if request.args.get('edit', '') == 'true':
                # valid comparisons
                comparisons = json.dumps(constants.filter_comparisons)

                # list of allowed absolute datetime comparisons
                absolute_comparisons = \
                    constants.filter_comparisons['datetime_absolute']

                relative_values = None
                if filter.comparison in \
                    constants.filter_comparisons['datetime_relative']:
                    relative_values = filter.compare_to['value']

                return render_template('filter_edit.html', filter=filter
                    , comparisons=comparisons
                    , absolute_datetime_comparisons=absolute_comparisons
                    , relative_datetime_compare_values=relative_values)
            else:
                # apply the filter; start with some defaults
                conditions = {
                    'project': project
                    , 'unique': True
                    , 'visible': True
                }
                entries = Entry.objects(**conditions)
                project_count = len(entries)
                
                if not filter.comparison:
                    return render_template('filter.html', filter=filter
                        , project_count=project_count)
                
                # find all matches for this filter to display
                matches = utilities.apply_filter(filter, entries)

                # sort the matches
                matches = utilities.sort_entries(project, matches)
                
                # pagination
                entries_per_page = 10
                pages = utilities.calculate_pages(len(matches)
                    , entries_per_page=entries_per_page)

                # validate the requested page
                current_page = utilities.validate_page_request(
                    request.args.get('page', 1), pages)
                
                # manually paginate
                start_index = entries_per_page * (current_page - 1)
                end_index = start_index + entries_per_page
                paginated_matches = matches[start_index:end_index]

                # list of allowed absolute datetime comparisons
                absolute_comparisons = \
                    constants.filter_comparisons['datetime_absolute']
                
                # if the filter is for a relative datetime, parse the value
                relative_value = None
                if filter.comparison in \
                    constants.filter_comparisons['datetime_relative']:
                    
                    # filter.compare_to is a list [year, month, etc..]
                    # first non zero value will be used
                    for i in range(len(filter.compare_to['value'])):
                        if filter.compare_to['value'][i] != 0:
                            break
                    # values in the compare_to list
                    periods = ['year', 'month', 'week', 'day', 'hour'
                        , 'minute']
                    # see if we need to make it plural
                    suffix = ''
                    if filter.compare_to['value'][i] > 1:
                        suffix = 's'
                    
                    relative_value = '%s %s%s' \
                        % (filter.compare_to['value'][i], periods[i], suffix)

                return render_template('filter.html', filter=filter
                    , entries = paginated_matches
                    , total_matches = len(matches)
                    , project_count=project_count
                    , current_page = current_page
                    , number_of_pages = pages
                    , absolute_datetime_comparisons=absolute_comparisons
                    , relative_datetime_value=relative_value)

        if request.args.get('create', '') == 'true':
            # create a new filter 

            # CSRF validation
            token = request.args.get('token', '')
            if not verify_token(token):
                abort(403)

            try:
                filter_name = 'filter-%s' % utilities.generate_random_string(6)
                new_filter= Filter(
                    creation_time = datetime.datetime.utcnow()
                    , creator = user
                    , label = filter_name.lower()
                    , project = project
                    , name = filter_name
                )
                new_filter.save() 
                app.logger.info('filter created by %s' % session['email'])
                flash('filter created; please change the defaults', 'success')
            except:
                app.logger.error('filter creation failed for %s' % \
                    session['email'])
                flash('There was an error, sorry :/', 'error')
                return redirect(url_for('projects', org_label=org.label
                    , project=project.label))
            
            # redirect to the editing screen
            return redirect(url_for('filters', org_label=org.label
                , project_label=project.label, filter_label=new_filter.label
                , edit='true'))
        
        # no filter in particular was specified, show 'em all
        filters = Filter.objects(project=project)
        
        # list of allowed absolute datetime comparisons
        absolute_comparisons = \
            constants.filter_comparisons['datetime_absolute']
        
        relative_values = {}
        for filter in filters:
            # if the filter is for a relative datetime, parse the value
            relative_value = None
            if filter.comparison in \
                constants.filter_comparisons['datetime_relative']:
                
                # filter.compare_to is a list [year, month, etc..]
                # first non zero value will be used
                for i in range(len(filter.compare_to['value'])):
                    if filter.compare_to['value'][i] != 0:
                        break
                # values in the compare_to list
                periods = ['year', 'month', 'week', 'day', 'hour'
                    , 'minute']
                # see if we need to make it plural
                suffix = ''
                if filter.compare_to['value'][i] > 1:
                    suffix = 's'
                
                relative_value = '%s %s%s' % (
                    filter.compare_to['value'][i], periods[i], suffix)

            relative_values[filter.name] = relative_value

        return render_template('project_filters.html', project=project
            , filters=filters
            , absolute_datetime_comparisons=absolute_comparisons
            , relative_datetime_values=relative_values)
Ejemplo n.º 35
0
    dashboard_page = DashboardPage(browser)
    title_has_part = dashboard_page.check_title()

    assert title_has_part is True


@pytest.mark.parametrize("user_name, pass_word", [
    pytest.param('', '', id='3_login_with_empty_data'),
    pytest.param('', data.LOGIN_PASSWORD, id='4_login_with_empty_email'),
    pytest.param(data.LOGIN_EMAIL, '', id='5_login_with_empty_password'),
    pytest.param(utilities.generate_random_email(data.LOGIN_EMAIL),
                 data.LOGIN_PASSWORD,
                 id='6_login_with_incorrect_email'),
    pytest.param(
        data.LOGIN_EMAIL,
        utilities.generate_random_string(
            size=randint(8, 20), punc=True, original=data.LOGIN_PASSWORD),
        id='7_login_with_incorrect_password'),
    pytest.param(data.LOGIN_EMAIL,
                 data.LOGIN_PASSWORD.swapcase(),
                 id='8_login_with_case_changed_password'),
])
def testing_with_incorrect_data(browser, user_name, pass_word):
    login_page = LoginPage(browser)
    login_page.load(url=data.LOGIN_URL)
    login_page.fill_username(user_name)
    login_page.fill_password(pass_word)
    login_page.click_login()
    login_message_has_part = login_page.check_login_message('Please try again')

    assert login_message_has_part is True
Ejemplo n.º 36
0
def members(internal_id):
    ''' show the users and their verification/confirmation status
    if there's an email included in the route, render that profile for editing
    '''
    if request.method == 'POST':
        users = User.objects(id=internal_id)
        if not users:
            abort(404)
        user = users[0]

        profile_form_type = request.form.get('profile_form_type', '')
        if profile_form_type == 'info':
            if user.name != request.form.get('name', ''):
                app.logger.info('%s edited the name of %s to %s' %
                                (session['email'], request.form['email'],
                                 request.form.get('name', '')))
                user.name = request.form.get('name', '')

            if user.email != request.form.get('email', ''):
                app.logger.info('%s edited the email of %s to %s' %
                                (session['email'], request.form['email'],
                                 request.form.get('email', '')))
                user.email = request.form.get('email', '')

            if request.form['verification'] == 'verified':
                # check to see if the verification status has changed
                if not user.verified:
                    app.logger.info('%s verified %s' %
                                    (session['email'], request.form['email']))

                    # send email to user that they've been verified
                    utilities.send_notification_of_verification(
                        user, request.form.get('email', ''))
                    user.verified = True

                    # create API credentials for the user
                    user.api_id = 'ID' + utilities.generate_random_string(32)
                    user.api_key = utilities.generate_random_string(34)

            elif request.form['verification'] == 'unverified':
                if user.verified:
                    app.logger.info('%s unverified %s' %
                                    (session['email'], request.form['email']))
                user.verified = False

            if request.form['admin'] == 'admin':
                if not user.admin_rights:
                    app.logger.info('%s gave admin privileges to %s' %
                                    (session['email'], request.form['email']))
                user.admin_rights = True

            elif request.form['admin'] == 'normal':
                if user.admin_rights:
                    app.logger.info('%s removed admin privileges from %s' %
                                    (session['email'], request.form['email']))
                user.admin_rights = False

        elif profile_form_type == 'account':
            # delete the user
            # first pull out the user from the relevant orgs
            orgs = user.organizations
            for org in orgs:
                org.update(pull__users=user)

            # delete the user itself
            user_email = user.email
            user.delete()
            app.logger.info('%s deleted %s' % (session['email'], user_email))
            flash('user deleted', 'success')
            return redirect(url_for('members'))

        else:
            # bad 'profile_form_type'
            abort(404)

        try:
            user.save()
            flash('changes saved successfully', 'success')
        except:
            app.logger.error('%s experienced an error saving info about %s' %
                             (session['email'], request.form['email']))
            flash('error saving changes, sorry /:')

        return redirect(url_for('members', internal_id=user.id))

    if request.method == 'GET':
        if internal_id:
            user = User.objects(id=internal_id)
            if not user:
                abort(404)
            user = user[0]
            return render_template('members_edit.html', user=user)

        # nobody in particular was specified; show em all
        users = User.objects()
        return render_template('members.html', users=users)