Skip to content

Commit dcd7890

Browse files
authored
Merge pull request #66 from ComputerScienceHouse/mp-delete
Allow Deletion of Major Projects
2 parents 665cfd5 + a38eced commit dcd7890

File tree

10 files changed

+150
-79
lines changed

10 files changed

+150
-79
lines changed

conditional/blueprints/dashboard.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ def display_dashboard():
9696
user_name = request.headers.get('x-webauth-user')
9797

9898
can_vote = get_voting_members()
99-
logger.info('backend', action=can_vote)
10099
data = dict()
101100
data['username'] = user_name
102101
data['name'] = ldap_get_name(user_name)
@@ -142,6 +141,7 @@ def display_dashboard():
142141

143142
data['major_projects'] = [
144143
{
144+
'id': p.id,
145145
'name': p.name,
146146
'status': p.status,
147147
'description': p.description

conditional/blueprints/major_project_submission.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ def display_major_project():
3434
'proj_name': p.name,
3535
'status': p.status,
3636
'description': p.description,
37-
'id': p.id
37+
'id': p.id,
38+
'is_owner': bool(user_name == p.uid)
3839
} for p in
3940
MajorProject.query]
4041

@@ -95,3 +96,27 @@ def major_project_review():
9596
db.session.flush()
9697
db.session.commit()
9798
return jsonify({"success": True}), 200
99+
100+
101+
@major_project_bp.route('/major_project/delete/<pid>', methods=['DELETE'])
102+
def major_project_delete(pid):
103+
log = logger.new(user_name=request.headers.get("x-webauth-user"),
104+
request_id=str(uuid.uuid4()))
105+
log.info('api', action='review major project')
106+
107+
# get user data
108+
user_name = request.headers.get('x-webauth-user')
109+
major_project = MajorProject.query.filter(
110+
MajorProject.id == pid
111+
).first()
112+
creator = major_project.uid
113+
114+
if creator == user_name or ldap_is_eval_director(user_name):
115+
MajorProject.query.filter(
116+
MajorProject.id == pid
117+
).delete()
118+
db.session.flush()
119+
db.session.commit()
120+
return jsonify({"success": True}), 200
121+
else:
122+
return "Must be project owner to delete!", 401

conditional/templates/dashboard.html

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -201,23 +201,24 @@ <h3 class="panel-title">Conditionals</h3>
201201
<div class="panel-heading">
202202
<h3 class="panel-title">Major Projects</h3>
203203
</div>
204-
<table>
205-
{% for p in major_projects %}
206-
<tr>
204+
<div class="panel-body">
205+
{% for p in major_projects %}
206+
<div class="mp-container">
207207
{% if p['status'] == "Passed" %}
208-
<span class="title"><span style="padding-left:15px; padding-top:15px;" class="glyphicon glyphicon-ok-sign green"></span> {{p['name']}}
209-
</span>
208+
<div class="title"><span class="glyphicon glyphicon-ok-sign green"></span> {{p['name']}}</div>
210209
{% elif p['status'] == "Pending" %}
211-
<span class="title"><span style="padding-left:15px; padding-top:15px;" class="glyphicon glyphicon-hourglass yellow"></span> {{p['name']}}
212-
</span>
210+
<div class="title">
211+
<span class="glyphicon glyphicon-hourglass yellow"></span> {{p['name']}}
212+
<button class="btn-xs btn-danger pull-right" data-module="majorProjectStatus" data-id="{{p['id']}}"><span class="glyphicon glyphicon-trash"></span> Delete</button>
213+
</div>
213214
{% else %}
214-
<span class="title"><span style="padding-left:15px; padding-top:15px;" class="glyphicon glyphicon-minus-sign red"></span> {{p['name']}}
215-
</span>
215+
<div class="title"><span class="glyphicon glyphicon-minus-sign red"></span> {{p['name']}}</div>
216216
{% endif %}
217-
<div class="panel-body" style="word-wrap:break-word">{{p['description']}}</div>
218-
</tr>
219-
{% endfor %}
220-
</table>
217+
<div class="mp-description">{{p['description']}}</div>
218+
</div>
219+
</tr>
220+
{% endfor %}
221+
</div>
221222
</div>
222223
{% endif %}
223224

conditional/templates/major_project_submission.html

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ <h4>{{p['proj_name']}}</h4>
4141
<img class="table-img" src="https://profiles.csh.rit.edu/image/{{p['username']}}">
4242
{{p['name']}} ({{p['username']}})
4343
</div>
44-
{% if is_eval_director %}
4544
<div class="col-xs-4 col-sm-2">
45+
46+
{% if is_eval_director %}
47+
4648
<div class="btn-group" data-module="majorProjectStatus" data-id="{{p['id']}}">
4749
<a href="#" class="btn {% if p['status'] == 'Passed' %}btn-success{% elif p['status'] == 'Failed' %}btn-danger{% else %}btn-warning{% endif %} dropdown-toggle btn-mp" data-toggle="dropdown" aria-expanded="false">
4850
{{p['status']}}
@@ -58,20 +60,24 @@ <h4>{{p['proj_name']}}</h4>
5860
<li>
5961
<a href="#" data-option="Failed"><span class="glyphicon glyphicon-remove-sign red"></span> Failed</a>
6062
</li>
63+
<li>
64+
<a href="#" data-option="Delete"><span class="glyphicon glyphicon-trash red"></span> Delete</a>
65+
</li>
6166
</ul>
6267
</div>
63-
</div>
6468
{% else %}
65-
<div class="col-xs-4 col-sm-2">
6669
{% if p['status'] == 'Passed' %}
6770
<h5 style="padding:15px 20px;float:right"><span class="glyphicon glyphicon-ok green"></span></h5>
6871
{% elif p['status'] == 'Failed' %}
6972
<h5 style="padding:15px 20px;float:right"><span class="glyphicon glyphicon-remove red"></span></h5>
7073
{% else %}
7174
<h5 style="padding:15px 20px;float:right"><span class="glyphicon glyphicon-hourglass yellow"></span></h5>
7275
{% endif %}
73-
</div>
76+
{% if p.is_owner and p['status'] == 'Pending' %}
77+
<button class="btn btn-danger btn-mp" data-module="majorProjectStatus" data-id="{{p['id']}}"><span class="glyphicon glyphicon-trash"></span> Delete</button>
78+
{% endif %}
7479
{% endif %}
80+
</div>
7581
<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>
7682
<div class="collapse" id="evalsCollapse-{{p['id']}}">
7783
{{p['description']}}

frontend/javascript/modules/attendanceForm.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export default class AttendanceForm {
5353
});
5454

5555
FetchUtil.postWithWarning(this.endpoint, payload, {
56-
warningText: "You will not be able to edit this event once" +
56+
warningText: "You will not be able to edit this event once " +
5757
"attendance has been recorded.",
5858
successText: "Attendance has been submitted."
5959
});

frontend/javascript/modules/editUser.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,8 @@ export default class EditUser {
352352

353353
FetchUtil.fetchWithWarning(this.endpoints.userDetails + this.uid, {
354354
method: 'DELETE',
355-
warningText: 'This account\'s data will be permanently deleted.',
356-
successText: 'Freshman account has been deleted.'
355+
warningText: "This account's data will be permanently deleted.",
356+
successText: "Freshman account has been deleted."
357357
}, () => {
358358
$(modal).modal('hide');
359359
window.location.reload();
@@ -371,9 +371,9 @@ export default class EditUser {
371371
};
372372

373373
FetchUtil.postWithWarning(this.endpoints.userUpgrade, payload, {
374-
warningText: 'This will irreversibly migrate all of this ' +
375-
'freshman\'s data to the specified member account.',
376-
successText: 'Account has been upgraded.'
374+
warningText: "This will irreversibly migrate all of this " +
375+
"freshman's data to the specified member account.",
376+
successText: "Account has been upgraded."
377377
}, () => {
378378
$(modal).modal('hide');
379379
window.location.reload();

frontend/javascript/modules/hmForm.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export default class HouseMeetingForm {
5858
payload.members = upperclassmen;
5959

6060
FetchUtil.postWithWarning(this.endpoint, payload, {
61-
warningText: "You will not be able to unmark a member as present" +
61+
warningText: "You will not be able to unmark a member as present " +
6262
"once attendance has been recorded.",
6363
successText: "Attendance has been submitted."
6464
});

frontend/javascript/modules/majorProjectStatus.js

Lines changed: 80 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -6,68 +6,95 @@ import FetchUtil from "../utils/fetchUtil";
66
import sweetAlert from "../../../node_modules/bootstrap-sweetalert/dev/sweetalert.es6.js"; // eslint-disable-line max-len
77

88
export default class MajorProjectStatus {
9-
constructor(dropdown) {
10-
this.dropdown = dropdown;
11-
this.id = this.dropdown.dataset.id;
9+
constructor(control) {
10+
this.control = control;
11+
this.id = this.control.dataset.id;
1212
this.endpoint = '/major_project/review';
13+
this.deleteEndpoint = '/major_project/delete/';
1314
this.render();
1415
}
1516

1617
render() {
17-
const options = this.dropdown.querySelectorAll('[data-option]');
18-
options.forEach(option => {
19-
option.addEventListener('click', e => this._changeStatus(e));
20-
});
18+
if (this.control.tagName.toLowerCase() === "div") {
19+
// Evals director dropdown
20+
const options = this.control.querySelectorAll('[data-option]');
21+
options.forEach(option => {
22+
option.addEventListener('click', e => {
23+
e.preventDefault();
24+
this._changeStatus(e.target.dataset.option);
25+
});
26+
});
27+
} else {
28+
// Member self-delete button
29+
this.control.addEventListener('click',
30+
() => this._changeStatus('Delete'));
31+
}
2132
}
2233

23-
_changeStatus(e) {
24-
e.preventDefault();
25-
26-
let option = e.target.dataset.option;
27-
let payload = {
28-
id: this.id,
29-
status: option
30-
};
31-
32-
fetch(this.endpoint, {
33-
method: 'POST',
34-
headers: {
35-
'Accept': 'application/json',
36-
'Content-Type': 'application/json'
37-
},
38-
credentials: "same-origin",
39-
body: JSON.stringify(payload)
40-
})
41-
.then(FetchUtil.checkStatus)
42-
.then(FetchUtil.parseJSON)
43-
.then(response => {
44-
if (response.hasOwnProperty('success') && response.success === true) {
45-
let toggle = this.dropdown.querySelector('.dropdown-toggle');
46-
["btn-success", "btn-danger", "btn-warning"].forEach(classToRemove =>
47-
toggle.classList.remove(classToRemove));
48-
49-
const caret = document.createElement('span');
50-
caret.classList.add('caret');
51-
toggle.text = option + " ";
52-
toggle.appendChild(caret);
53-
54-
if (option === "Passed") {
55-
toggle.classList.add("btn-success");
56-
} else if (option === "Failed") {
57-
toggle.classList.add("btn-danger");
58-
} else {
59-
toggle.classList.add("btn-warning");
60-
}
34+
_changeStatus(option) {
35+
if (option === "Delete") {
36+
FetchUtil.fetchWithWarning(this.deleteEndpoint + this.id, {
37+
method: 'DELETE',
38+
warningText: 'This action cannot be undone.',
39+
successText: 'Major project deleted.'
40+
}, () => {
41+
let dashboardContainer = this.control.closest(".mp-container");
42+
if (dashboardContainer) {
43+
// Dashboard button
44+
$(dashboardContainer).hide();
6145
} else {
62-
sweetAlert("Uh oh...", "We're having trouble updating this project " +
63-
"right now. Please try again later.", "error");
64-
throw new Exception(FetchException.REQUEST_FAILED, response);
46+
// Major projects page button
47+
$(this.control.closest(".panel")).fadeOut();
6548
}
66-
})
67-
.catch(error => {
68-
sweetAlert("Uh oh...", "We're having trouble updating this project " +
69-
"right now. Please try again later.", "error");
70-
throw new Exception(FetchException.REQUEST_FAILED, error);
7149
});
50+
} else {
51+
let payload = {
52+
id: this.id,
53+
status: option
54+
};
55+
56+
fetch(this.endpoint, {
57+
method: 'POST',
58+
headers: {
59+
'Accept': 'application/json',
60+
'Content-Type': 'application/json'
61+
},
62+
credentials: "same-origin",
63+
body: JSON.stringify(payload)
64+
})
65+
.then(FetchUtil.checkStatus)
66+
.then(FetchUtil.parseJSON)
67+
.then(response => {
68+
if (response.hasOwnProperty('success') &&
69+
response.success === true) {
70+
let toggle = this.control.querySelector('.dropdown-toggle');
71+
["btn-success", "btn-danger", "btn-warning"]
72+
.forEach(classToRemove =>
73+
toggle.classList.remove(classToRemove));
74+
75+
const caret = document.createElement('span');
76+
caret.classList.add('caret');
77+
toggle.text = option + " ";
78+
toggle.appendChild(caret);
79+
80+
if (option === "Passed") {
81+
toggle.classList.add("btn-success");
82+
} else if (option === "Failed") {
83+
toggle.classList.add("btn-danger");
84+
} else {
85+
toggle.classList.add("btn-warning");
86+
}
87+
} else {
88+
sweetAlert("Uh oh...", "We're having trouble updating " +
89+
"this project right now. Please try again later.", "error");
90+
throw new Exception(FetchException.REQUEST_FAILED, response);
91+
}
92+
})
93+
.catch(error => {
94+
sweetAlert("Uh oh...", "We're having trouble updating " +
95+
"this project right now. Please try again later.", "error");
96+
throw new Exception(FetchException.REQUEST_FAILED, error);
97+
});
98+
}
7299
}
73100
}

frontend/stylesheets/pages/_dashboard.scss

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,13 @@
3131
.profile-container {
3232
padding: 0 0 0 15px;
3333
}
34+
35+
.mp-description {
36+
margin: 5px 0 15px;
37+
38+
word-wrap: break-word;
39+
40+
&:last-of-type {
41+
margin: 5px 0;
42+
}
43+
}

frontend/stylesheets/pages/_management.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,9 @@
124124
}
125125

126126
.btn-mp {
127+
float: right;
127128
margin-top: 20px;
129+
margin-right: 15px;
128130
}
129131

130132
.stat-number {

0 commit comments

Comments
 (0)