Tällä sivulla kerron teknologiasta ja työkaluista, jota on tullut käytettyä projektin aikana.
Alle on listattu asiat joiden parissa tuli työskenneltyä, ja muutama pointti jokaisesta
Minun vastuualueeseen kuului quizien toimintalogiikka. Tein töitä pääosin toisen koodarin kanssa yhdessä. Quizeja tehdessä pääsi tutustumaan tietokantoihin, backendiin ja frontendiin kunnolla. Projektin alussa, minulla oli jonkinnäköinen ajatus siitä, miten koko paketti yhdessä toimii, mutta tässä vaiheessa kaikkien osien yhdessä toimiminen alkaa olla jo ymmärrettävää, ja siitä on saanut paljon selkeämmän kuvan
Quizeja varten tietokantoihin piti luoda neljä eri taulua; Quiz, question, userquiz ja wronganswers. Tämän lisäksi komponenttiin piti hakea tiedot käyttäjästä, tämän tasosta, ja mahdollistaa tietokantojen muokkaamisen quizin oikeiden vastausten määrästä riippuen. Alla olevissa kuvissa on demonstroituna tietokannan luomista, ja tietojen hakemista tietokannasta backendin kautta.
CREATE TABLE Quiz (
quiz INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(30) NOT NULL,
info VARCHAR(300) NOT NULL,
reward INT NOT NULL,
rewardxp INT NOT NULL
) ENGINE=INNODB;
CREATE TABLE Question (
question_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
question VARCHAR(200) NOT NULL,
correct VARCHAR(30) NOT NULL,
mediaurl VARCHAR(100),
quiz INT NOT NULL,
CONSTRAINT question_quiz_fk
FOREIGN KEY (quiz) REFERENCES Quiz(quiz)
ON UPDATE RESTRICT
ON DELETE CASCADE
) ENGINE=INNODB;
// Metodi, joka hakee tietyn quizin kysymykset kannasta
getQuestions: (req, res) => {
Question.findAll({
where: {
quiz: req.params.quiz
}
}).then((questions) => {
res.json(questions);
}).catch((err) => {
console.log(err);
});
},
// Metodi, joka hakee kysymystä vastaavat väärät vastaukset
getWrongAnswers: (req, res) => {
WrongAnswers.findAll({
where: {
question_id: req.params.question
}
}).then((answers) => {
res.json(answers);
}).catch((err) => {
console.log(err);
});
},
Backendin puolella piti tehdä mahdolliseksi quizien löytäminen frontendiin tietokannoista, sekä niiden muokkaaminen frontendin tapahtumien perusteella. Päätimme käyttää sequelizea tietokantojen käsittelyyn, ja tämä osoittautui mielestäni toimivaksi ratkaisuksi. Alla esimerkit Sequelizen mallista, ja metodista jolla lisätään käyttäjälle merkintä suoritetusta quizista.
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = require('../dbconnection');
// Sequelize-malli quizeille
const Quiz = sequelize.define('Quiz', {
quiz: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
title: {
type: DataTypes.STRING,
allowNull: false
},
info: {
type: DataTypes.STRING,
allowNull: false
},
reward: {
type: DataTypes.INTEGER,
allowNull: false
},
rewardxp: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
freezeTableName: true
});
module.exports = Quiz;
updateQuizCompleted: (req, res) => {
UserQuiz.increment('completed', {
where: {
userid: req.body.userid,
quiz: req.body.quiz
}
}).then(() => {
res.json('Completed updated by +1');
}).catch((err) => {
console.log(err);
});
},
Frontendiin käyttäjälle piti saada näkyviin quizin kysymys, vastausvaihtoehdot ja vastauksen tulos, ja päästää käyttäjä seuraavaan kysymykseen. Tämän lisäksi komponentin piti lisäillä backendiin käyttäjän progressio, ja näyttää se myös käyttäjälle tulosruudussa. Alla kuvissa näytettynä nextQuestion() - metodi, joka päästää käyttäjän seuraavaan kysymykseen, kunnes quiz on ohi, näyttää tulosruudun ja kirjaa tapahtumat tietokantaan. Toisessa kuvassa on quizien html-tiedostosta toiminnallinen pätkä.
nextQuestion(): void {
this.questionCounter++;
if (this.questionCounter >= this.questions.length) {
this.results = true;
this.updateTries();
let currency = this.user.currency + this.quizdata.reward;
this.userService.updateCurrency(this.user, currency).subscribe();
this.updateUserExperience(this.user, this.quizExperienceReward);
if (this.user.experience + this.quizExperienceReward >= 50) {
this.levelup = true;
}
console.log(this.quizExperienceReward)
if(this.correctCounter === this.questions.length) {
this.updateCompleted();
}
if (this.results) {
document.getElementById("quiz").style.display = "none";
}
console.log(this.results);
} else {
this.getWrongAnswers();
this.randomizeAnswers();
this.buttoncolor = false;
this.greenbuttoncolor = false;
this.buttonsDisabled = false;
this.randomizeButtons();
console.log(this.correctCounter);
}
console.log(this.questionCounter);
}
Tein paljon töitä myös rekisteröitymisen kanssa. Rekisteröityminen tapahtuu omassa komponentissaan, joka pyytää käyttäjältä tiedot, lähettää ne backendiin, hashaa salasanan, ja sen jälkeen kirjoittaa kaiken ylös tietokantaan turvallisesti. Vasemmalla alapuolella olevassa kuvassa on createUser() - metodi, joka luo käyttäjän, ja lähettää tiedot backendiin, jos käyttäjä laittaa hyväksyttävät tiedot oikeassa muodossa. Virheen tapahtuessa käyttäjälle ilmoitetaan jos sähköposti on jo käytössä, tai sitten jo lomaketta täyttäessä, jos jokin kohta ei vastaa annettuja kriteerejä. Lopullisessa komponentissa myös lisätään käyttäjälle kirja ja kitara metodissa, mutta itse en sitä ollut tekemässä, joten pilkoin sen pois koodista. Oikean puolen koodissa on kuva formin username- kohdasta, joka näyttää toimintalogiikkaa ilmoituksista käyttäjälle jo formia täyttäessä.
createUser(): void {
this.userService
.createUser(this.email, this.username, this.password)
.subscribe(
data => {
console.log(data);
this.registrationSuccessful = true;
// Otetaan talteen tulevan käyttäjän userid
const user = this.userid + 1;
console.log(this.userid);
// luodaan useritem-tauluun tavaratietueet
for (let i = 0; i < this.items.length; i++) {
this.createUserItem(user,this.items[i].item);
console.log(this.items[i].itemname);
}
// Pienen viiveen jälkeen siirrytään login-ruutuun
setTimeout(() => this.router.navigate(['/login']), 3000);
}, (err) => {
console.log(err);
if(err.errors.email) {
this.errorMessage = "Email address is already in use";
this.error = true;
}
}
);
}
Rekisteröinnin yhteydessä halusimme lähettää käyttäjälle sähköpostin, joka kertoo käyttäjälle rekisteröitymisen onnistuneen. Todennäköisesti myös product labissa otamme käyttöön salasanan turvallisen palauttamisen nodemailerin avulla, mikäli projekti sinne viedään. Tällä aikataululla salasanan palautus ei ehtinyt projektiin mukaan, sillä toiminnallisia ongelmia oli sen verran, että halusimme saada ne kuntoon ensiksi. Alla olevassa kuvassa on koodia backendista, jossa rekisteröitymisen yhteydessä määritetään ja lähetetään käyttäjälle sähköpostia, luodaan käyttäjä, ja annetaan käyttäjälle aloitusarvot.
createUser: (req, res) => {
const hashedPassword = bcrypt.hashSync(req.body.password, 10);
const emaili = req.body.email;
const username1 = req.body.username;
let transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.EMAIL,
pass: process.env.PASSWORD
}
});
let mailOptions = {
from: 'acornporate@gmail.com',
to: emaili,
subject: 'Welcome to Acorn-Family!',
text: 'You have succssessfully created an account! Your username is ' + username1 + ' Thanks. HAVE FUN. Don´t do drugs.'
}
transporter.sendMail(mailOptions, function(err, data) {
if (err) {
console.log('errori');
} else {
console.log('email sent')
}
});
User.create({
email: req.body.email,
username: req.body.username,
psw: hashedPassword,
experience: 0,
lvl: 1,
currency: 0,
streak: 0,
quizcounter: 1,
practisecounter: 1,
})
.then((user) => {
res.json(user);
})
.catch((err) => {
console.log(err);
return res.status(500).send('Email is already in use');
});
};