Skip to content

Commit 586a56d

Browse files
authoredApr 27, 2017
Merge pull request #134 from ComputerScienceHouse/develop
Conditional v1.4.0
·
v1.12.0v1.4.0
2 parents 6abd03b + 5c8af5a commit 586a56d

26 files changed

+262
-65
lines changed
 

‎conditional/__init__.py

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
11
import os
22
import subprocess
3-
from flask import Flask
4-
from flask import redirect
3+
from flask import Flask, redirect, request, render_template, g
54
from flask_sqlalchemy import SQLAlchemy
65
from flask_migrate import Migrate
76
from csh_ldap import CSHLDAP
7+
from raven import fetch_git_sha
8+
from raven.contrib.flask import Sentry
9+
from raven.exceptions import InvalidGitRepository
810
import structlog
911

1012
app = Flask(__name__)
1113

12-
config = os.path.join(os.getcwd(), "config.py")
14+
config = os.path.join(app.config.get('ROOT_DIR', os.getcwd()), "config.py")
1315

1416
app.config.from_pyfile(config)
1517
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
1618

17-
app.config["GIT_REVISION"] = subprocess.check_output(['git',
18-
'rev-parse',
19-
'--short',
20-
'HEAD']).decode('utf-8').rstrip()
19+
try:
20+
app.config["GIT_REVISION"] = fetch_git_sha(app.config["ROOT_DIR"])[:7]
21+
except (InvalidGitRepository, KeyError):
22+
app.config["GIT_REVISION"] = "unknown"
23+
2124
db = SQLAlchemy(app)
2225
migrate = Migrate(app, db)
2326
logger = structlog.get_logger()
27+
sentry = Sentry(app)
2428

2529
ldap = CSHLDAP(app.config['LDAP_BIND_DN'],
2630
app.config['LDAP_BIND_PW'],
@@ -52,6 +56,7 @@
5256
app.register_blueprint(slideshow_bp)
5357
app.register_blueprint(cache_bp)
5458

59+
from conditional.util.ldap import ldap_get_member
5560

5661
@app.route('/<path:path>')
5762
def static_proxy(path):
@@ -63,6 +68,42 @@ def static_proxy(path):
6368
def default_route():
6469
return redirect('/dashboard')
6570

71+
@app.errorhandler(404)
72+
@app.errorhandler(500)
73+
def route_errors(error):
74+
data = dict()
75+
username = request.headers.get('x-webauth-user')
76+
77+
# Handle the case where the header isn't present
78+
if username is not None:
79+
member = ldap_get_member(username)
80+
data['username'] = member.uid
81+
data['name'] = member.cn
82+
else:
83+
data['username'] = "unknown"
84+
data['name'] = "Unknown"
85+
86+
# Figure out what kind of error was passed
87+
if isinstance(error, int):
88+
code = error
89+
elif hasattr(error, 'code'):
90+
code = error.code
91+
else:
92+
# Unhandled exception
93+
code = 500
94+
95+
# Is this a 404?
96+
if code == 404:
97+
error_desc = "Page Not Found"
98+
else:
99+
error_desc = type(error).__name__
100+
101+
return render_template('errors.html',
102+
error=error_desc,
103+
error_code=code,
104+
event_id=g.sentry_event_id,
105+
public_dsn=sentry.client.get_public_dsn('https'),
106+
**data), int(code)
66107

67108
@app.cli.command()
68109
def zoo():

‎conditional/blueprints/attendance.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def get_non_alumni_non_coop(internal=False):
8080
for account in active_members:
8181
if account.uid in coop_members:
8282
# Members who are on co-op don't need to go to house meeting.
83-
pass
83+
continue
8484

8585
eligible_members.append(
8686
{
@@ -91,8 +91,8 @@ def get_non_alumni_non_coop(internal=False):
9191

9292
if internal:
9393
return eligible_members
94-
else:
95-
return jsonify({'members': eligible_members}), 200
94+
95+
return jsonify({'members': eligible_members}), 200
9696

9797

9898
@attendance_bp.route('/attendance/cm_members')
@@ -314,15 +314,15 @@ def alter_house_attendance(uid, hid):
314314
member_meeting.attendance_status = "Attended"
315315
db.session.commit()
316316
return jsonify({"success": True}), 200
317-
else:
318-
freshman_meeting = FreshmanHouseMeetingAttendance.query.filter(
319-
FreshmanHouseMeetingAttendance.fid == uid,
320-
FreshmanHouseMeetingAttendance.meeting_id == hid
321-
).first()
322317

323-
freshman_meeting.attendance_status = "Attended"
324-
db.session.commit()
325-
return jsonify({"success": True}), 200
318+
freshman_meeting = FreshmanHouseMeetingAttendance.query.filter(
319+
FreshmanHouseMeetingAttendance.fid == uid,
320+
FreshmanHouseMeetingAttendance.meeting_id == hid
321+
).first()
322+
323+
freshman_meeting.attendance_status = "Attended"
324+
db.session.commit()
325+
return jsonify({"success": True}), 200
326326

327327

328328
@attendance_bp.route('/attendance/alter/hm/<uid>/<hid>', methods=['POST'])

‎conditional/blueprints/conditional.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,5 +117,5 @@ def conditional_delete(cid):
117117
db.session.flush()
118118
db.session.commit()
119119
return jsonify({"success": True}), 200
120-
else:
121-
return "Must be evals director to delete!", 401
120+
121+
return "Must be evals director to delete!", 401

‎conditional/blueprints/intro_evals.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from conditional.models.models import FreshmanCommitteeAttendance
1111
from conditional.models.models import MemberCommitteeAttendance
12+
from conditional.models.models import CommitteeMeeting
1213
from conditional.models.models import FreshmanAccount
1314
from conditional.models.models import FreshmanEvalData
1415
from conditional.models.models import FreshmanHouseMeetingAttendance
@@ -33,11 +34,13 @@ def display_intro_evals(internal=False):
3334
# get user data
3435
def get_uid_cm_count(member_id):
3536
return len([a for a in MemberCommitteeAttendance.query.filter(
36-
MemberCommitteeAttendance.uid == member_id)])
37+
MemberCommitteeAttendance.uid == member_id)
38+
if CommitteeMeeting.query.filter(CommitteeMeeting.id == a.meeting_id).approved])
3739

3840
def get_fid_cm_count(member_id):
3941
return len([a for a in FreshmanCommitteeAttendance.query.filter(
40-
FreshmanCommitteeAttendance.fid == member_id)])
42+
FreshmanCommitteeAttendance.fid == member_id)
43+
if CommitteeMeeting.query.filter(CommitteeMeeting.id == a.meeting_id).approved])
4144

4245
user_name = None
4346
if not internal:
@@ -88,7 +91,8 @@ def get_fid_cm_count(member_id):
8891
[s.name for s in TechnicalSeminar.query.filter(
8992
TechnicalSeminar.id.in_(
9093
[a.seminar_id for a in FreshmanSeminarAttendance.query.filter(
91-
FreshmanSeminarAttendance.fid == fid.id)]
94+
FreshmanSeminarAttendance.fid == fid.id)
95+
if TechnicalSeminar.query.filter(TechnicalSeminar.id == a.seminar_id).approved]
9296
))
9397
],
9498
'social_events': '',
@@ -141,7 +145,8 @@ def get_fid_cm_count(member_id):
141145
[s.name for s in TechnicalSeminar.query.filter(
142146
TechnicalSeminar.id.in_(
143147
[a.seminar_id for a in MemberSeminarAttendance.query.filter(
144-
MemberSeminarAttendance.uid == uid)]
148+
MemberSeminarAttendance.uid == uid)
149+
if TechnicalSeminar.query.filter(TechnicalSeminar.id == a.seminar_id).approved]
145150
))
146151
],
147152
'social_events': freshman_data.social_events,
@@ -160,9 +165,9 @@ def get_fid_cm_count(member_id):
160165

161166
if internal:
162167
return ie_members
163-
else:
164-
# return names in 'first last (username)' format
165-
return render_template(request,
166-
'intro_evals.html',
167-
username=user_name,
168-
members=ie_members)
168+
169+
# return names in 'first last (username)' format
170+
return render_template(request,
171+
'intro_evals.html',
172+
username=user_name,
173+
members=ie_members)

‎conditional/blueprints/intro_evals_form.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from conditional.models.models import FreshmanEvalData
77
from conditional.models.models import EvalSettings
88
from conditional.util.ldap import ldap_is_intromember
9+
from conditional.util.ldap import ldap_get_member
910
from conditional.util.flask import render_template
1011

1112
from conditional import db
@@ -23,8 +24,8 @@ def display_intro_evals_form():
2324

2425
# get user data
2526
user_name = request.headers.get('x-webauth-user')
26-
27-
if not ldap_is_intromember(user_name):
27+
account = ldap_get_member(user_name)
28+
if not ldap_is_intromember(account):
2829
return redirect("/dashboard")
2930
eval_data = FreshmanEvalData.query.filter(
3031
FreshmanEvalData.uid == user_name).first()

‎conditional/blueprints/major_project_submission.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,5 +121,5 @@ def major_project_delete(pid):
121121
db.session.flush()
122122
db.session.commit()
123123
return jsonify({"success": True}), 200
124-
else:
125-
return "Must be project owner to delete!", 401
124+
125+
return "Must be project owner to delete!", 401

‎conditional/blueprints/member_management.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -373,13 +373,13 @@ def get_hm_date(hm_id):
373373
'missed_hm': hms_missed,
374374
'user': 'eval'
375375
}), 200
376-
else:
377-
return jsonify(
378-
{
379-
'name': account.cn,
380-
'active_member': ldap_is_active(account),
381-
'user': 'financial'
382-
}), 200
376+
377+
return jsonify(
378+
{
379+
'name': account.cn,
380+
'active_member': ldap_is_active(account),
381+
'user': 'financial'
382+
}), 200
383383

384384

385385
@member_management_bp.route('/manage/user/<fid>', methods=['DELETE'])

‎conditional/blueprints/spring_evals.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from conditional.util.ldap import ldap_get_active_members
77

88
from conditional.models.models import MemberCommitteeAttendance
9+
from conditional.models.models import CommitteeMeeting
910
from conditional.models.models import MemberHouseMeetingAttendance
1011
from conditional.models.models import MajorProject
1112
from conditional.models.models import HouseMeeting
@@ -28,7 +29,8 @@ def display_spring_evals(internal=False):
2829

2930
def get_cm_count(member_id):
3031
return len([a for a in MemberCommitteeAttendance.query.filter(
31-
MemberCommitteeAttendance.uid == member_id)])
32+
MemberCommitteeAttendance.uid == member_id)
33+
if CommitteeMeeting.query.filter(CommitteeMeeting.id == a.meeting_id).approved])
3234

3335
user_name = None
3436
if not internal:
@@ -87,6 +89,14 @@ def get_cm_count(member_id):
8789
MajorProject.uid == uid)]
8890
}
8991
member['major_projects_len'] = len(member['major_projects'])
92+
member['major_project_passed'] = [
93+
{
94+
'name': p.name,
95+
'status': p.status,
96+
'description': p.description
97+
} for p in MajorProject.query.filter(MajorProject.uid == uid)
98+
if p.status == "Passed"]
99+
member['major_projects_passed_len'] = len(member['major_projects_passed'])
90100
member['major_project_passed'] = False
91101
for mp in member['major_projects']:
92102
if mp['status'] == "Passed":
@@ -103,8 +113,8 @@ def get_cm_count(member_id):
103113
# return names in 'first last (username)' format
104114
if internal:
105115
return sp_members
106-
else:
107-
return render_template(request,
108-
'spring_evals.html',
109-
username=user_name,
110-
members=sp_members)
116+
117+
return render_template(request,
118+
'spring_evals.html',
119+
username=user_name,
120+
members=sp_members)

‎conditional/templates/base.html

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,14 @@
2323
{% block body %}
2424
{% endblock %}
2525
{% else %}
26-
<div class="container main">
27-
<div class="alert alert-danger" role="alert"><span class="glyphicon glyphicon-remove-sign"></span> Site Lockdown: Evaluations in progress. Please check back later.</div>
26+
<div class="container main error-page align-center">
27+
<div class="col-xs-12">
28+
<img src="/static/images/material_lock.svg" alt="Attention!">
29+
<h1>Congratulations or I'm Sorry...</h1>
30+
<h2>We cannot reveal the results of your evaluation!</h2>
31+
<h3>Evaluations underway:</h3>
32+
<p>Conditional is in Lockdown</p>
33+
</div>
2834
</div>
2935
{% endif %}
3036

‎conditional/templates/errors.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{% extends "nav.html" %}
2+
{% block title %}Error {{ error_code }}{% endblock %}
3+
{% block body %}
4+
<div class="container main error-page align-center">
5+
<div class="col-xs-12">
6+
<img src="/static/images/material_attention.svg" alt="Attention!">
7+
<h1>Congratulations or I'm Sorry...</h1>
8+
<h2>Something has gone terribly wrong!</h2>
9+
<h3>{{ error }}</h3>
10+
{% if event_id %}
11+
<p>The error identifier is: {{ event_id }}</p>
12+
{% endif %}
13+
</div>
14+
{% if event_id %}
15+
<button type="button" class="btn btn-primary" data-module="errorReport" data-event="{{ event_id }}">Report this Error</button>
16+
{% endif %}
17+
</div>
18+
19+
{% endblock %}

‎conditional/templates/intro_evals.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,12 @@ <h6 class="eval-uid">{{ m['uid'] }}</h6>
4545
<div class="text-center">
4646
{% if m['signatures_missed'] == 0 %}
4747
<div class="eval-info-label">
48-
<span class="glyphicon glyphicon-ok-sign green eval-info-status"></span>Sigantures Missed
48+
<span class="glyphicon glyphicon-ok-sign green eval-info-status"></span>Signatures Missed
4949
<span class="eval-info-number">{{ m['signatures_missed'] }}</span>
5050
</div>
5151
{% elif m['signatures_missed'] > 0 %}
5252
<div class="eval-info-label">
53-
<span class="glyphicon glyphicon-remove-sign red eval-info-status"></span>Sigantures Missed
53+
<span class="glyphicon glyphicon-remove-sign red eval-info-status"></span>Signatures Missed
5454
<span class="eval-info-number">{{ m['signatures_missed'] }}</span>
5555
</div>
5656
{% else %}

‎conditional/templates/major_project_submission.html

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,17 @@ <h3 class="page-title">Major Project Form</h3>
1111
<div class="panel-body">
1212
<div class="form-group label-floating is-empty">
1313
<label class="control-label" for="name">Project Name</label>
14-
<input class="form-control" id="name" name="name" type="text" maxlength="64">
14+
<input class="form-control" id="name" name="name" type="text" maxlength="64"
15+
placeholder="A clever name for your project, sometimes people will come up with an acronym.">
1516
</div>
1617
</div>
1718
</div>
1819
<div class="panel panel-default">
1920
<div class="panel-body" style="padding-top:20px;">
2021
<div class="form-group label-floating is-empty">
2122
<label class="control-label" for="description">Description</label>
22-
<textarea name="description" class="form-control" rows="3" id="description"></textarea>
23+
<textarea name="description" class="form-control" rows="3" id="description"
24+
placeholder="A 'two liner' description of what your project is. If you have source materials like a GitHub repo publicly available, it's also useful to include links to them."></textarea>
2325
</div>
2426
</div>
2527
</div>
@@ -79,7 +81,7 @@ <h5 style="padding:15px 20px;float:right"><span class="glyphicon glyphicon-hourg
7981
{% endif %}
8082
</div>
8183
<button class="btn-expand-panel" role="button" data-toggle="collapse" href="#evalsCollapse-{{p['id']}}" aria-expanded="false" aria-controls="evalsCollapse-{{p['id']}}"><span class="glyphicon glyphicon glyphicon-menu-down"></span></button>
82-
<div class="collapse" id="evalsCollapse-{{p['id']}}">
84+
<div class="collapse major-project-desc" id="evalsCollapse-{{p['id']}}">
8385
{{p['description']}}
8486
</div>
8587
</div>

‎conditional/templates/spring_eval_slideshow.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,17 @@ <h3>{{m['house_meetings_missed']|length}}</h3>
3131
</div>
3232
</div>
3333
<div class="col-xs-12 col-md-4">
34-
{% set major_projects_passed = m['major_projects_len'] >= 1 %}
35-
<div class="item{% if major_projects_passed %} passed{% endif %}">
36-
<span class="icon glyphicon glyphicon-{% if major_projects_passed %}ok passed{%else%}remove{% endif %}" aria-hidden="true"></span>
37-
<h3>{{m['major_projects_len']}}</h3>
34+
{% set major_projects_passed = m['major_projects_passed_len'] >= 1 %}
35+
<div class="item{% if major_projects_passed_len %} passed{% endif %}">
36+
<span class="icon glyphicon glyphicon-{% if major_projects_passed_len %}ok passed{%else%}remove{% endif %}" aria-hidden="true"></span>
37+
<h3>{{m['major_projects_passed_len']}}</h3>
3838
<p>Major Projects</p>
3939
</div>
4040
</div>
4141
</div>
4242
<h4>Major Projects:
43-
{% for i in range(m['major_projects_len']) %}
44-
{{ m['major_projects'][i]['name'] }}{% if i < m['major_projects_len'] - 1 %}, {% endif %}
43+
{% for i in range(m['major_projects_passed_len']) %}
44+
{{ m['major_projects_passed'][i]['name'] }}{% if i < m['major_projects_passed_len'] - 1 %}, {% endif %}
4545
{% endfor %}
4646
</h4>
4747

‎conditional/util/flask.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def render_template(request, template_name, **kwargs):
4040
ts_review = len(TechnicalSeminar.query.filter(
4141
TechnicalSeminar.approved == False).all()) # pylint: disable=singleton-comparison
4242

43-
if is_eval:
43+
if is_eboard:
4444
lockdown = False
4545

4646
return flask_render_template(

‎conditional/util/ldap.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,7 @@ def ldap_is_financial_director(account):
8888

8989

9090
def ldap_is_eval_director(account):
91-
# TODO FIXME Evaulations -> Evaluations
92-
return _ldap_is_member_of_directorship(account, 'Evaulations')
91+
return _ldap_is_member_of_directorship(account, 'Evaluations')
9392

9493

9594
def ldap_is_current_student(account):
@@ -103,6 +102,8 @@ def ldap_set_housingpoints(account, housing_points):
103102

104103

105104
def ldap_set_roomnumber(account, room_number):
105+
if room_number == "":
106+
room_number = None
106107
account.roomNumber = room_number
107108
ldap_get_current_students.cache_clear()
108109
ldap_get_member.cache_clear()

‎config.sample.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import os
2+
from raven import fetch_git_sha
23

34
# Flask config
45
DEBUG = True
6+
ROOT_DIR = os.path.dirname(__file__)
57
HOST_NAME = 'localhost'
68
APP_NAME = 'conditional'
79
IP = '0.0.0.0'
@@ -12,6 +14,13 @@
1214
LDAP_BIND_DN = 'cn=conditional,ou=Apps,dc=csh,dc=rit,dc=edu'
1315
LDAP_BIND_PW = ''
1416

17+
# Sentry config
18+
# Do not set the DSN for local development
19+
SENTRY_CONFIG = {
20+
'dsn': '',
21+
'release': fetch_git_sha(ROOT_DIR),
22+
}
23+
1524
# Database config
1625
SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format(os.path.join(os.getcwd(), "data.db"))
1726
ZOO_DATABASE_URI = 'mysql+pymysql://user:pass@host/database'
Lines changed: 12 additions & 0 deletions
Loading

‎frontend/images/material_lock.svg

Lines changed: 14 additions & 0 deletions
Loading

‎frontend/javascript/app.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
// Install Raven to send errors to Sentry
2+
import Raven from 'raven-js';
3+
Raven
4+
.config('https://151ecfab1a8242009012d45a19064cfd@sentry.io/133175')
5+
.install();
6+
7+
// Capture unhandled exceptions in promises
8+
window.addEventListener('unhandledrejection', err => {
9+
Raven.captureException(err.reason);
10+
});
11+
12+
// Load the rest of the modules
113
import "jquery";
214
import "bootstrap";
315
import "./modules";
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Raven from 'raven-js';
2+
3+
export default class ErrorReport {
4+
constructor(btn) {
5+
this.btn = btn;
6+
this.eventId = this.btn.dataset.event;
7+
this.render();
8+
}
9+
10+
render() {
11+
this.btn.addEventListener('click', () => this._invokeRavenModal());
12+
}
13+
14+
_invokeRavenModal() {
15+
Raven.showReportDialog({
16+
eventId: this.eventId
17+
});
18+
}
19+
}

‎frontend/stylesheets/app.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
@import 'pages/dashboard';
2323
@import 'pages/evals';
2424
@import 'pages/housing';
25+
@import 'pages/major-project';
2526
@import 'pages/management';
2627
@import 'pages/hm-attendance';
2728
@import 'pages/attendance-hist';
29+
@import 'pages/errors';
2830
@import 'components/reveal';
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
.error-page {
2+
padding-top: 80px;
3+
4+
.glyphicon,
5+
h1,
6+
h2,
7+
h3,
8+
p {
9+
color: #878787;
10+
}
11+
12+
img {
13+
margin-bottom: 40px;
14+
width: 200px;
15+
}
16+
17+
h1 {
18+
font-size: 50px;
19+
font-weight: bolder;
20+
}
21+
22+
h2 {
23+
margin-bottom: 50px;
24+
font-size: 30px;
25+
}
26+
27+
h3 {
28+
font-size: 25px;
29+
}
30+
31+
p {
32+
font-size: 15px;
33+
font-weight: lighter;
34+
}
35+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.major-project-desc {
2+
white-space: pre-line;
3+
}

‎frontend/stylesheets/partials/_global.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
.navbar-fixed-bottom, .navbar-fixed-top {
2+
z-index: 900;
3+
}
4+
15
.sub-header {
26
border-bottom: 1px solid #eee;
37
padding-bottom: 10px;
@@ -68,7 +72,7 @@ tr {
6872
.footer-version {
6973
display: block;
7074
opacity: .3;
71-
margin: 10px auto 20px;
75+
margin: 50px auto 20px;
7276
text-align: center;
7377
color: #000;
7478
font-size: .9em;

‎package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"object-assign": "4.1.0",
9090
"open": "0.0.5",
9191
"pretty-hrtime": "1.0.2",
92+
"raven-js": "^3.9.2",
9293
"require-dir": "0.3.0",
9394
"sinon": "1.17.4",
9495
"sinon-chai": "2.8.0",

‎requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ structlog
99
flask_sqlalchemy
1010
flask_migrate
1111
pylint
12-
psycopg2
12+
#psycopg2
13+
raven[flask]

0 commit comments

Comments
 (0)
Please sign in to comment.