Skip to content

Allow Members to Opt-In to Active Status #147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions conditional/blueprints/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from conditional.util.ldap import ldap_is_onfloor
from conditional.util.ldap import ldap_is_active
from conditional.util.ldap import ldap_is_intromember
from conditional.util.ldap import ldap_is_current_student
from conditional.util.ldap import ldap_get_member
from conditional.util.ldap import ldap_get_active_members

Expand Down Expand Up @@ -44,6 +45,8 @@ def display_dashboard():
data['active'] = ldap_is_active(member)
data['onfloor'] = ldap_is_onfloor(member)
data['voting'] = bool(member.uid in can_vote)
data['student'] = ldap_is_current_student(member)


data['voting_count'] = {"Voting Members": len(can_vote),
"Active Members": len(ldap_get_active_members())}
Expand Down
77 changes: 76 additions & 1 deletion conditional/blueprints/member_management.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import csv
import io
import re

from datetime import datetime

import structlog

from flask import Blueprint, request, jsonify, abort
from flask import Blueprint, request, jsonify, abort, make_response

from conditional import app

from conditional.models.models import FreshmanAccount
from conditional.models.models import FreshmanEvalData
Expand All @@ -28,6 +31,7 @@
from conditional.util.ldap import ldap_is_financial_director
from conditional.util.ldap import ldap_is_active
from conditional.util.ldap import ldap_is_onfloor
from conditional.util.ldap import ldap_is_current_student
from conditional.util.ldap import ldap_set_roomnumber
from conditional.util.ldap import ldap_set_active
from conditional.util.ldap import ldap_set_inactive
Expand Down Expand Up @@ -87,9 +91,11 @@ def display_member_management():
if settings:
lockdown = settings.site_lockdown
intro_form = settings.intro_form_active
accept_dues_until = settings.accept_dues_until
else:
lockdown = False
intro_form = False
accept_dues_until = datetime.now()

return render_template(request, "member_management.html",
username=username,
Expand All @@ -101,6 +107,7 @@ def display_member_management():
freshmen=freshmen_list,
co_op=co_op_list,
site_lockdown=lockdown,
accept_dues_until=accept_dues_until,
intro_form=intro_form)


Expand Down Expand Up @@ -135,6 +142,31 @@ def member_management_eval():
return jsonify({"success": True}), 200


@member_management_bp.route('/manage/accept_dues_until', methods=['PUT'])
def member_management_financial():
log = logger.new(request=request)

username = request.headers.get('x-webauth-user')
account = ldap_get_member(username)

if not ldap_is_financial_director(account):
return "must be financial director", 403

post_data = request.get_json()

if 'acceptDuesUntil' in post_data:
date = datetime.strptime(post_data['acceptDuesUntil'], "%Y-%m-%d")
log.info('Changed Dues Accepted Until: {}'.format(date))
EvalSettings.query.update(
{
'accept_dues_until': date
})

db.session.flush()
db.session.commit()
return jsonify({"success": True}), 200


@member_management_bp.route('/manage/user', methods=['POST'])
def member_management_adduser():
log = logger.new(request=request)
Expand Down Expand Up @@ -491,6 +523,23 @@ def member_management_upgrade_user():
return jsonify({"success": True}), 200


@member_management_bp.route('/manage/make_user_active', methods=['POST'])
def member_management_make_user_active():
log = logger.new(request=request)

uid = request.headers.get('x-webauth-user')
account = ldap_get_member(uid)

if not ldap_is_current_student(account) or ldap_is_active(account):
return "must be current student and not active", 403

ldap_set_active(account)
log.info("Make user {} active".format(uid))

clear_members_cache()
return jsonify({"success": True}), 200


@member_management_bp.route('/manage/intro_project', methods=['GET'])
def introductory_project():
log = logger.new(request=request)
Expand Down Expand Up @@ -583,6 +632,32 @@ def clear_active_members():
return jsonify({"success": True}), 200


@member_management_bp.route('/manage/export_active_list', methods=['GET'])
def export_active_list():
sio = io.StringIO()
csvw = csv.writer(sio)

active_list = [["Full Name", "RIT Username", "Amount to Charge"]]
for member in ldap_get_active_members():
full_name = member.cn
rit_username = re.search(".*uid=(\\w*)", member.ritDn).group(1)
will_coop = CurrentCoops.query.filter(
CurrentCoops.date_created > start_of_year(),
CurrentCoops.uid == member.uid).first()
dues_per_semester = app.config['DUES_PER_SEMESTER']
if will_coop:
dues = dues_per_semester
else:
dues = 2 * dues_per_semester
active_list.append([full_name, rit_username, dues])

csvw.writerows(active_list)
output = make_response(sio.getvalue())
output.headers["Content-Disposition"] = "attachment; filename=csh_active_list.csv"
output.headers["Content-type"] = "text/csv"
return output


@member_management_bp.route('/manage/current/<uid>', methods=['POST', 'DELETE'])
def remove_current_student(uid):
log = logger.new(request=request)
Expand Down
2 changes: 2 additions & 0 deletions conditional/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,13 @@ class EvalSettings(db.Model):
housing_form_active = Column(Boolean)
intro_form_active = Column(Boolean)
site_lockdown = Column(Boolean)
accept_dues_until = Column(Date)

def __init__(self):
self.housing_form_active = True
self.intro_form_active = True
self.site_lockdown = False
self.accept_dues_until = datetime.now()


class SpringEval(db.Model):
Expand Down
15 changes: 15 additions & 0 deletions conditional/templates/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,22 @@ <h3 class="panel-title">Major Projects</h3>
</div>
{% endif %}

{% if accepting_dues and student and not active %}
<div class="panel panel-info" id="becomeActive">
<div class="panel-heading">
<h3 class="panel-title">Become Active</h3>
</div>
<div class="panel-body">
Hey there, you're eligible to become an active member! Click the button below if you'd like to become active and pay dues.
</div>
<div class="panel-footer text-right">
<a href="#" data-module="becomeActive" class="btn btn-sm btn-default">Become Active</a>
</div>
</div>
{% endif %}

</div>

<div class="col-lg-6 col-md-6">
{% if housing %}
<div class="panel panel-default">
Expand Down
16 changes: 14 additions & 2 deletions conditional/templates/member_management.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ <h3 class="panel-title">Administration</h3>
</div>
<div class="panel-body">
<div class="container-fluid">
<div class="col-xs-6 col-sm-2{% if not is_eval_director %} col-sm-offset-2{% endif %}">
<div class="col-xs-6 col-sm-2">
<div class="stat-number">{{num_current}}</div>
<div class="stat-title">Current Students</div>
</div>
Expand All @@ -37,6 +37,11 @@ <h3 class="panel-title">Administration</h3>
<div class="col-xs-6 col-sm-2 align-center">
<a href="/manage/new" class="btn btn-danger btn-sm btn-new-year"><span class="glyphicon glyphicon-repeat"></span> New Year</a>
</div>
{% else %}
<div class="col-xs-6 col-sm-2 align-center">
<label for="acceptDuesUntil" class="control-label accept-dues-until">Accept Dues Until</label>
<input type="text" name="acceptDuesUntil" class="form-control" value="{{ accept_dues_until }}" data-module="acceptDuesDatepicker" data-setting="acceptDuesUntil" />
</div>
{% endif %}
</div>
</div>
Expand Down Expand Up @@ -166,7 +171,14 @@ <h3 class="panel-title">Freshmen Management</h3>

<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Member Management</h3>
<h3 class="panel-title">
Member Management
<a href="/manage/export_active_list">
<button type="button" class="btn btn-primary btn-sm btn-conditional pull-right">
<span class="glyphicon glyphicon-save"></span> Export Active List
</button>
</a>
</h3>
</div>
<div class="panel-body table-fill">
<div class="table-responsive">
Expand Down
4 changes: 4 additions & 0 deletions conditional/util/flask.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from datetime import date

from flask import render_template as flask_render_template
from conditional.models.models import EvalSettings

Expand Down Expand Up @@ -27,6 +29,7 @@ def render_template(request, template_name, **kwargs):
db.session.commit()
account = ldap_get_member(user_name)
lockdown = EvalSettings.query.first().site_lockdown
accepting_dues = EvalSettings.query.first().accept_dues_until > date.today()
is_active = ldap_is_active(account)
is_alumni = ldap_is_alumni(account)
is_eboard = ldap_is_eboard(account)
Expand All @@ -46,6 +49,7 @@ def render_template(request, template_name, **kwargs):
return flask_render_template(
template_name,
lockdown=lockdown,
accepting_dues=accepting_dues,
is_active=is_active,
is_alumni=is_alumni,
is_eboard=is_eboard,
Expand Down
3 changes: 3 additions & 0 deletions config.sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@
# Database config
SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format(os.path.join(os.getcwd(), "data.db"))
ZOO_DATABASE_URI = 'mysql+pymysql://user:pass@host/database'

# General config
DUES_PER_SEMESTER = 80
58 changes: 58 additions & 0 deletions frontend/javascript/modules/acceptDuesDatepicker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* global $ */
import "bootstrap-material-datetimepicker";
import "whatwg-fetch";
import FetchUtil from "../utils/fetchUtil";
import Exception from "../exceptions/exception";
import FetchException from "../exceptions/fetchException";
import sweetAlert from "../../../node_modules/bootstrap-sweetalert/dev/sweetalert.es6.js"; // eslint-disable-line max-len

export default class DatePicker {
constructor(input) {
this.input = input;
this.endpoint = '/manage/accept_dues_until';
this.setting = input.dataset.setting;
this.render();
}

render() {
$(this.input).bootstrapMaterialDatePicker({
weekStart: 0,
time: false
});

document.getElementsByClassName('dtp-btn-ok')[0].addEventListener('click',
() => {
this._updateSetting();
});
}

_updateSetting() {
console.log("Update dues until: " + this.input.value);
let payload = {};
payload[this.setting] = this.input.value;

fetch(this.endpoint, {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
credentials: 'same-origin',
body: JSON.stringify(payload)
})
.then(FetchUtil.checkStatus)
.then(FetchUtil.parseJSON)
.then(response => {
if (!response.hasOwnProperty('success') || !response.success) {
sweetAlert("Uh oh...", "We're having trouble submitting this " +
"form right now. Please try again later.", "error");
throw new Exception(FetchException.REQUEST_FAILED, response);
}
})
.catch(error => {
sweetAlert("Uh oh...", "We're having trouble submitting this " +
"form right now. Please try again later.", "error");
throw new Exception(FetchException.REQUEST_FAILED, error);
});
}
}
26 changes: 26 additions & 0 deletions frontend/javascript/modules/becomeActive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import FetchUtil from "../utils/fetchUtil";

export default class becomeActive {
constructor(link) {
this.link = link;
this.endpoint = '/manage/make_user_active';
this.render();
}

render() {
this.link.addEventListener('click', e => this._delete(e));
}

_delete(e) {
e.preventDefault();

FetchUtil.postWithWarning(this.endpoint, {}, {
warningText: "Becoming an active member means that you will be charged" +
" dues, which are detailed in the CSH Constitution.",
successText: "You are now an active member."
}, () => {
document.getElementById('becomeActive').remove();
});
}
}

11 changes: 11 additions & 0 deletions frontend/stylesheets/pages/_management.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@
margin: 30px 0;
}

.btn-get-active {
float: right;
margin-top: -4px;
box-shadow: none;
padding: 3px 7px;
}

.accept-dues-until {
margin-top: 10px;
}

.upload-title {
padding-top: 20px;
height: 55px;
Expand Down
26 changes: 26 additions & 0 deletions migrations/versions/d1a06ab54211_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Add Accept Dues Until site setting

Revision ID: d1a06ab54211
Revises: 117567def844
Create Date: 2017-07-21 17:09:37.540766

"""

# revision identifiers, used by Alembic.
revision = 'd1a06ab54211'
down_revision = '117567def844'

from alembic import op
import sqlalchemy as sa
from datetime import datetime

def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('settings', sa.Column('accept_dues_until', sa.Date(), server_default=datetime.now().strftime("%Y-%m-%d"), nullable=True))
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('settings', 'accept_dues_until')
# ### end Alembic commands ###