Skip to content

Commit 7080c8f

Browse files
author
Sam Redmond
committed
Changes EVERYTHING! And I mean everything. Not only has everything been organized more clearly (all held under an app/ folder, rather than jumbled). Begins to incorporate databases, and has a hell of a lot more dependencies (thank you Flask, for integrating with literally EVERY python module known to man. Also separates views, models, and config, and clarifies the distinction and relationship between various HTML pages. Externalized the startup command to run.py. All python scripts are now executable by the venv python interpreter. What else... the project becomes a package, rather than a module (or I may have that wrong... - we now have an __init__.py). There is more (namely, better login support), but this commit is just because I'm deathly afraid I'll erase all of my code again. Yay version control! Sorry for the long commit - if you're reading this, get back to work.
1 parent 2ada984 commit 7080c8f

38 files changed

+617
-454
lines changed

.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,10 @@ venv
4242
#Request and Feedback pickled arrays
4343
requests/*
4444
feedback/*
45+
46+
#Ignore database storage
47+
db_*
48+
app.db
49+
50+
#Ignore tmp
51+
tmp/*

Procfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
web: gunicorn main:app
1+
web: gunicorn run:app

app/__init__.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import os
2+
from flask import Flask
3+
from flask.ext.sqlalchemy import SQLAlchemy
4+
from flask.ext.login import LoginManager
5+
from flask.ext.openid import OpenID
6+
from config import basedir
7+
8+
app = Flask(__name__)
9+
app.config.from_object('config')
10+
db = SQLAlchemy(app)
11+
12+
lm = LoginManager()
13+
lm.init_app(app)
14+
lm.login_view = 'login'
15+
16+
from app import views, models

clear.py renamed to app/clear.py

File renamed without changes.

app/dbLib.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from flask.ext.sqlalchemy import SQLAlchemy #Our SQL helper
File renamed without changes.

app/forms.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from flask.ext.wtf import Form
2+
from wtforms import TextField, BooleanField
3+
from wtforms.validators import Required
4+
5+
class LoginForm(Form):
6+
openid = TextField('openid', validators = [Required()])
7+
remember_me = BooleanField('remember_me', default = False)

app/main.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import os
2+
from flask import Flask, request, render_template, url_for, redirect, flash #The main module that does a lot of hidden work
3+
from flask.ext.sqlalchemy import SQLAlchemy #Our SQL helper
4+
import cPickle as cp #A workaround to SQL for now
5+
from mshc_lib import * #Our custom library
6+
import bcrypt
7+
8+
DEBUG=True #CHANGE THIS TO FALSE ON MAJOR RELEASES
9+
app = Flask(__name__)
10+
app.secret_key=os.urandom(24)
11+
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
12+
db = SQLAlchemy(app)
13+
14+
pickle_path = os.path.join(os.getcwd(), 'requests/pickled_requests.txt')
15+
feedback_path = os.path.join(os.getcwd(), 'feedback/feedback.txt')
16+
17+
18+
##DECLARE DATABASE MODELS##
19+
class Request(db.Model):
20+
request_id = db.Column(db.Integer, primary_key=True)
21+
title = db.Column(db.String, unique=True)
22+
description = db.Column(db.String, unique=True)
23+
class_name = db.Column(db.String, unique=True)
24+
user = db.relationship('User', backref='request')
25+
26+
def __init__(self, username, email, class_name):
27+
self.username = username
28+
self.email = email
29+
30+
def __repr__(self):
31+
return '<User %r>' % self.title
32+
33+
class User(db.Model):
34+
user_id = db.Column(db.Integer, primary_key=True)
35+
username = db.Column(db.String(50), unique=True)
36+
pw_hash = db.Column(db.String, unique=True)
37+
38+
first_name = db.Column(db.String(50), unique=True)
39+
last_name = db.Column(db.String(50), unique=True)
40+
grade = db.Column(db.Integer, unique=True)
41+
email = db.Column(db.String(150), unique=True)
42+
43+
##Classes the user is taking
44+
45+
##Classes the user can Tutor
46+
47+
##Requests I have made
48+
49+
50+
def __init__(self, username, email):
51+
self.username = username
52+
self.email = email
53+
54+
def __repr__(self):
55+
return '<User %r>' % self.username
56+
57+
##END DECLARE DB MODELS####
58+
59+
#DATABASE STUFF
60+
61+
#Run the app
62+
if __name__ == '__main__':
63+
app.run(debug=DEBUG)

app/models.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from app import db
2+
ROLE_USER = 0
3+
ROLE_ADMIN = 1
4+
5+
class User(db.Model):
6+
id = db.Column(db.Integer, primary_key = True)
7+
8+
#Information for logging in
9+
email = db.Column(db.String(120), unique = True)
10+
hashed_password = db.Column(db.String(60));
11+
12+
#Personal Information
13+
first_name = db.Column(db.String(20))
14+
last_name = db.Column(db.String(20))
15+
grade = db.Column(db.SmallInteger)
16+
17+
#Classes
18+
#classes_taking = db.Column(db.PickleType(mutable=True))
19+
#classes_can_help = db.Column(db.PickleType(mutable=True))
20+
21+
#Administrative
22+
role = db.Column(db.SmallInteger, default = ROLE_USER)
23+
24+
#Requests Made
25+
requests = db.relationship('Request', backref = 'author', lazy = 'dynamic')
26+
27+
#Necessary methods for Flask-Login
28+
def is_authenticated(self):
29+
return True
30+
31+
def is_active(self):
32+
return True
33+
34+
def is_anonymous(self):
35+
return False
36+
37+
def get_id(self):
38+
return unicode(self.id)
39+
40+
def verify_password(self, password):
41+
return bcrypt.hashpw(password, self.hashed_password) == self.hashed_password
42+
43+
def __repr__(self):
44+
return '<User %r>' % (self.email)
45+
46+
class Request(db.Model):
47+
id = db.Column(db.Integer, primary_key = True)
48+
#A form of meta data
49+
class_for = db.Column(db.String(64)) #64 is arbitrary
50+
issue = db.Column(db.String(64)) #Again, arbitrary - this field holds "homework" or "project" or "test" or something else
51+
52+
#Content
53+
body = db.Column(db.String(255)) #Max length for VARCHARs in older versions of SQL
54+
extra_requests = db.Column(db.String(255))
55+
availability = db.Column(db.String(255))
56+
additional = db.Column(db.String(255))
57+
58+
#Other
59+
timestamp = db.Column(db.DateTime)
60+
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
61+
62+
def __repr__(self):
63+
return '<Post %r>' % (self.body)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

static/css/extra_styles.css renamed to app/static/css/extra_styles.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
.footer {
1414
text-align: center;
15-
padding: 30px 0;
15+
padding: 30px 0 0 0;
1616
margin-top: 70px;
1717
border-top: 1px solid #e5e5e5;
1818
background-color: #f5f5f5;
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

app/templates/index.html

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{% extends "layout.html" %}
2+
{% block content %}
3+
<h1>Hi, {{user.email}}!</h1>
4+
{% for post in posts %}
5+
<div><p>{{post.author.email}} says: <b>{{post.body}}</b></p></div>
6+
{% endfor %}
7+
{% endblock %}

app/templates/layout.html

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<!--You found an easter egg!
5+
__ _____________.____ _________ ________ _____ ___________
6+
/ \ / \_ _____/| | \_ ___ \ \_____ \ / \ \_ _____/
7+
\ \/\/ /| __)_ | | / \ \/ / | \ / \ / \ | __)_
8+
\ / | \| |__\ \____/ | \/ Y \| \
9+
\__/\ / /_______ /|_______ \______ /\_______ /\____|__ /_______ /
10+
\/ \/ \/ \/ \/ \/ \/
11+
___________________
12+
\__ ___/\_____ \
13+
| | / | \
14+
| | / | \
15+
|____| \_______ /
16+
\/
17+
______________ ______________
18+
\__ ___/ | \_ _____/
19+
| | / ~ \ __)_
20+
| | \ Y / \
21+
|____| \___|_ /_______ /
22+
\/ \/
23+
_____ _________ ___ ___ _________
24+
/ \ / _____// | \ \_ ___ \
25+
/ \ / \ \_____ \/ ~ \/ \ \/
26+
/ Y \/ \ Y /\ \____
27+
\____|__ /_______ /\___|_ / \______ /
28+
\/ \/ \/ \/
29+
-->
30+
31+
<meta charset="utf-8">
32+
{% if title %}
33+
<title>{{ title }} - MSHC</title>
34+
{% else %}
35+
<title>Menlo's Math and Science Help Center</title>
36+
{% endif %}
37+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
38+
<meta name="keywords" content="menlo, mshc, math, science, help">
39+
<meta name="description" content="">
40+
<meta name="author" content="Sam Redmond">
41+
42+
<!-- Styles -->
43+
<link href="/static/css/bootstrap.css" rel="stylesheet">
44+
<link href="/static/css/extra_styles.css" rel="stylesheet">
45+
46+
<!-- Touch and fav icons -->
47+
<link rel="icon" href="/static/img/favicon.ico" type="image/x-icon" />
48+
49+
</head>
50+
<body>
51+
<!--Flash any messages-->
52+
{% with messages = get_flashed_messages() %}
53+
{% if messages %}
54+
<ul class="flashes">
55+
{% for message in messages %}
56+
<li>{{ message }}</li>
57+
{% endfor %}
58+
</ul>
59+
{% endif %}
60+
{% endwith %}
61+
<!-- Begin header -->
62+
<div class="navbar navbar-inverse navbar-fixed-top">
63+
<div class="navbar-inner">
64+
<div class="container-fluid">
65+
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
66+
<span class="icon-bar"></span>
67+
<span class="icon-bar"></span>
68+
<span class="icon-bar"></span>
69+
</button>
70+
<a class="brand" href="/">Math&amp;Science Help Center</a>
71+
<div class="nav-collapse collapse">
72+
<ul class="nav">
73+
<li class="active"><a href="/">Home</a></li>
74+
<li class="dropdown">
75+
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Me<b class="caret"></b></a>
76+
<ul class="dropdown-menu">
77+
<li class="nav-header">View</li>
78+
<li><a href="#">Learner View</a></li>
79+
<li><a href="#">Tutor View</a></li>
80+
<li class="divider"></li>
81+
<li class="nav-header">Settings</li>
82+
<li><a href="#">My information</a></li>
83+
<li><a href="#">My classes</a></li>
84+
<li><a href="#">My history</a></li>
85+
</ul>
86+
</li>
87+
</ul>
88+
<form class="navbar-form pull-right">
89+
{% if g.user.is_authenticated() %}
90+
<button type="submit" class="btn">Logout</button>
91+
{% endif %}
92+
<input class="span2" type="text" placeholder="Email" disabled>
93+
<input class="span2" type="password" placeholder="Password" disabled>
94+
<button type="submit" class="btn">Sign in</button>
95+
</form>
96+
</div><!--/.nav-collapse -->
97+
</div>
98+
</div>
99+
</div>
100+
</div>
101+
<!-- End header -->
102+
<!--Flash any messages-->
103+
{% with messages = get_flashed_messages() %}
104+
{% if messages %}
105+
<ul>
106+
{% for message in messages %}
107+
<li>{{ message }} </li>
108+
{% endfor %}
109+
</ul>
110+
{% endif %}
111+
{% endwith %}
112+
<!--End message flashing-->
113+
{% block content %}
114+
{% endblock %}
115+
<!-- Begin footer -->
116+
<div class="footer">
117+
<div class="container">
118+
<p class="muted">Designed and built lovingly by Sam Redmond</p>
119+
<p class="muted">This project is structured on <a href="http://getbootstrap.com/2.3.2/" target="_blank">Twitter Bootstrap v2.3.2</a></p>
120+
<p class="muted">Code licensed under <a href="http://www.apache.org/licenses/LICENSE-2.0" target="_blank">Apache License v2.0</a> (actually MIT license), documentation under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.</p>
121+
<p class="muted"><a href="http://glyphicons.com">Glyphicons Free</a> licensed under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.</p>
122+
<ul class="footer-links">
123+
<li><a href="https://github.com/sredmond/menlo-mathsci-help" target="_blank">Contribute</a></li>
124+
<li class="muted">&middot;</li>
125+
<li><a href="https://github.com/sredmond/menlo-mathsci-help/issues?state=open" target="_blank">Issues</a></li>
126+
<li class="muted">&middot;</li>
127+
<li><a href="https://github.com/sredmond/menlo-mathsci-help/releases" target="_blank">Changelog</a></li>
128+
</ul>
129+
</div>
130+
</div>
131+
<!-- End footer -->
132+
133+
<!-- Scripts -->
134+
<!-- Placed at the end of the document so the pages load faster-->
135+
<script src="http://code.jquery.com/jquery.js"></script>
136+
<script src="/static/js/bootstrap.js"></script>
137+
<script src="/static/js/main.js"></script>
138+
</body>
139+
</html>
File renamed without changes.

app/templates/login.html

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{% extends "layout.html" %}
2+
{% block content %}
3+
<script type="text/javascript">
4+
function set_openid(openid, pr)
5+
{
6+
u = openid.search('<username>');
7+
if (u != -1) {
8+
// openid requires username
9+
user = prompt('Enter your ' + pr + ' username:');
10+
openid = openid.substr(0, u) + user;
11+
}
12+
form = document.forms['login'];
13+
form.elements['openid'].value = openid;
14+
}
15+
</script>
16+
<h1>Sign In</h1>
17+
<form action="" method="post" name="login">
18+
{{form.hidden_tag()}}
19+
<p>
20+
Please enter your OpenID, or select one of the providers below:<br>
21+
{{form.openid(size=80)}}
22+
{% for error in form.errors.openid %}
23+
<span style="color: red;">[{{error}}]</span>
24+
{% endfor %}<br>
25+
</p>
26+
<p>{{form.remember_me}} Remember Me</p>
27+
<p><input type="submit" value="Sign In"></p>
28+
</form>
29+
<!--<div class="container-fluid">
30+
<div class="hero-unit">
31+
<h1>Login</h1>
32+
{% if error %}
33+
<p class="error"><strong>Error:</strong> {{ error }}</p>
34+
{% endif %}
35+
<form action="" method="post">
36+
<dl>
37+
<dt>Username:</dt>
38+
<dd><input type="text" name="username" value={{ request.form.username }}></dd>
39+
<dt>Password:</dt>
40+
<dd><input type="password" name="password"></dd>
41+
</dl>
42+
<p><input type=submit value=Login></p>
43+
</form>
44+
</div>
45+
</div>-->
46+
{% endblock %}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)