Skip to content

Commit

Permalink
clean up initial group admin
Browse files Browse the repository at this point in the history
  • Loading branch information
davidism committed Aug 25, 2014
1 parent e95fbea commit c087d97
Show file tree
Hide file tree
Showing 12 changed files with 134 additions and 128 deletions.
1 change: 1 addition & 0 deletions sopy/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

bp = Blueprint('admin', __name__)


@bp.record_once
def register(state):
from sopy.admin import views
40 changes: 29 additions & 11 deletions sopy/admin/forms.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
from wtforms import ValidationError
from flask.ext.wtf import Form
from flask_wtf import Form
from wtforms.fields import StringField
from wtforms.validators import Required

from wtforms.validators import ValidationError, InputRequired
from sopy import db
from sopy.auth.models import User


class EditUserGroupsForm(Form):
""" a form to add a user to a group """
user_id = StringField('user id', validators=[Required()])
class UserListForm(Form):
user_ids = StringField('User IDs', validators=[InputRequired()])

def validate_user_ids(self, field):
raw_ids = field.data.split()
int_ids = set()

for id in raw_ids:
try:
int_ids.add(int(id))
except TypeError:
raise ValidationError('Invalid ID "{}"'.format(id))

q_ids = db.session.query(db.cast(db.func.unnest(field.data.split()), db.Integer).label('id')).subquery()
users = User.query.join((q_ids, q_ids.c.id == User.id)).all()
new_ids = int_ids - {u.id for u in users}

for id in new_ids:
#TODO: use api to load multiple users in one request
user = User.se_load(id)

if user is None:
raise ValidationError('Invalid ID "{}"'.format(id))

users.append(user)

def validate_user_id(self, field):
""" check that the user ID exists in the db """
if User.query.get(field.data) is None:
raise ValidationError('No user with that ID exists')
self.users = users
66 changes: 29 additions & 37 deletions sopy/admin/views.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,42 @@
from flask import abort, render_template, request

from sopy import db
from sopy.admin import bp
from sopy.admin.forms import EditUserGroupsForm
from sopy.admin.forms import UserListForm
from sopy.ext.views import template, redirect_for
from sopy.auth.models import User, Group
from sopy.auth.login import has_group, current_user, login_required
from sopy.auth.login import group_required


@bp.before_request
@login_required
@group_required('approved')
def authorize():
""" Check that the user has privileges to access this page. """
if not current_user.has_group('approved'):
abort(403) # page forbidden


@bp.route('/groups')
@template('admin/view_all_groups.html')
def view_all_groups():
""" returns all distinct groups in the database, adds links to the page
note: the links are not very pretty. """
groups = Group.query.distinct()
return {'groups': groups}


@bp.route('/groups/<group_name>', methods=['GET', 'POST'])
@template('/admin/view_group.html')
def view_group(group_name):
""" Find all groups, and the users which belong to them. """
selected_group = User.query.filter(User._groups.any(Group.name == group_name)).all()
form = EditUserGroupsForm()
pass


@bp.route('/groups/')
@template('admin/groups/index.html')
def groups_index():
return {'groups': Group.query.order_by(Group.name).all()}


@bp.route('/groups/<name>/', methods=['GET', 'POST'])
@template('/admin/groups/detail.html')
def groups_detail(name):
group = Group.query.options(db.joinedload(Group.users)).filter(Group.name == name).first_or_404()
form = UserListForm()

if form.validate_on_submit():
user = User.query.get(form.user_id.data)
group = Group.query.filter_by(name=group_name).one()
user._groups.add(group)
group.users.update(form.users)
db.session.commit()
return redirect_for('admin.view_group', group_name=group_name)
return {'group': group_name, 'group_users': selected_group, 'form': form}

return redirect_for('admin.groups_detail', name=name)

return {'group': group, 'form': form}

@bp.route('/groups/<group_name>/remove/<int:user_id>')
def remove_user_from_group(group_name, user_id):
""" remove a specified user from a group """
user = User.query.get_or_404(user_id)
group = Group.query.filter_by(name=group_name).one()
user._groups.discard(group)

@bp.route('/groups/<name>/remove/<int:user_id>')
def groups_remove_user(name, user_id):
user = User.query.options(db.joinedload(User._groups)).get_or_404(user_id)
user.groups.discard(name)
db.session.commit()
return redirect_for('admin.view_group', group_name=group_name)

return redirect_for('admin.groups_detail', name=name)
23 changes: 18 additions & 5 deletions sopy/migrations/1f5a6a1a28c_add_profile_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,33 @@
"""

# revision identifiers, used by Alembic.
import click

revision = '1f5a6a1a28c'
down_revision = '20e76fbc8f6'

from alembic import op
import sqlalchemy as sa
from sqlalchemy.orm import Session


def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('se_user', sa.Column('profile_image', sa.String(), nullable=True))
### end Alembic commands ###
from sopy.se_data.models import SEUser

session = Session(bind=op.get_bind())
op.add_column('se_user', sa.Column('profile_image', sa.String))

with click.progressbar(
session.query(SEUser).all(),
label='Updating user data'
) as bar:
for user in bar:
user.se_update()

session.commit()

op.alter_column('se_user', 'profile_image', nullable=False)


def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_column('se_user', 'profile_image')
### end Alembic commands ###
9 changes: 6 additions & 3 deletions sopy/se_data/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

class SEUser(ExternalIDModel):
display_name = db.Column(db.String, nullable=False)
profile_image = db.Column(db.String)
profile_image = db.Column(db.String, nullable=False)

def __str__(self):
return self.display_name
Expand All @@ -29,9 +29,12 @@ def se_load(cls, ident):
'key': current_app.config.get('SE_API_KEY'),
'site': 'stackoverflow',
})
data = r.json()['items'][0]
items = r.json()['items']

return o.se_update(data)
if not items:
return None

return o.se_update(items[0])

def se_update(self, data=None):
if data is None:
Expand Down
6 changes: 0 additions & 6 deletions sopy/static/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,3 @@ body>footer {
text-align: center;
padding-top: 21px;
}

.vbottom {
display: inline-block;
vertical-align: bottom;
float: none;
}
2 changes: 1 addition & 1 deletion sopy/templates/admin/base.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{% extends "base.html" %}
{% extends 'base.html' %}

{% block nav_admin %}active{% endblock %}
38 changes: 38 additions & 0 deletions sopy/templates/admin/groups/detail.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{% extends "admin/base.html" %}

{% block content %}
<div class="row"><div class="col-md-12">
<h2 class="page-header">{% block title %}{{ group.name }}{% endblock %}</h2>

<form method="post" class="form-inline">
{{ form.hidden_tag() }}
<div class="form-group{% if form.user_ids.errors %} has-error{% endif %}">
{{ form.user_ids.label(class='sr-only') }}
{{ form.user_ids(class='form-control', placeholder=form.user_ids.label.text) }}
</div>
<button type="submit" class="btn btn-success"><i class="fa fa-plus"></i> Add Users</button>
</form>

<hr/>

<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th class="col-sm-1">ID</th>
<th>Name</th>
<th class="col-sm-1"></th>
</tr>
</thead>

<tbody>
{% for user in group.users|sort(attribute='id') %}
<tr>
<td>{{ user.id }}</td>
<td><img src="{{ user.profile_image }}" alt="" height="24"/> {{ user.display_name }}</td>
<td><a href="{{ url_for('admin.groups_remove_user', name=group.name, user_id=user.id) }}" class="btn btn-danger btn-block btn-xs"><i class="fa fa-minus"></i> Remove</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div></div>
{% endblock %}
11 changes: 11 additions & 0 deletions sopy/templates/admin/groups/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{% extends 'admin/base.html' %}

{% block content %}
<div class="row"><div class="col-md-12">
<h2 class="page-header">{% block title %}Groups{% endblock %}</h2>

<ul>{% for group in groups %}
<li><a href="{{ url_for('admin.groups_detail', name=group.name) }}">{{ group.name }}</a></li>
{% endfor %}</ul>
</div></div>
{% endblock %}
15 changes: 0 additions & 15 deletions sopy/templates/admin/view_all_groups.html

This file was deleted.

47 changes: 0 additions & 47 deletions sopy/templates/admin/view_group.html

This file was deleted.

4 changes: 1 addition & 3 deletions sopy/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,14 @@
<li class="{% block nav_salad %}{% endblock %}"><a href="{{ url_for('salad.index') }}">Salad Language</a></li>
<li class="{% block nav_chatroom %}{% endblock %}"><a href="{{ url_for('pages.page', name='chatroom') }}">Chatroom</a></li>
<li class="{% block nav_nidaba %}{% endblock %}"><a href="{{ url_for('pages.page', name='nidaba') }}">Nidaba</a></li>
{% if has_group('approved') %}<li class="{% block nav_admin %}{% endblock %}"><a href="{{ url_for('admin.groups_index') }}">Admin</a></li>{% endif %}
</ul>
<ul class="nav navbar-nav navbar-right">
{% if current_user.anonymous %}
<li><a href="{{ url_for('auth.login', next=request.path) }}">Log In</a></li>
{% else %}
<li class="{% block nav_user %}{% endblock %}"><a href="#">{{ current_user.display_name }}</a></li>
<li><a href="{{ url_for('auth.logout') }}">Log Out</a></li>
{% if has_group('approved') %}
<li class="{% block nav_admin %}{% endblock %}"><a href="{{ url_for('admin.view_all_groups') }}">Admin</a></li>
{% endif %}
{% endif %}
</ul>
</div>
Expand Down

0 comments on commit c087d97

Please sign in to comment.