Refactor spaghetti code I wrote + save source code in file system instead than in db

This commit is contained in:
Francois G 2023-02-25 19:40:16 +01:00
parent 64a88f7282
commit 4b5d21345c
6 changed files with 287 additions and 162 deletions

View file

@ -0,0 +1,62 @@
package be.jeffcheasey88.peeratcode.model;
public class Completion {
private int puzzleId;
private int playerId;
private int tries;
private String fileName;
private byte[] code;
private int score;
public Completion(int playerId, int puzzleId, String fileName, int score) {
this(playerId, puzzleId, -1, 1, fileName, score, null);
}
public Completion(int playerId, int puzzleId, int idCompletion, int tries, String fileName, int score) {
this(playerId, puzzleId, idCompletion, tries, fileName, score, null);
}
public Completion(int playerId, int puzzleId, int idCompletion, int tries, String fileName, int score,
byte[] file) {
this.playerId = playerId;
this.puzzleId = puzzleId;
this.tries = tries;
this.fileName = fileName;
this.score = score;
this.code = file;
}
public int getPuzzleId() {
return puzzleId;
}
public int getPlayerId() {
return playerId;
}
public int getTries() {
return tries;
}
public void addTry() {
this.tries++;
updateScore();
}
private void updateScore() {
if (tries > 1) {
score = score * (1-((tries-1)/10));
}
}
public String getFileName() {
return fileName;
}
public byte[] getCode() {
return code;
}
public int getScore() {
return score;
}
}

View file

@ -1,6 +1,7 @@
package be.jeffcheasey88.peeratcode.model;
public class Player {
public static final String PATH_TO_CODE = "/home/%s/peer-at-source/";
private String pseudo;
private String email;
@ -9,7 +10,7 @@ public class Player {
private String description;
private String sgroup;
public Player(String pseudo, String email, String firstname, String lastname, String description, String sgroup){
public Player(String pseudo, String email, String firstname, String lastname, String description, String sgroup) {
this.pseudo = pseudo;
this.email = email;
this.firstname = firstname;
@ -18,28 +19,31 @@ public class Player {
this.sgroup = sgroup;
}
public String getPseudo(){
public String getPseudo() {
return this.pseudo;
}
public String getEmail(){
public String getEmail() {
return this.email;
}
public String getFirstname(){
public String getFirstname() {
return this.firstname;
}
public String getLastname(){
public String getLastname() {
return this.lastname;
}
public String getDescription(){
public String getDescription() {
return this.description;
}
public String getGroup(){
public String getGroup() {
return this.sgroup;
}
public String getPathToSourceCode() {
return String.format(PATH_TO_CODE, pseudo);
}
}

View file

@ -58,7 +58,7 @@ public class Puzzle {
this.verify = regex;
}
private int getScoreMax(){
public int getScoreMax(){
return this.scoreMax;
}

View file

@ -14,6 +14,8 @@ import com.password4j.Password;
import be.jeffcheasey88.peeratcode.Configuration;
import be.jeffcheasey88.peeratcode.model.Chapter;
import be.jeffcheasey88.peeratcode.model.Completion;
import be.jeffcheasey88.peeratcode.model.Player;
import be.jeffcheasey88.peeratcode.model.Puzzle;
public class DatabaseRepository {
@ -26,34 +28,47 @@ public class DatabaseRepository {
private static final String REGISTER_QUERY = "INSERT INTO players (pseudo, email, passwd, firstname, lastname, description, sgroup, avatar) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
private static final String CHECK_PASSWORD = "SELECT id_player, passwd FROM players WHERE pseudo=?";
private static final String SCORE = "SELECT score FROM completions WHERE fk_player = ? AND fk_puzzle = ?";
private static final String GET_PUZZLE_SOLUTION = "SELECT soluce FROM puzzles WHERE id_puzzle=?";
private static final String GET_PUZZLE_SCORE_MAX = "SELECT score_max FROM puzzles WHERE id_puzzle=?";
private static final String GET_PLAYER_ID_BY_PSEUDO = "SELECT id_player FROM players WHERE pseudo=?";
private static final String GET_PUZZLE_NB_TRIES_AND_SCORE_BY_PLAYER = "SELECT tries, score FROM completions WHERE fk_puzzle = ? AND fk_player = ?";
private static final String GET_COMPLETION = "SELECT id_completion, tries, fileName, score FROM completions WHERE fk_puzzle = ? AND fk_player = ?";
private static final String GET_PLAYER = "SELECT * FROM players WHERE id_player = ?";
private static final String INSERT_COMPLETION = "INSERT INTO completions (fk_puzzle, fk_player, tries, code, fileName, score) values (?, ?, ?, ?, ?, ?)";
private static final String UPDATE_COMPLETION = "UPDATE completions SET tries = ?, code = ?, filename = ?, score = ?";
private static final String UPDATE_COMPLETION = "UPDATE completions SET tries = ?, filename = ?, score = ? WHERE fk_puzzle = ? AND fk_player = ?";
private Connection con;
private Configuration config;
public DatabaseRepository(Configuration config){
public DatabaseRepository(Configuration config) {
this.config = config;
}
private void ensureConnection() throws SQLException{
if(con == null || (!con.isValid(5))){
this.con = DriverManager.getConnection("jdbc:mysql://"+config.getDbHost()+":"+config.getDbPort()+"/"+config.getDbDatabase()+"",config.getDbUser(), config.getDbPassword());
private void ensureConnection() throws SQLException {
if (con == null || (!con.isValid(5))) {
this.con = DriverManager.getConnection(
"jdbc:mysql://" + config.getDbHost() + ":" + config.getDbPort() + "/" + config.getDbDatabase() + "",
config.getDbUser(), config.getDbPassword());
}
}
private Puzzle makePuzzle(ResultSet puzzleResult) throws SQLException {
return new Puzzle(puzzleResult.getInt("id_puzzle"), puzzleResult.getString("name"), puzzleResult.getString("content"), null,"",0);
return new Puzzle(puzzleResult.getInt("id_puzzle"), puzzleResult.getString("name"),
puzzleResult.getString("content"), null, "", 0);
}
private Chapter makeChapter(ResultSet chapterResult) throws SQLException {
return new Chapter(chapterResult.getInt("id_chapter"), chapterResult.getString("name"));
}
private Completion makeCompletion(int playerId, int puzzleId, ResultSet completionResult) throws SQLException {
return new Completion(playerId, puzzleId, completionResult.getInt("id_completion"),
completionResult.getInt("tries"), completionResult.getString("fileName"),
completionResult.getInt("score"), null);
}
private Player makePlayer(ResultSet playerResult) throws SQLException {
return new Player(playerResult.getString("pseudo"), playerResult.getString("email"),
playerResult.getString("firstName"), playerResult.getString("LastName"),
playerResult.getString("description"), playerResult.getString("sgroup"));
}
private List<Puzzle> getPuzzlesInChapter(int id) throws SQLException {
List<Puzzle> puzzles = new ArrayList<>();
ensureConnection();
@ -87,20 +102,51 @@ public class DatabaseRepository {
return null;
}
public int getScore(int user, int puzzle){
public int getScore(int user, int puzzle) {
try {
ensureConnection();
PreparedStatement stmt = this.con.prepareStatement(SCORE);
stmt.setInt(1, user);
stmt.setInt(2, puzzle);
ResultSet result = stmt.executeQuery();
if(result.next()) result.getInt("score");
}catch(Exception e){
if (result.next())
result.getInt("score");
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
public Completion getCompletion(int playerId, int puzzleId) {
try {
PreparedStatement completionsStmt = con.prepareStatement(GET_COMPLETION);
completionsStmt.setInt(1, puzzleId);
completionsStmt.setInt(2, playerId);
ResultSet result = completionsStmt.executeQuery();
if (result.next()) {
return makeCompletion(playerId, puzzleId, result);
}
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public Player getPlayer(int idPlayer) {
try {
PreparedStatement completionsStmt = con.prepareStatement(GET_PLAYER);
completionsStmt.setInt(1, idPlayer);
ResultSet result = completionsStmt.executeQuery();
if (result.next()) {
return makePlayer(result);
}
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/**
* Get a specific chapter
*
@ -194,7 +240,8 @@ public class DatabaseRepository {
* @param avatar The avatar of the user
* @return True if the user was registered, false if an error occurred
*/
public int register(String pseudo, String email, String password, String firstname, String lastname, String description, String sgroup, String avatar) {
public int register(String pseudo, String email, String password, String firstname, String lastname,
String description, String sgroup, String avatar) {
Hash hash = Password.hash(password).withArgon2();
try {
ensureConnection();
@ -207,9 +254,10 @@ public class DatabaseRepository {
statement.setString(6, description);
statement.setString(7, sgroup);
statement.setString(8, avatar);
if(statement.executeUpdate() == 1){
if (statement.executeUpdate() == 1) {
ResultSet inserted = statement.getGeneratedKeys();
if(inserted.next()) return inserted.getInt("id_player");
if (inserted.next())
return inserted.getInt("id_player");
}
} catch (SQLException e) {
e.printStackTrace();
@ -232,97 +280,51 @@ public class DatabaseRepository {
ResultSet result = statement.executeQuery();
if (result.next()) {
String hashedPassword = result.getString("passwd");
if(Password.check(password, hashedPassword).withArgon2()) return result.getInt("id_player");
}
} catch (SQLException e) {
}
return -1;
}
public byte[] getPuzzleSolution(int puzzleId) {
try {
PreparedStatement puzzleStmt = con.prepareStatement(GET_PUZZLE_SOLUTION);
puzzleStmt.setInt(1, puzzleId);
ResultSet result = puzzleStmt.executeQuery();
if (result.next()) {
return result.getBytes("soluce");
}
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public int insertOrUpdatePuzzleResponse(int puzzleId, String pseudo, String fileName, byte[] code) {
try {
ensureConnection();
int[] triesAndScore = getPuzzleNbTriesAndScore(puzzleId, pseudo);
int playerId = getPlayerIdByPseudo(pseudo);
int puzzleScoreMax = getPuzzleScoreMax(puzzleId);
if (triesAndScore[0] < 0) {
// Insert completions
PreparedStatement statement = con.prepareStatement(INSERT_COMPLETION);
statement.setInt(1, puzzleId);
statement.setInt(2, playerId);
statement.setInt(3, 0);
statement.setBytes(4, code);
statement.setString(5, fileName);
statement.setInt(6, puzzleScoreMax);
return puzzleScoreMax;
} else {
// Update completions
int score = puzzleScoreMax * (((triesAndScore[0]-1)*10)/100);
PreparedStatement statement = con.prepareStatement(UPDATE_COMPLETION);
statement.setInt(1, triesAndScore[0]+1);
statement.setBytes(2, code);
statement.setString(3, fileName);
statement.setInt(4, score);
return score;
}
} catch (SQLException e) {
e.printStackTrace();
}
return -1;
}
public int getPuzzleScoreMax(int puzzleId) {
try {
PreparedStatement puzzleStmt = con.prepareStatement(GET_PUZZLE_SCORE_MAX);
puzzleStmt.setInt(1, puzzleId);
ResultSet result = puzzleStmt.executeQuery();
if (result.next()) {
return result.getInt("score_max");
}
} catch (SQLException e) {
e.printStackTrace();
}
return -1;
}
public int[] getPuzzleNbTriesAndScore(int puzzleId, String pseudo) {
try {
PreparedStatement puzzleStmt = con.prepareStatement(GET_PUZZLE_NB_TRIES_AND_SCORE_BY_PLAYER);
puzzleStmt.setInt(1, puzzleId);
ResultSet result = puzzleStmt.executeQuery();
int[] res = new int[2];
if (result.next()) {
res[0] = result.getInt("tries");
res[1] = result.getInt("score");
}
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
private int getPlayerIdByPseudo(String pseudo) {
try {
PreparedStatement puzzleStmt = con.prepareStatement(GET_PLAYER_ID_BY_PSEUDO);
puzzleStmt.setString(1, pseudo);
ResultSet result = puzzleStmt.executeQuery();
if (result.next()) {
if (Password.check(password, hashedPassword).withArgon2())
return result.getInt("id_player");
}
} catch (SQLException e) {
e.printStackTrace();
}
return -1;
}
public Completion insertOrUpdatePuzzleResponse(int puzzleId, int userId, String fileName, byte[] code) {
try {
Puzzle currentPuzzle = getPuzzle(puzzleId);
Completion completion = getCompletion(userId, puzzleId);
if (completion == null) {
insertCompletion(new Completion(userId, puzzleId, fileName, currentPuzzle.getScoreMax()));
} else {
completion.addTry();
updateCompletion(completion);
}
return completion;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
private void insertCompletion(Completion newCompletion) throws SQLException {
// Insert completions
PreparedStatement statement = con.prepareStatement(INSERT_COMPLETION);
statement.setInt(1, newCompletion.getPuzzleId());
statement.setInt(2, newCompletion.getPlayerId());
statement.setInt(3, newCompletion.getTries());
statement.setBytes(4, newCompletion.getCode());
statement.setString(5, newCompletion.getFileName());
statement.setInt(6, newCompletion.getScore());
statement.executeUpdate();
}
private void updateCompletion(Completion completionToUpdate) throws SQLException {
// Update completions
PreparedStatement statement = con.prepareStatement(UPDATE_COMPLETION);
statement.setInt(1, completionToUpdate.getTries());
statement.setString(2, completionToUpdate.getFileName());
statement.setInt(3, completionToUpdate.getScore());
statement.setInt(4, completionToUpdate.getPuzzleId());
statement.setInt(5, completionToUpdate.getPlayerId());
statement.executeUpdate();
}
}

View file

@ -1,11 +1,17 @@
package be.jeffcheasey88.peeratcode.routes;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.simple.JSONObject;
import be.jeffcheasey88.peeratcode.model.Completion;
import be.jeffcheasey88.peeratcode.model.Player;
import be.jeffcheasey88.peeratcode.repository.DatabaseRepository;
import be.jeffcheasey88.peeratcode.webserver.HttpReader;
import be.jeffcheasey88.peeratcode.webserver.HttpUtil;
@ -22,54 +28,39 @@ public class PuzzleResponse implements Response {
@Override
public void exec(Matcher matcher, User user, HttpReader reader, HttpWriter writer) throws Exception {
if(user == null){
if (user == null) {
HttpUtil.responseHeaders(writer, 403, "Access-Control-Allow-Origin: *");
return;
}
HttpUtil.skipHeaders(reader);
int puzzleId = Integer.parseInt(matcher.group(1));
byte[] response;
byte[] fileName;
byte[] sourceCode;
// Response
int hSize = reader.readInt();
response = new byte[hSize];
if (hSize == reader.read(response)) {
// File Name
hSize = reader.readInt();
fileName = new byte[hSize];
if (hSize == reader.read(fileName)) {
// Source Code
hSize = reader.readInt();
sourceCode = new byte[hSize];
if (hSize == reader.read(sourceCode)) {
int score = databaseRepo.insertOrUpdatePuzzleResponse(puzzleId, "Pseudo", fileName.toString(), sourceCode);
if (Arrays.equals(response, databaseRepo.getPuzzleSolution(puzzleId))) {
HttpUtil.responseHeaders(writer, 200,
"Access-Control-Allow-Origin: *",
"Content-Type: application/json");
ReceivedResponse received = new ReceivedResponse(matcher, reader);
saveSourceCode(received, databaseRepo.getPlayer(user.getId()));
JSONObject responseJSON = new JSONObject();
responseJSON.put("id", puzzleId);
responseJSON.put("score", score);
Completion completion = databaseRepo.insertOrUpdatePuzzleResponse(received.getPuzzleId(), 3,
received.getFileName(), received.getSourceCode());
if (Arrays.equals(received.getResponse(), databaseRepo.getPuzzle(received.getPuzzleId()).getSoluce())) {
HttpUtil.responseHeaders(writer, 200, "Access-Control-Allow-Origin: *", "Content-Type: application/json");
responseJSON.put("score", completion.getScore());
responseJSON.put("tries", completion.getTries());
} else if (completion != null) {
HttpUtil.responseHeaders(writer, 406, "Access-Control-Allow-Origin: *", "Content-Type: application/json");
responseJSON.put("tries", completion.getTries());
} else {
HttpUtil.responseHeaders(writer, 403, "Access-Control-Allow-Origin: *");
return;
}
writer.write(responseJSON.toJSONString());
writer.flush();
writer.close();
} else {
HttpUtil.responseHeaders(writer, 406, "Access-Control-Allow-Origin: *");
}
}
else {
HttpUtil.responseHeaders(writer, 400, "Access-Control-Allow-Origin: *");
}
}
else {
HttpUtil.responseHeaders(writer, 400, "Access-Control-Allow-Origin: *");
}
}
HttpUtil.responseHeaders(writer, 403, "Access-Control-Allow-Origin: *");
private void saveSourceCode(ReceivedResponse received, Player player) throws IOException {
Path path = Paths.get(player.getPathToSourceCode() + received.getFileName());
Files.write(path, received.getSourceCode());
}
@Override
@ -82,3 +73,60 @@ public class PuzzleResponse implements Response {
return "POST";
}
}
class ReceivedResponse {
private int puzzleId;
private byte[] response;
private String fileName;
private byte[] sourceCode;
private HttpReader reader;
public ReceivedResponse(Matcher matcher, HttpReader reader) throws Exception {
this.reader = reader;
puzzleId = Integer.parseInt(matcher.group(1));
readResponse();
readFileName();
readSourceCode();
}
private void readResponse() throws Exception {
int hSize = reader.readInt();
response = new byte[hSize];
if (hSize != reader.read(response))
response = null;
}
private void readFileName() throws Exception {
byte[] tmpFileName;
int hSize = reader.readInt();
tmpFileName = new byte[hSize];
if (hSize == reader.read(tmpFileName))
fileName = tmpFileName.toString();
else
fileName = null;
}
private void readSourceCode() throws Exception {
int hSize = reader.readInt();
sourceCode = new byte[hSize];
if (hSize != reader.read(sourceCode))
sourceCode = null;
}
public int getPuzzleId() {
return puzzleId;
}
public byte[] getResponse() {
return response;
}
public String getFileName() {
return fileName;
}
public byte[] getSourceCode() {
return sourceCode;
}
}

View file

@ -1,10 +1,14 @@
package be.jeffcheasey88.peeratcode.routes;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.simple.JSONObject;
import be.jeffcheasey88.peeratcode.model.Player;
import be.jeffcheasey88.peeratcode.repository.DatabaseRepository;
import be.jeffcheasey88.peeratcode.webserver.HttpReader;
import be.jeffcheasey88.peeratcode.webserver.HttpUtil;
@ -49,10 +53,11 @@ public class Register implements Response {
boolean emailAvailable = databaseRepo.checkEmailAvailability(email);
if (pseudoAvailable && emailAvailable) {
int id;
if ((id = databaseRepo.register(pseudo, email, password, firstname, lastname, description, group, avatar)) >= 0){
HttpUtil.responseHeaders(writer, 200,
"Access-Control-Allow-Origin: *",
"Authorization: Bearer "+this.router.createAuthUser(id));
if ((id = databaseRepo.register(pseudo, email, password, firstname, lastname, description, group,
avatar)) >= 0) {
HttpUtil.responseHeaders(writer, 200, "Access-Control-Allow-Origin: *",
"Authorization: Bearer " + this.router.createAuthUser(id));
createFolderToSaveSourceCode(pseudo);
return;
}
} else {
@ -67,13 +72,17 @@ public class Register implements Response {
HttpUtil.responseHeaders(writer, 403, "Access-Control-Allow-Origin: *");
}
private void createFolderToSaveSourceCode(String pseudo) throws IOException {
Files.createDirectories(Paths.get(String.format(Player.PATH_TO_CODE, pseudo)));
}
@Override
public Pattern getPattern() {
return Pattern.compile("^\\/register$");
}
@Override
public String getType(){
public String getType() {
return "POST";
}