Merge branch 'c4' into dev

This commit is contained in:
Ceferino Patino 2022-08-18 14:54:01 -04:00
commit 55514b408b
17 changed files with 224 additions and 40 deletions

64
server/badges.json Normal file
View file

@ -0,0 +1,64 @@
{
"badges": [
{
"title": "Great Question",
"text": "Have a question with net total points of >100",
"rank": "Gold"
},
{
"title": "Good Question",
"text": "Have a question with net total points of >25",
"rank": "Silver"
},
{
"title": "Nice Question",
"text": "Have a question with net total points of >10",
"rank": "Bronze"
},
{
"title": "Great Answer",
"text": "Have a answer with net total points of >100",
"rank": "Gold"
},
{
"title": "Good Answer",
"text": "Have a answer with net total points of >25",
"rank": "Silver"
},
{
"title": "Nice Answer",
"text": "Have a answer with net total points of >10",
"rank": "Bronze"
},
{
"title": "Socratic",
"text": "Have at least 10000 points",
"rank": "Gold"
},
{
"title": "Inquisitive",
"text": "Have at least 3000 points",
"rank": "Silver"
},
{
"title": "Curious",
"text": "Have at least 100 points",
"rank": "Bronze"
},
{
"title": "Zombie",
"text": "Have a question that is reopened",
"rank": "Gold"
},
{
"title": "Protected",
"text": "Have a question that is protected",
"rank": "Silver"
},
{
"title": "Scholar",
"text": "Accept an answer",
"rank": "Bronze"
}
]
}

View file

@ -1,4 +1,6 @@
const mongoose = require('mongoose');
const calculateAnswerBadges = require('../../utils/badges/answerBadges');
const User = require('./User');
const Answer = mongoose.Schema(
{
@ -15,4 +17,10 @@ const Answer = mongoose.Schema(
{ timestamps: { createdAt: false, updatedAt: true } }
);
Answer.post('findOneAndUpdate', (doc) => {
const badges = calculateAnswerBadges(doc.points);
User.findOneAndUpdate({ username: doc.creator }, { $addToSet: { tags: { $each: badges } } });
});
module.exports = mongoose.model('Answer', Answer);

View file

@ -1,18 +1,9 @@
const mongoose = require('mongoose');
const Answer = mongoose.Schema(
{
question_id: { type: mongoose.Schema.Types.ObjectId, ref: 'Question' },
answer_id: { type: String, required: true },
creator: { type: String, required: true },
text: { type: String, required: true },
upvotes: { type: Number, required: true, default: 0 },
downvotes: { type: Number, required: true, default: 0 },
accepted: { type: Boolean, required: true, default: false },
createdAt: { type: Date, required: true },
lastCommentFetch: { type: Date, default: new Date(0) },
},
{ timestamps: { createdAt: false, updatedAt: true } }
);
const Badge = mongoose.Schema({
title: { type: String, required: true },
text: { type: String, required: true },
rank: { type: String, required: true, enum: ['Gold', 'Silver', 'Bronze'] },
});
module.exports = mongoose.model('Answer', Answer);
module.exports = mongoose.model('Badge', Badge);

View file

@ -1,4 +1,6 @@
const mongoose = require('mongoose');
const calculateQuestionBadges = require('../../utils/badges/questionBadges');
const User = require('./User');
const Question = mongoose.Schema(
{
@ -27,4 +29,10 @@ const Question = mongoose.Schema(
{ timestamps: { createdAt: false, updatedAt: true } }
);
Question.post('findOneAndUpdate', (doc) => {
const badges = calculateQuestionBadges(doc.points);
User.findOneAndUpdate({ username: doc.creator }, { $addToSet: { tags: { $each: badges } } });
});
module.exports = mongoose.model('Question', Question);

View file

@ -1,4 +1,5 @@
const mongoose = require('mongoose');
const calculateUserBadges = require('server/utils/badges/userBadges');
const User = mongoose.Schema(
{
@ -9,8 +10,21 @@ const User = mongoose.Schema(
lastMailFetch: { type: Date, default: new Date(0) },
lastAnswerFetch: { type: Date, default: new Date(0) },
user_id: { type: String, required: true, unique: true },
badges: [String],
},
{ timestamps: { createdAt: false, updatedAt: true } }
);
User.post('findOneAndUpdate', async (doc) => {
const badges = calculateUserBadges(doc.points);
console.log(`first: ${doc.badges}`);
doc.badges = [...new Set([...doc.badges, ...badges])];
console.log(doc.badges);
doc.save();
});
module.exports = mongoose.model('User', User);

View file

@ -9,6 +9,6 @@ const ReadMail = require('./mail/Read');
router.get('/', tokenAuth, GetMail);
router.post('/', tokenAuth, SendMail);
router.patch('/', tokenAuth, ReadMail);
router.patch('/:mail_id', tokenAuth, ReadMail);
module.exports = router;

View file

@ -2,9 +2,9 @@ const Mail = require('server/db/models/Mail');
const config = require('server/config.json');
async function Read(req, res) {
const { id } = req.body;
const { mail_id } = req.params;
const cachedMail = await Mail.findByIdAndUpdate(id, { read: true });
const cachedMail = await Mail.findByIdAndUpdate(mail_id, { read: true });
return cachedMail ? res.sendStatus(200) : res.status(404).send(config.errorNotFound);
}

View file

@ -5,10 +5,14 @@ const createRequest = require('server/utils/api');
async function CreateQuestion(req, res) {
const user = req.user;
const { title, text } = req.body;
const { title, text, tags } = req.body;
if (!title || !text) return res.status(400).send(config.errorIncomplete);
if (tags && tags.length > 5) {
return res.status(400).send(config.errorIncomplete);
}
const { success, question } = await createRequest('post', `/questions`, {
creator: user.username,
title,
@ -17,7 +21,7 @@ async function CreateQuestion(req, res) {
if (!success) return res.status(500).send(config.errorGeneric);
const newQuestion = await Question.create({ ...question, _id: question.question_id });
const newQuestion = await Question.create({ ...question, tags, _id: question.question_id });
// Increment user points by 1
await createRequest('patch', `/users/${user.username}/points`, {

View file

@ -15,8 +15,9 @@ async function EditAnswerAccepted(req, res) {
if (!question) return res.status(404).send(config.errorNotFound);
const {hasBounty} = question;
// Verify user owns question and question does not already have an accepted answer
if (question.creator !== user.username || question.hasAccepted)
if (question.creator !== user.username || question.hasAccepted) {
return res.status(403).send(config.errorForbidden);
}
// Patch question with BDPA server
const patchAnswer = await createRequest(

View file

@ -18,10 +18,7 @@ async function EditQuestionStatusProtected(req, res) {
if (!question) return res.status(404).send(config.errorNotFound);
// Verify question does not have incompatible status
if (
question.status === 'closed' ||
question.status === 'protected'
) {
if (question.status === 'closed' || question.status === 'protected') {
return res.status(400).send({
success: false,
error: 'This question is already closed or protected.',
@ -48,11 +45,11 @@ async function EditQuestionStatusProtected(req, res) {
status: 'protected',
});
const { success } = await createRequest(
'patch',
`/questions/${question_id}`,
{ status: 'protected' }
);
await User.findOneAndUpdate({ username: creator }, { $push: { badges: 'Protected' } });
const { success } = await createRequest('patch', `/questions/${question_id}`, {
status: 'protected',
});
await Question.findByIdAndUpdate(question_id, { status: 'protected' });
return success

View file

@ -1,6 +1,6 @@
const config = require('server/config.json');
const Question = require('server/db/models/Question');
const { getQuestion, refreshQuestion } = require('server/utils/question');
const { getQuestion } = require('server/utils/question');
const getUserLevel = require('server/utils/getUserLevel');
const createRequest = require('server/utils/api');
@ -18,10 +18,7 @@ async function EditQuestionStatusReopened(req, res) {
if (!question) return res.status(404).send(config.errorNotFound);
// Make sure that question has compatible status
if (
question.status === 'protected' ||
question.status === 'open'
) {
if (question.status === 'protected' || question.status === 'open') {
return res.status(400).send({
success: false,
error: 'This question is already open.',
@ -48,11 +45,11 @@ async function EditQuestionStatusReopened(req, res) {
status: 'open',
});
const { success } = await createRequest(
'patch',
`/questions/${question_id}`,
{ status: 'open' }
);
await User.findOneAndUpdate({ username: creator }, { $push: { badges: 'Zombie' } });
const { success } = await createRequest('patch', `/questions/${question_id}`, {
status: 'open',
});
await Question.findByIdAndUpdate(question_id, { status: 'open' });
return success

View file

@ -5,6 +5,7 @@ const basicAuth = require('server/middleware/basicAuth');
const tokenAuth = require('server/middleware/tokenAuth');
const Answers = require('./user/Answers');
const GetBadges = require('./user/GetBadges');
const Delete = require('./user/Delete');
const Edit = require('./user/Edit');
const GetUser = require('./user/GetUser');
@ -20,6 +21,7 @@ router.post('/', Register);
router.patch('/', tokenAuth, Edit);
router.get('/questions', tokenAuth, Questions);
router.get('/answers', tokenAuth, Answers);
router.get('/badge', tokenAuth, GetBadges);
router.post('/login', basicAuth, Login);
router.get('/remember', tokenAuth, Remember);
router.delete('/logout', tokenAuth, Logout);

View file

@ -0,0 +1,24 @@
const Badge = require('server/db/models/Badge');
async function GetBadges(req, res) {
const { user } = req;
const badges = await Badge.find();
const response = badges.reduce(
(acc, badge) => {
if (user.badges.includes(badge.title)) {
acc.obtained.append(badge);
} else {
acc.unobtained.append(badge);
}
return acc;
},
{ obtained: [], unobtained: [] }
);
return res.send(response);
}
module.exports = GetBadges;

View file

@ -2,6 +2,7 @@ const createRequest = require('server/utils/api');
const config = require('server/config.json');
const User = require('server/db/models/User');
const getUserLevel = require('server/utils/getUserLevel');
const Badge = require('server/db/models/Badge');
async function GetUser(req, res) {
const { username } = req.params;
@ -27,6 +28,27 @@ async function GetUser(req, res) {
new: true,
});
const badges = await Badge.find({ title: { $in: user.badges } });
const badgeCount = badges.reduce(
(acc, badge) => {
if (badge.rank === 'Gold') {
acc.gold = acc.gold++;
} else if (badge.rank === 'Silver') {
acc.silver = acc.silver++;
} else if (badge.rank === 'Bronze') {
acc.bronze = acc.bronze++;
}
return acc;
},
{
gold: 0,
silver: 0,
bronze: 0,
}
);
return res.send({
user: {
username: newUser.username,
@ -34,6 +56,7 @@ async function GetUser(req, res) {
points: newUser.points,
level: getUserLevel(newUser.points),
},
badgeCount,
});
}

View file

@ -0,0 +1,17 @@
function calculateAnswerBadges(points) {
let badges = [];
if (points >= 100) {
badges.push('Great Answer');
}
if (points >= 25) {
badges.push('Good Answer');
}
if (points >= 10) {
badges.push('Nice Answer');
}
return badges;
}
module.exports = calculateAnswerBadges;

View file

@ -0,0 +1,17 @@
function calculateQuestionBadges(points) {
let badges = [];
if (points >= 100) {
badges.push('Great Question');
}
if (points >= 25) {
badges.push('Good Question');
}
if (points >= 10) {
badges.push('Nice Question');
}
return badges;
}
module.exports = calculateQuestionBadges;

View file

@ -0,0 +1,17 @@
function calculateUserBadges(points) {
let badges = [];
if (points >= 10000) {
badges.push('Socratic');
}
if (points >= 3000) {
badges.push('Inquisitive');
}
if (points >= 100) {
badges.push('Curious');
}
return badges;
}
module.exports = calculateUserBadges;