From c8ac5bcd74ea69c59e657fa205330f006680321e Mon Sep 17 00:00:00 2001 From: Jason Michalski Date: Mon, 14 Apr 2014 08:23:25 -0700 Subject: [PATCH 1/3] Normalise quotes to single quotes --- studygroup/models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/studygroup/models.py b/studygroup/models.py index 63cdfa8..e037bfe 100644 --- a/studygroup/models.py +++ b/studygroup/models.py @@ -8,13 +8,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): @@ -31,7 +32,7 @@ class Group(db.Model): max_members = db.Column(db.Integer, nullable=False, default=settings.DEFAULT_MAX_MEMBERS) - memberships = db.relationship("Membership", backref="group") + memberships = db.relationship('Membership', backref='group') @classmethod def all_with_memberships(cls): From f2ff675b4ce5e3f225ca476b72a4d65ff41a47dd Mon Sep 17 00:00:00 2001 From: Jason Michalski Date: Mon, 14 Apr 2014 08:23:52 -0700 Subject: [PATCH 2/3] Add group statuses Add statueses to groups and default new groups to have the status of proposed. --- manage.py | 5 ++++- studygroup/application.py | 15 +++++++++++++++ studygroup/models.py | 12 ++++++++++++ tests/test_app.py | 1 + tests/tools.py | 6 ++---- 5 files changed, 34 insertions(+), 5 deletions(-) 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 e037bfe..a1b6a0b 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 @@ -25,14 +26,25 @@ 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') + status = db.relationship('GroupStatus') @classmethod def all_with_memberships(cls): diff --git a/tests/test_app.py b/tests/test_app.py index 09b7198..2849c1d 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -42,3 +42,4 @@ 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') diff --git a/tests/tools.py b/tests/tools.py index 569f634..85cc100 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,6 +14,7 @@ class StudyGroupTestCase(TestCase): """ def setUp(self): db.create_all() + create_baseline_data() self.alice_id = self.create_user(full_name="Alice B. Admin") def tearDown(self): From 8bd455a3450d4327999d12181892d73b4ca17ea1 Mon Sep 17 00:00:00 2001 From: Jason Michalski Date: Wed, 16 Apr 2014 19:23:21 -0700 Subject: [PATCH 3/3] Filter study groups based on proposed Do not list proposed groups to non-admins. Also respond with a 404 to groups that are proposed to non-admins. --- studygroup/models.py | 6 ++++- studygroup/views.py | 21 ++++++++++++--- tests/test_app.py | 63 ++++++++++++++++++++++++++++++++++++++++++-- tests/tools.py | 3 ++- 4 files changed, 86 insertions(+), 7 deletions(-) diff --git a/studygroup/models.py b/studygroup/models.py index a1b6a0b..aa774cb 100644 --- a/studygroup/models.py +++ b/studygroup/models.py @@ -48,12 +48,16 @@ class Group(db.Model): @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 2849c1d..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) @@ -43,3 +44,61 @@ def test_make_a_group(self): 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 85cc100..c10ea14 100644 --- a/tests/tools.py +++ b/tests/tools.py @@ -15,7 +15,8 @@ class StudyGroupTestCase(TestCase): def setUp(self): db.create_all() create_baseline_data() - self.alice_id = self.create_user(full_name="Alice B. Admin") + 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()