diff --git a/manage.py b/manage.py index 0a85552..64b701d 100644 --- a/manage.py +++ b/manage.py @@ -1,12 +1,15 @@ from flask.ext.script import Manager -from studygroup.application import create_app, db +from studygroup.application import create_app, db, create_baseline_data manager = Manager(create_app) + @manager.command def create_tables(): db.create_all() + create_baseline_data() + if __name__ == "__main__": manager.run() diff --git a/studygroup/application.py b/studygroup/application.py index 0fcd48e..36e6a26 100644 --- a/studygroup/application.py +++ b/studygroup/application.py @@ -9,6 +9,21 @@ db = SQLAlchemy() oauth = OAuth() + +def create_baseline_data(): + from studygroup.models import GroupStatus + group_statuses = [ + ('proposed', 'Proposed'), + ('active', 'Active'), + ('inactive', 'Inactive'), + ] + for name, display in group_statuses: + if not db.session.query(GroupStatus).filter(GroupStatus.name == name).first(): + db.session.add(GroupStatus(name=name, display=display)) + + db.session.commit() + + def create_app(debug=True): from views import studygroup diff --git a/studygroup/models.py b/studygroup/models.py index 63cdfa8..aa774cb 100644 --- a/studygroup/models.py +++ b/studygroup/models.py @@ -1,6 +1,7 @@ """ Data models for StudyGroups """ +from sqlalchemy.sql import select from .application import db import settings @@ -8,13 +9,14 @@ ROLE_MEMBER = 1 ROLE_GROUP_LEADER = 10 + class User(db.Model): id = db.Column(db.Integer, primary_key=True) meetup_member_id = db.Column(db.String, unique=True) full_name = db.Column(db.String) is_admin = db.Column(db.Boolean, nullable=False, default=False) - memberships = db.relationship("Membership", backref="user") + memberships = db.relationship('Membership', backref='user') class Membership(db.Model): @@ -24,23 +26,38 @@ class Membership(db.Model): role = db.Column(db.Integer, nullable=False, default=ROLE_MEMBER) +class GroupStatus(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String, nullable=False, unique=True) + display = db.Column(db.String, nullable=False) + + class Group(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, unique=True) description = db.Column(db.String) max_members = db.Column(db.Integer, nullable=False, default=settings.DEFAULT_MAX_MEMBERS) + status_id = db.Column(db.Integer, db.ForeignKey('group_status.id'), + nullable=False, + default=select([GroupStatus.__table__.c.id]). + where(GroupStatus.__table__.c.name == 'proposed')) - memberships = db.relationship("Membership", backref="group") + memberships = db.relationship('Membership', backref='group') + status = db.relationship('GroupStatus') @classmethod def all_with_memberships(cls): - return Group.query.options(db.joinedload(Group.memberships)).all() + return Group.query.options(db.joinedload(Group.memberships)) @classmethod def by_id_with_memberships(cls, id): return Group.query.filter_by(id=id)\ .options(db.joinedload(Group.memberships)).first() + @staticmethod + def filter_proposed(query): + return query.join(GroupStatus).filter(GroupStatus.name != 'proposed') + def is_full(self): return len(self.memberships) >= self.max_members diff --git a/studygroup/views.py b/studygroup/views.py index 3060ac1..71cda43 100644 --- a/studygroup/views.py +++ b/studygroup/views.py @@ -1,5 +1,5 @@ from flask import (g, request, redirect, render_template, - session, url_for, jsonify, Blueprint) + session, url_for, jsonify, Blueprint, abort) from .models import User, Group from .application import db, meetup @@ -8,6 +8,7 @@ studygroup = Blueprint("studygroup", __name__, static_folder='static') + @studygroup.before_request def load_user(): user_id = session.get('user_id') @@ -21,17 +22,29 @@ def load_user(): def index(): return render_template('index.html') + @studygroup.route('/groups') @login_required def show_groups(): - g.groups = Group.all_with_memberships() + groups = Group.all_with_memberships() + if not g.user.is_admin: + groups = Group.filter_proposed(groups) + g.groups = groups return render_template('groups.html') + @studygroup.route('/group/') def show_group(id): - g.group = Group.query.filter_by(id=id).first() + groups = Group.query.filter_by(id=id) + if not g.user.is_admin: + groups = Group.filter_proposed(groups) + group = groups.first() + if not group: + abort(404) + g.group = group return render_template('show_group.html') + @studygroup.route('/group/new', methods=('GET', 'POST')) def new_group(): form = GroupForm() @@ -40,6 +53,7 @@ def new_group(): return redirect(url_for('.show_group', id=group.id)) return render_template('new_group.html', form=form) + @studygroup.route('/join_group', methods=('POST',)) def join_group(): pass @@ -92,6 +106,7 @@ def send_message(member_id): else: return "Invalid Request", 500 + @studygroup.route('/boom') def boom(): raise Exception('BOOM') diff --git a/tests/test_app.py b/tests/test_app.py index 09b7198..15a9a3c 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -2,7 +2,8 @@ from .tools import StudyGroupTestCase -from studygroup.models import Group +from studygroup.application import db +from studygroup.models import Group, GroupStatus class HomePageTest(StudyGroupTestCase): @@ -16,7 +17,7 @@ def test_logged_in(self): self.login(self.alice_id) resp = self.client.get('/') self.assert200(resp) - self.assertIn('Welcome, Alice B. Admin', resp.data) + self.assertIn('Welcome, Alice B.', resp.data) self.assertNotIn('Sign In Now', resp.data) @@ -42,3 +43,62 @@ def test_make_a_group(self): self.assertEqual(g1.name, "The Group") self.assertEqual(g1.description, "A group!") self.assertEqual(g1.max_members, 10) + self.assertEqual(g1.status.name, 'proposed') + + +class GroupListTest(StudyGroupTestCase): + def setUp(self): + super(GroupListTest, self).setUp() + self.proposed_group = Group( + name='Proposed Group', + description='Proposed Group', + status=db.session.query(GroupStatus).filter(GroupStatus.name == 'proposed').one()) + db.session.add(self.proposed_group) + + self.active_group = Group( + name='Active Group', + description='Active Group', + status=db.session.query(GroupStatus).filter(GroupStatus.name == 'active').one()) + db.session.add(self.active_group) + + db.session.commit() + + def test_hide_proposed_from_non_admin(self): + self.login(self.alice_id) + resp = self.client.get(url_for('studygroup.show_groups')) + + self.assert200(resp) + self.assertIn('Active Group', resp.data) + self.assertNotIn('Proposed Group', resp.data) + + def test_show_proposed_to_admin(self): + self.login(self.admin_id) + resp = self.client.get(url_for('studygroup.show_groups')) + + self.assert200(resp) + self.assertIn('Active Group', resp.data) + self.assertIn('Proposed Group', resp.data) + + +class ShowGroupTest(StudyGroupTestCase): + def setUp(self): + super(ShowGroupTest, self).setUp() + self.proposed_group = Group( + name='Proposed Group', + description='Proposed Group', + status=db.session.query(GroupStatus).filter(GroupStatus.name == 'proposed').one()) + db.session.add(self.proposed_group) + + db.session.commit() + + def test_hide_proposed_from_non_admin(self): + self.login(self.alice_id) + resp = self.client.get(url_for('studygroup.show_group', id=self.proposed_group.id)) + + self.assert404(resp) + + def test_show_proposed_to_admin(self): + self.login(self.admin_id) + resp = self.client.get(url_for('studygroup.show_group', id=self.proposed_group.id)) + + self.assert200(resp) diff --git a/tests/tools.py b/tests/tools.py index 569f634..c10ea14 100644 --- a/tests/tools.py +++ b/tests/tools.py @@ -2,12 +2,9 @@ Tools to help test StudyGroup. """ -import unittest - -from flask import session from flask.ext.testing import TestCase -from studygroup.application import create_app, db +from studygroup.application import create_app, db, create_baseline_data from studygroup.models import User @@ -17,7 +14,9 @@ class StudyGroupTestCase(TestCase): """ def setUp(self): db.create_all() - self.alice_id = self.create_user(full_name="Alice B. Admin") + create_baseline_data() + self.alice_id = self.create_user(full_name='Alice B.') + self.admin_id = self.create_user(full_name='Bob Admin', is_admin=True) def tearDown(self): db.session.remove()