Skip to content

Commit 22a3a89

Browse files
authored
Add Ability to Alter Committee Meeting Attendance (#106)
* Committee Meeting History page * Selector control for attendance history * Small attendance history refactor and fix submission * Move history pagination in path to query string * Add delete functionality * Remove unneeded vcenter references
1 parent a447028 commit 22a3a89

File tree

11 files changed

+358
-6
lines changed

11 files changed

+358
-6
lines changed

conditional/blueprints/attendance.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,113 @@ def alter_house_excuse(uid, hid):
377377
db.session.flush()
378378
db.session.commit()
379379
return jsonify({"success": True}), 200
380+
381+
382+
@attendance_bp.route('/attendance/history', methods=['GET'])
383+
def attendance_history():
384+
385+
386+
def get_meeting_attendees(meeting_id):
387+
attendees = [ldap_get_member(a.uid).displayName for a in
388+
MemberCommitteeAttendance.query.filter(
389+
MemberCommitteeAttendance.meeting_id == meeting_id).all()]
390+
391+
for freshman in [a.fid for a in
392+
FreshmanCommitteeAttendance.query.filter(
393+
FreshmanCommitteeAttendance.meeting_id == meeting_id).all()]:
394+
attendees.append(FreshmanAccount.query.filter(
395+
FreshmanAccount.id == freshman).first().name)
396+
return attendees
397+
398+
log = logger.new(user_name=request.headers.get("x-webauth-user"),
399+
request_id=str(uuid.uuid4()))
400+
401+
user_name = request.headers.get('x-webauth-user')
402+
account = ldap_get_member(user_name)
403+
if not ldap_is_eboard(account):
404+
return "must be eboard", 403
405+
406+
if request.method == 'GET':
407+
page = request.args.get('page', 1)
408+
log.info('api', action='view past attendance submitions')
409+
offset = 0 if int(page) == 1 else ((int(page)-1)*10)
410+
limit = int(page)*10
411+
all_cm = [{"meeting_id": m.id,
412+
"directorship": m.committee,
413+
"dt_obj": m.timestamp,
414+
"date": m.timestamp.strftime("%a %m/%d/%Y"),
415+
"attendees": get_meeting_attendees(m.id)
416+
} for m in CommitteeMeeting.query.all()]
417+
c_meetings = sorted(all_cm, key=lambda k: k['dt_obj'], reverse=True)[offset:limit]
418+
if len(all_cm) % 10 != 0:
419+
total_pages = (int(len(all_cm) / 10) + 1)
420+
else:
421+
total_pages = (int(len(all_cm) / 10))
422+
return render_template(request,
423+
'attendance_history.html',
424+
username=user_name,
425+
history=c_meetings,
426+
num_pages=total_pages,
427+
current_page=int(page))
428+
429+
@attendance_bp.route('/attendance/alter/cm/<cid>', methods=['POST'])
430+
def alter_committee_attendance(cid):
431+
log = logger.new(user_name=request.headers.get("x-webauth-user"),
432+
request_id=str(uuid.uuid4()))
433+
log.info('api', action='edit committee meeting attendance')
434+
435+
user_name = request.headers.get('x-webauth-user')
436+
437+
account = ldap_get_member(user_name)
438+
if not ldap_is_eboard(account):
439+
return "must be eboard", 403
440+
441+
post_data = request.get_json()
442+
meeting_id = cid
443+
m_attendees = post_data['members']
444+
f_attendees = post_data['freshmen']
445+
446+
FreshmanCommitteeAttendance.query.filter(
447+
FreshmanCommitteeAttendance.meeting_id == meeting_id).delete()
448+
449+
MemberCommitteeAttendance.query.filter(
450+
MemberCommitteeAttendance.meeting_id == meeting_id).delete()
451+
452+
for m in m_attendees:
453+
db.session.add(MemberCommitteeAttendance(m, meeting_id))
454+
455+
for f in f_attendees:
456+
db.session.add(FreshmanCommitteeAttendance(f, meeting_id))
457+
458+
db.session.flush()
459+
db.session.commit()
460+
return jsonify({"success": True}), 200
461+
462+
@attendance_bp.route('/attendance/cm/<cid>', methods=['GET', 'DELETE'])
463+
def get_cm_attendees(cid):
464+
if request.method == 'GET':
465+
attendees = [{"value": a.uid,
466+
"display": ldap_get_member(a.uid).displayName
467+
} for a in
468+
MemberCommitteeAttendance.query.filter(
469+
MemberCommitteeAttendance.meeting_id == cid).all()]
470+
471+
for freshman in [{"value": a.fid,
472+
"display": FreshmanAccount.query.filter(FreshmanAccount.id == a.fid).first().name
473+
} for a in FreshmanCommitteeAttendance.query.filter(
474+
FreshmanCommitteeAttendance.meeting_id == cid).all()]:
475+
attendees.append(freshman)
476+
return jsonify({"attendees": attendees}), 200
477+
478+
elif request.method == 'DELETE':
479+
FreshmanCommitteeAttendance.query.filter(
480+
FreshmanCommitteeAttendance.meeting_id == cid).delete()
481+
MemberCommitteeAttendance.query.filter(
482+
MemberCommitteeAttendance.meeting_id == cid).delete()
483+
CommitteeMeeting.query.filter(
484+
CommitteeMeeting.id == cid).delete()
485+
486+
db.session.flush()
487+
db.session.commit()
488+
489+
return jsonify({"success": True}), 200
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
{% extends "nav.html" %}
2+
{% block title %}
3+
Attendance History
4+
{% endblock %}
5+
{% block body %}
6+
<div class="container main">
7+
<h3 class="page-title">Attendance History</h3>
8+
{% for meeting in history %}
9+
<div class="panel panel-default">
10+
<div class="panel-body eval-panel">
11+
<div class="container-fluid">
12+
<div class="col-xs-12 col-sm-3 vcenter">
13+
<h5>{{meeting["directorship"]}}</h5>
14+
<p>{{meeting["date"]}}<p>
15+
</div><!--
16+
--><div class="com-xs-12 col-sm-7 vcenter">
17+
<p class="attend-list" id="attendees-{{meeting["id"]}}">
18+
{% for name in meeting["attendees"] %}
19+
{{name}}{% if not loop.last %}, {% endif %}
20+
{% endfor %}
21+
</p>
22+
</div><!--
23+
--><div class="col-xs-12 col-sm-2 vcenter text-center">
24+
<button type="button" class="btn btn-default navbar-btn" data-module="cmAttendance" data-modal="editMeeting" data-cid="{{meeting["id"]}}">
25+
<span class="glyphicon glyphicon-edit attend-edit-icon"></span> Edit
26+
</button>
27+
</div>
28+
</div>
29+
</div>
30+
</div>
31+
{% endfor %}
32+
{% if num_pages > 1 or current_page > 1%}
33+
34+
<nav aria-label="Page navigation" class="align-center">
35+
<ul class="pagination">
36+
<li {%if current_page == 1 %}class="disabled"{% endif %}>
37+
{%if current_page != 1 %}
38+
<a href="?page={{ current_page - 1 }}" aria-label="Previous">
39+
{% endif %}
40+
<span aria-hidden="true">&laquo;</span>
41+
</a>
42+
</li>
43+
{% for number in range(1, num_pages+1) %}
44+
<li {% if number == current_page %} class="active" {% endif %}>
45+
<a href="?page={{ number }}">{{ number }}</a>
46+
</li>
47+
{% endfor %}
48+
<li {%if current_page == num_pages %}class="disabled"{% endif %}>
49+
{%if current_page != num_pages %}
50+
<a href="?page={{ current_page + 1 }}" aria-label="Next">
51+
{% endif %}
52+
<span aria-hidden="true">&raquo;</span>
53+
</a>
54+
</li>
55+
</ul>
56+
</nav>
57+
{% endif %}
58+
</div>
59+
60+
<div class="modal fade" id="editMeeting" tabindex="-1">
61+
<div class="vertical-alignment-helper">
62+
<div class="modal-dialog vertical-align-center">
63+
<div class="modal-content">
64+
<div class="modal-header">
65+
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
66+
<h4 class="modal-title" id="editMeetingTitle">Edit Meeting</h4>
67+
</div>
68+
<form method="post">
69+
<div class="modal-body">
70+
<div class="row user-edit-row">
71+
<label class="control-label" for="attendees">Attendees</label>
72+
<input type="text" name="attendees" class="form-control" />
73+
</div>
74+
</div>
75+
<div class="modal-footer">
76+
<button type="button" class="btn btn-danger delete-btn pull-left">Delete</button>
77+
<input type="submit" class="btn btn-primary" value="Submit">
78+
</div>
79+
</form>
80+
</div>
81+
</div>
82+
</div>
83+
</div>
84+
{% endblock %}

conditional/templates/dashboard.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
<div class="panel panel-default">
88
<div class="panel-body dashboard-user">
99
<div class="row">
10-
<div class="col-md-2 col-sm-3 col-xs-12 vcenter profile-container">
10+
<div class="col-md-2 col-sm-3 col-xs-12 profile-container">
1111
<img class="profile-image" src="https://profiles.csh.rit.edu/image/{{username}}">
1212
</div>
13-
<div class="col-xs-12 col-sm-9 col-md-9 vcenter profile-container">
13+
<div class="col-xs-12 col-sm-9 col-md-9 profile-container">
1414
<h3 class="username">{{name}}</h3>
1515
<h5 class="email">{{username}}@csh.rit.edu</h5>
1616
<div class="profile-badges">

conditional/templates/intro_evals.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
<div class="container-fluid">
1414
<div class="row">
15-
<div class="col-sm-2 col-md-2 col-lg-2 vcenter">
15+
<div class="col-sm-2 col-md-2 col-lg-2">
1616
<div style="margin:auto; width:100px">
1717
<img class="eval-user-img" alt="{{m['uid']}}" src="https://profiles.csh.rit.edu/image/{{m['uid']}}" width="100" height="100" />
1818
{% if m['ldap_account'] %}
@@ -40,7 +40,7 @@ <h6 class="eval-uid">{{ m['uid'] }}</h6>
4040

4141
</div>
4242
<!---->
43-
<div class="col-sm-5 col-md-6 col-lg-4 vcenter">
43+
<div class="col-sm-5 col-md-6 col-lg-4">
4444
<div class="intro-info row">
4545
<div class="text-center">
4646
{% if m['signatures_missed'] == 0 %}

conditional/templates/nav.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-ok"></span> Attendance <span class="caret"></span></a>
3838
<ul class="dropdown-menu">
3939
<li><a href="/attendance_cm"><span class="glyphicon glyphicon-briefcase"></span> Directorship Meeting </a></li>
40+
<li><a href="/attendance/history"><span class="glyphicon glyphicon-calendar"></span> Attendance History </a></li>
4041
<li><a href="/attendance_ts"><span class="glyphicon glyphicon-blackboard"></span> Technical Seminar</a></li>
4142
{% if is_eval_director %}
4243
<li><a href="/attendance_hm"><span class="glyphicon glyphicon-home"></span> House Meeting</a></li>

conditional/templates/spring_evals.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<div class="container-fluid">
1313
<div class="row">
14-
<div class="col-sm-2 col-md-2 col-lg-2 vcenter">
14+
<div class="col-sm-2 col-md-2 col-lg-2">
1515
<div style="margin:auto; width:100px">
1616
<img class="eval-user-img" alt="{{m['uid']}}" src="https://profiles.csh.rit.edu/image/{{m['uid']}}" width="100" height="100" />
1717

@@ -31,7 +31,7 @@ <h4 class="eval-name">{{m['name']}}</h4>
3131
<h6 class="eval-uid">{{m['uid']}}</h6>
3232
</div>
3333
<!---->
34-
<div class="col-sm-5 col-md-6 col-lg-4 vcenter">
34+
<div class="col-sm-5 col-md-6 col-lg-4">
3535
<div class="spring-info row">
3636
<div class="text-center">
3737
{% if m['committee_meetings'] < 25 %}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {Enum} from 'enumify';
2+
3+
class CmAttendanceException extends Enum {
4+
}
5+
6+
CmAttendanceException.initEnum({
7+
SUBMIT_BEFORE_RENDER: {
8+
get message() {
9+
return "Cannot submit updated attendance before the modal renders.";
10+
}
11+
}
12+
});
13+
14+
export default CmAttendanceException;

0 commit comments

Comments
 (0)