Skip to content
This repository was archived by the owner on Dec 8, 2022. It is now read-only.

Commit 422a7df

Browse files
authored
Merge branch 'stage' into master
2 parents b857d02 + a4c43ff commit 422a7df

File tree

14 files changed

+345
-60
lines changed

14 files changed

+345
-60
lines changed

CodeChallenge/api/vote.py

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from flask_jwt_extended import get_current_user, jwt_optional
33
from flask_mail import Message
44
from itsdangerous import URLSafeSerializer
5+
from sqlalchemy import or_
56

67
from .. import core
78
from ..auth import Users
@@ -49,25 +50,14 @@ def get_contestants():
4950
contestants = []
5051
for ans in p.items: # type: Answer
5152

52-
display = None
53-
if ans.user.studentfirstname \
54-
and ans.user.studentlastname:
55-
display = f"{ans.user.studentfirstname} " \
56-
f"{ans.user.studentlastname[0]}."
57-
58-
confirmed_votes = []
59-
for v in ans.votes:
60-
if v.confirmed:
61-
confirmed_votes.append(v)
62-
6353
contestants.append(dict(
6454
id=ans.id,
6555
text=ans.text,
66-
numVotes=len(confirmed_votes),
56+
numVotes=ans.confirmed_votes(),
6757
firstName=ans.user.studentfirstname,
6858
lastName=ans.user.studentlastname,
6959
username=ans.user.username,
70-
display=display
60+
display=ans.user.display()
7161
))
7262

7363
return jsonify(
@@ -188,3 +178,51 @@ def vote_confirm():
188178

189179
return jsonify(status="success",
190180
reason="vote confirmed")
181+
182+
183+
@bp.route("/search", methods=["GET"])
184+
def search():
185+
keyword = request.args.get("q")
186+
try:
187+
page = int(request.args.get("page", 1))
188+
per = int(request.args.get("per", 20))
189+
except ValueError:
190+
return jsonify(status="error",
191+
reason="invalid 'page' or 'per' parameter"), 400
192+
193+
if keyword is None:
194+
return jsonify(status="error", reason="missing 'q' parameter"), 400
195+
196+
keyword = f"%{keyword}%"
197+
198+
p = Answer.query \
199+
.join(Answer.question) \
200+
.join(Answer.user) \
201+
.filter(Question.rank == core.max_rank(),
202+
Answer.correct, or_(Users.username.ilike(keyword), Users.studentlastname.ilike(keyword),
203+
Users.studentlastname.ilike(keyword))) \
204+
.paginate(page=page, per_page=per)
205+
206+
results = []
207+
208+
for ans in p.items: # type: Answer
209+
results.append(dict(
210+
id=ans.id,
211+
text=ans.text,
212+
numVotes=ans.confirmed_votes(),
213+
firstName=ans.user.studentfirstname,
214+
lastName=ans.user.studentlastname,
215+
username=ans.user.username,
216+
display=ans.user.display()
217+
))
218+
219+
return jsonify(
220+
items=results,
221+
totalItems=p.total,
222+
page=p.page,
223+
totalPages=p.pages,
224+
hasNext=p.has_next,
225+
nextNum=p.next_num,
226+
hasPrev=p.has_prev,
227+
prevNum=p.prev_num
228+
)

CodeChallenge/auth.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ def votes(self):
5353
.all()
5454
return v
5555

56+
def display(self):
57+
if self.studentfirstname is not None \
58+
and self.studentlastname is not None \
59+
and len(self.studentlastname):
60+
return f"{self.studentfirstname} " \
61+
f"{self.studentlastname[0]}."
62+
5663

5764
def hash_password(plaintext):
5865
ph = argon2.PasswordHasher()

CodeChallenge/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ class Answer(db.Model):
4040
votes = db.relationship("Vote", cascade="all,delete",
4141
lazy=True, uselist=True)
4242

43+
def confirmed_votes(self) -> int:
44+
confirmed = 0
45+
for vote in self.votes:
46+
if vote.confirmed:
47+
confirmed += 1
48+
49+
return confirmed
50+
4351

4452
class Vote(db.Model):
4553
id = db.Column(db.Integer, primary_key=True)

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141
"vue-cli-plugin-codemirror": "^0.0.6",
4242
"vue-cli-plugin-vuetify": "^2.0.3",
4343
"vue-template-compiler": "^2.6.10",
44-
"vuetify-loader": "^1.3.0"
44+
"vuetify-loader": "^1.3.0",
45+
"minimist": "^1.2.2",
46+
"acorn": "^6.4.1"
4547
},
4648
"eslintConfig": {
4749
"root": true,

src/api/routes.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ export default {
2929
voting_cast: id => {
3030
return route(`/api/v1/vote/${id}/cast`, "POST");
3131
},
32-
voting_confirm: route("/api/v1/vote/confirm", "POST")
32+
voting_confirm: route("/api/v1/vote/confirm", "POST"),
33+
voting_ballot_search: route("/api/v1/vote/search", "GET")
3334
};
3435

3536
// export default {

src/api/voting.js

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
import routes from "./routes";
22
import request from "./request";
33

4+
function processBallotResponse(result) {
5+
if (result.items) {
6+
result.items = result.items.map(item => {
7+
return { ...item, ...{ initials: initials(item) } };
8+
});
9+
}
10+
return result;
11+
}
12+
413
function lastInitial(item) {
514
if (item.lastName) {
615
return item.lastName[0];
@@ -26,16 +35,11 @@ function initials(item) {
2635
}
2736

2837
async function getBallot(page, per) {
29-
const result = await request(routes.voting_ballot, {
30-
params: { page, per }
31-
});
32-
33-
if (result.items) {
34-
result.items = result.items.map(item => {
35-
return { ...item, ...{ initials: initials(item) } };
36-
});
37-
}
38-
return result;
38+
return processBallotResponse(
39+
await request(routes.voting_ballot, {
40+
params: { page, per }
41+
})
42+
);
3943
}
4044

4145
async function cast(answerId, email) {
@@ -46,8 +50,17 @@ async function confirm(token) {
4650
return request(routes.voting_confirm, { data: { token } });
4751
}
4852

53+
async function search(text, page, per) {
54+
return processBallotResponse(
55+
await request(routes.voting_ballot_search, {
56+
params: { q: text, page, per }
57+
})
58+
);
59+
}
60+
4961
export default {
5062
getBallot,
5163
cast,
52-
confirm
64+
confirm,
65+
search
5366
};

src/styles/ballot-card.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ $ballot-gray: #ddd;
2222
.ballot-card {
2323
margin: 25px;
2424
width: 200px;
25-
height: 300px;
25+
height: 320px;
2626
background-color: #fff !important;
2727

2828
border: solid 2px $ballot-gray !important;

0 commit comments

Comments
 (0)