1
0
forked from PGL/Clyde

Huge commit

- Rework the inscription requests system so that it considers the equivalence systems and the impact of the teacher in the inscription procedure.
This commit is contained in:
LeoMoulin 2024-04-11 16:45:48 +02:00
parent 34c0a2bfe8
commit 4199663d64
18 changed files with 253 additions and 192 deletions

View File

@ -41,4 +41,12 @@ public class ExternalCurriculumController {
ArrayList<ExternalCurriculum> toReturn = ecr.getExternalCurriculumByInscriptionRequest(ir);
return new ResponseEntity<>(toReturn, HttpStatus.OK);
}
@PatchMapping("/externalcurriculum/{extcurrid}/{newstate}")
public ResponseEntity<Object> changeExternalCurrState(@PathVariable long extcurrid, @PathVariable RequestState newstate){
ExternalCurriculum externalCurriculum = ecr.getExternalCurriculumById(extcurrid);
externalCurriculum.setState(newstate);
ecr.save(externalCurriculum);
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@ -1,5 +1,6 @@
package ovh.herisson.Clyde.EndPoints;
import org.apache.tomcat.util.http.parser.Authorization;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@ -79,4 +80,22 @@ public class InscriptionController {
return new ResponseEntity<>(HttpStatus.OK);
}
//Allow teacher or admin to accept or refuse the equivalence
@PatchMapping("/request/registerequiv/{id}/{newstate}")
public ResponseEntity<Object> editRegisterEquiv(@RequestHeader("Authorization") String token, @PathVariable long id, @PathVariable RequestState newstate){
if (authServ.isNotIn(new Role[]{Role.Admin,Role.Teacher}, token))
return new UnauthorizedResponse<>(null);
InscriptionRequest toEdit = inscriptionServ.getById(id);
toEdit.setEquivalenceState(newstate);
inscriptionServ.save(toEdit);
if (toEdit.getState() == RequestState.Accepted && (toEdit.getEquivalenceState() == RequestState.Accepted || toEdit.getEquivalenceState() == RequestState.Unrequired))
{
inscriptionServ.createUser(toEdit);
}
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@ -5,10 +5,15 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import ovh.herisson.Clyde.Repositories.CurriculumRepository;
import ovh.herisson.Clyde.Repositories.InscriptionRepository;
import ovh.herisson.Clyde.Responses.UnauthorizedResponse;
import ovh.herisson.Clyde.Services.AuthenticatorService;
import ovh.herisson.Clyde.Services.ProtectionService;
import ovh.herisson.Clyde.Tables.Curriculum;
import ovh.herisson.Clyde.Tables.InscriptionRequest;
import ovh.herisson.Clyde.Tables.RequestState;
import java.util.Date;
import java.util.Map;
@ -16,7 +21,7 @@ import java.util.Map;
@CrossOrigin(originPatterns = "*", allowCredentials = "true")
public class LoginController {
private final AuthenticatorService authServ;
private final CurriculumRepository curriculumRepository;
static public class RequestLogin{
private final String identifier;
private final String password;
@ -29,8 +34,9 @@ public class LoginController {
}
}
public LoginController(AuthenticatorService authServ){
public LoginController(AuthenticatorService authServ, CurriculumRepository curriculumRepository){
this.authServ = authServ;
this.curriculumRepository = curriculumRepository;
}
@PostMapping(value = "/login")
@ -48,9 +54,18 @@ public class LoginController {
@PostMapping("/register")
public ResponseEntity<Map<String,Object>> register(@RequestBody InscriptionRequest inscriptionRequest){
//We ensure here that if the targeted cursus year is more than first grade then we need the teacher equivalence approval
Curriculum curr = curriculumRepository.findById(inscriptionRequest.getCurriculumId());
if (curr.getYear() > 1){
inscriptionRequest.setEquivalenceState(RequestState.Pending);
}else{
inscriptionRequest.setEquivalenceState(RequestState.Unrequired);
}
InscriptionRequest returnedInscriptionRequest = authServ.register(inscriptionRequest);
return new ResponseEntity<>(ProtectionService.requestWithoutPassword(returnedInscriptionRequest), HttpStatus.CREATED);
}
}

View File

@ -21,7 +21,7 @@ public class MockController {
public final CurriculumCourseService CurriculumCourseService;
public final CurriculumService curriculumService;
public final CourseService courseService;
public final ExternalCurriculumRepository externalCurriculumRepository;
public final InscriptionService inscriptionService;
ArrayList<User> mockUsers;
@ -31,13 +31,14 @@ public class MockController {
public final ScholarshipRequestRepository scholarshipRequestRepository;
public MockController(UserRepository userRepo, TokenRepository tokenRepo, TokenService tokenService, CurriculumCourseService CurriculumCourseService, CurriculumService curriculumService, CourseService courseService, InscriptionService inscriptionService, UserCurriculumRepository ucr, MinervalRepository minervalRepository, ScholarshipRequestRepository scholarshipRequestRepository){
public MockController(UserRepository userRepo, TokenRepository tokenRepo, TokenService tokenService, CurriculumCourseService CurriculumCourseService, CurriculumService curriculumService, CourseService courseService, ExternalCurriculumRepository externalCurriculumRepository, InscriptionService inscriptionService, UserCurriculumRepository ucr, MinervalRepository minervalRepository, ScholarshipRequestRepository scholarshipRequestRepository){
this.tokenRepo = tokenRepo;
this.userRepo = userRepo;
this.tokenService = tokenService;
this.CurriculumCourseService = CurriculumCourseService;
this.curriculumService = curriculumService;
this.courseService = courseService;
this.externalCurriculumRepository = externalCurriculumRepository;
this.inscriptionService = inscriptionService;
this.ucr = ucr;
this.minervalRepository = minervalRepository;
@ -114,9 +115,12 @@ public class MockController {
CurriculumCourseService.save(new CurriculumCourse(chemistryBab1,chemistry1));
InscriptionRequest inscriptionRequest = new InscriptionRequest("helen","prenom","non","helen@gmail.com","america",new Date(),(long) 1,RequestState.Pending,"yes.png","password", null, new Date());
InscriptionRequest inscriptionRequest = new InscriptionRequest("helen","prenom","non","helen@gmail.com","america",new Date(),(long) 4,RequestState.Pending,"yes.png","password", null, new Date(), RequestState.Pending);
inscriptionService.save(inscriptionRequest);
ExternalCurriculum externalCurriculum = new ExternalCurriculum(inscriptionRequest, "HEH", "Bachelier en informatique", "Completed", 2015, 2018, null, RequestState.Pending);
externalCurriculumRepository.save(externalCurriculum);
}
}

View File

@ -9,4 +9,6 @@ import java.util.ArrayList;
public interface ExternalCurriculumRepository extends CrudRepository<ExternalCurriculum, Long> {
ArrayList<ExternalCurriculum> getExternalCurriculumByInscriptionRequest(InscriptionRequest ir);
ExternalCurriculum getExternalCurriculumById(long id);
}

View File

@ -47,21 +47,20 @@ public class InscriptionService {
if (inscrRequest == null)
return false;
// if th state is the same we don't send an email
if (requestState == inscrRequest.getState())
return false;
inscrRequest.setState(requestState);
save(inscrRequest);
/** todo send an email to tell the poster of the inscrRequest (inscrRequest.getEmail())
* to notify them that the state of their request changed
* FooEmailFormat toSend = (String.format("Your request state changed from %s to %s"),
* inscrRequest.getState(), requestState)
* FooEmailSender.send(toSend, inscrRequest.getEmail())
*/
//saves the user from the request if accepted
if (requestState == RequestState.Accepted)
//saves the user from the request if accepted from teacher and inscription services
if (requestState == RequestState.Accepted && (inscrRequest.getEquivalenceState() == RequestState.Accepted || inscrRequest.getEquivalenceState() == RequestState.Unrequired))
{
return createUser(inscrRequest);
}
return true;
}
public boolean createUser(InscriptionRequest inscrRequest){
//We must send an email here
if (curriculumRepo.findById(inscrRequest.getCurriculumId()) == null)
return false;
@ -83,12 +82,8 @@ public class InscriptionService {
Minerval minerval = new Minerval(userFromRequest.getRegNo(), 0, 852, 2023);
minervalRepository.save(minerval);
}
inscrRequest.setState(requestState);
save(inscrRequest);
return true;
}
public void delete(InscriptionRequest toDelete) {
inscriptionRepo.delete(toDelete);
}

View File

@ -89,6 +89,7 @@ public class ProtectionService {
toReturn.put("profilePictureUrl", inscriptionRequest.getProfilePicture());
toReturn.put("identityCard", inscriptionRequest.getIdentityCard());
toReturn.put("submissionDate", inscriptionRequest.getSubmissionDate());
toReturn.put("equivalenceState", inscriptionRequest.getEquivalenceState());
return toReturn;
}

View File

@ -21,9 +21,10 @@ public class InscriptionRequest {
private String password;
private String identityCard;
private Date submissionDate;
private RequestState equivalenceState;
public InscriptionRequest(){}
public InscriptionRequest(String lastName, String firstName, String address, String email, String country, Date birthDate,Long curriculumId, RequestState state, String profilePicture, String password, String identityCard, Date submissionDate){
public InscriptionRequest(String lastName, String firstName, String address, String email, String country, Date birthDate,Long curriculumId, RequestState state, String profilePicture, String password, String identityCard, Date submissionDate, RequestState equivalenceState){
this.lastName = lastName;
this.firstName = firstName;
this.address = address;
@ -36,6 +37,7 @@ public class InscriptionRequest {
this.password = password;
this.identityCard = identityCard;
this.submissionDate = submissionDate;
this.equivalenceState = equivalenceState;
}
public int getId() {
@ -137,4 +139,12 @@ public class InscriptionRequest {
public void setSubmissionDate(Date submissionDate) {
this.submissionDate = submissionDate;
}
public RequestState getEquivalenceState() {
return equivalenceState;
}
public void setEquivalenceState(RequestState equivalenceState) {
this.equivalenceState = equivalenceState;
}
}

View File

@ -3,5 +3,6 @@ package ovh.herisson.Clyde.Tables;
public enum RequestState {
Accepted,
Refused,
Pending
Pending,
Unrequired
}

View File

@ -1,27 +0,0 @@
package ovh.herisson.Clyde.Tables;
import jakarta.persistence.*;
@Entity
public class University {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
public University(){}
public University(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -1,27 +1,35 @@
<script setup>
import i18n from "@/i18n.js"
import {getUser} from '../rest/Users.js'
import {getSelf, getUser} from '../rest/Users.js'
import {getcurriculum,getSomeonesCurriculumList} from "@/rest/curriculum.js";
import {getRegisters} from "@/rest/ServiceInscription.js";
import {get} from "jsdom/lib/jsdom/named-properties-tracker.js";
import {getExternalCurriculumByInscrReq} from "@/rest/externalCurriculum.js";
import {ref} from "vue";
import ExternalCurriculumList from "@/Apps/ExternalCurriculumList.vue";
import {editEquivalenceState} from "@/rest/requests.js";
const props = defineProps(['target']);
let request = await getRegisters(props.target);
const request = await getRegisters(props.target);
const cursus = await getcurriculum(request.curriculum);
const user = await getSelf();
const list = ref(false);
const externalCurriculum = await getExternalCurriculumByInscrReq(request.id)
console.log(externalCurriculum)
function getPP(){
if(request.profilePictureUrl === null){
return "/Clyde.png"
}
return request.profilePictureUrl;
}
async function editEquivalence(id, newstate){
await editEquivalenceState(id, newstate)
}
</script>
<template>
<div class="body">
<div class="body" v-if="list == false">
<div class="container">
<div class="profilPic">
<img class="subContainter" :src=getPP()>
@ -46,24 +54,20 @@ function getPP(){
<div>
Cursus voulu : BAB {{cursus.year}} {{cursus.option}}
</div>
</div>
</div>
<div class="moreInfos">
<div class = "oldcursus">
<div class="listTitle">
Cursus extérieurs a l'univesité
</div>
<div class="listElement">
<div class=" containerElement" v-for="item in externalCurriculum">
<div class="formation">item.formation</div>
<div class="school">item.school</div>
<div v-if="cursus.year > 1">
<button style="background-color:rgb(105,05,105);margin-left: 5%" @click="list=!list" v-if="(user.role == 'Teacher' || user.role == 'Admin')&& request.equivalenceState == 'Pending'">See external curriculums</button>
</div>
</div>
</div>
</div>
</div>
<div v-if="list==true">
<ExternalCurriculumList :ext-curr-list="externalCurriculum" :inscr-req-id="request.id"></ExternalCurriculumList>
<div>
<button @click="editEquivalence(request.id, 'Accepted'); request.equivalenceState='Accepted'">Accept Equivalence</button>
<button @click="editEquivalence(request.id, 'Refused'); request.equivalenceState='Refused'">Refuse Equivalence</button>
</div>
</div>
</template>
@ -117,50 +121,6 @@ function getPP(){
border-radius:20px;
}
.moreInfos {
display:grid;
grid-template-rows:200px auto;
column-gap:50px;
row-gap:45px;
grid-template-areas:
"minfos minfos";
grid-template-columns:600px 600px;
}
.listTitle{
display: flex;
justify-content: center;
align-items: center;
width:250px;
margin-left:auto;
margin-right:auto;
border:2px solid black;
font-size:25px;
color:white;
padding:20px;
background-color:rgb(50,50,50);
border-radius:20px;margin-bottom:10px;
}
.listElement{
border:2px solid black;
font-size:25px;
color:white;
padding:20px;
background-color:rgb(50,50,50);
border-radius:20px;
margin-bottom:10px;
}
.containerElement{
justify-content:center;
display:grid;
grid-template-columns:100px 100px 300px;
grid-template-areas:
"year option dateyear";
column-gap:40px;
padding-left: 25px;
}
button{
font-size:15px;
height:50px;
@ -169,42 +129,4 @@ button{
border-radius:20px;
}
.moreInfos {
display:grid;
grid-template-rows:200px auto;
column-gap:50px;
row-gap:45px;
grid-template-areas:
"minfos minfos";
grid-template-columns:600px 600px;
align-items:center;
justify-content:center;
margin-left: 320%;
}
.listTitle{
display: flex;
justify-content: center;
align-items: center;
width:250px;
margin-left:auto;
margin-right:auto;
border:2px solid black;
font-size:25px;
color:white;
padding:20px;
background-color:rgb(50,50,50);
border-radius:20px;margin-bottom:10px;
}
.listElement{
border:2px solid black;
font-size:25px;
color:white;
padding:20px;
background-color:rgb(50,50,50);
border-radius:20px;
margin-bottom:10px;
}
</style>

View File

@ -0,0 +1,103 @@
<script setup>
import i18n from "@/i18n.js";
import {editExternalCurriculum} from "@/rest/externalCurriculum.js";
import {ref} from "vue";
import {editEquivalenceState} from "@/rest/requests.js";
const props = defineProps(["extCurrList","inscrReqId"])
const extCurrList = ref(props.extCurrList)
</script>
<template style="margin-top:5%;">
<div style="display:flex; justify-content:center; " v-for="item in extCurrList">
<div class="bodu">
<div class="container">
<div class="status"><a style="margin-left:30px">{{item.state}}</a></div>
<div class="school"><a>{{item.school}}</a></div>
<div class="formation"><a>{{item.formation}}</a></div>
<div class="completion"><a>{{item.completion}}</a></div>
<div class="download"><button>Download document</button></div>
</div>
</div>
</div>
</template>
<style scoped>
.container{
color:white;
height:100px;
font-size:30px;
display:grid;
grid-template-columns:15% 10% 20% 15% 13.1%;
grid-template-areas:
"status school formation completion download";
column-gap:10px;
}
.status {
grid-area:status;
align-self:center;
font-size: 70%;
}
.school{
grid-area:school;
align-self:center;
font-size: 70%;
}
.formation{
grid-area:formation;
align-self:center;
font-size: 70%;
}
.completion{
grid-area:completion;
align-self:center;
white-space: nowrap;
overflow: hidden;
text-overflow:ellipsis;
font-size: 70%;
}
.accept{
grid-area:accept;
align-self:center;
white-space: nowrap;
overflow: hidden;
text-overflow:ellipsis;
}
.refuse{
grid-area: refuse;
align-self:center;
}
.download{
grid-area: download;
align-self:center;
}
button{
font-size:15px;
height:50px;
width:75%;
border:none;
border-radius:20px;
}
.bodu {
margin-top:2%;
width:66%;
border:2px solid black;
border-radius:9px;
background-color:rgb(50,50,50);
}
</style>

View File

@ -16,9 +16,7 @@
async function upPage(id,review){
await validateRegister(id,review);
if (review == "Accepted"){
}
requests.value = await getAllRegisters();
}
@ -40,7 +38,7 @@
<template>
<div v-if="windowsState === 1">
<AboutRequest :target="targetId"></AboutRequest>
<button style="background-color:rgb(105,05,105);margin-left: 30%" @click="windowsState=0;">Retour</button>
<button style="background-color:rgb(105,05,105);margin-left: 30%; margin-top: 5%" @click="windowsState=0;loadRequests()">Retour</button>
</div>
<div v-if="windowsState === 0">
<div style="margin-top: 2%;margin-left: 2%">
@ -61,15 +59,16 @@
</span>
</div>
<div style='display:flex; justify-content:center; min-width:1140px;' v-for="item of requests">
<div class="bodu" v-if="filterType == 'None' || filterType == item.state">
<div class="container" style="grid-template-columns:15% 15% 10% 14.2% 14.2% 14.2% 14.2%;grid-template-areas:'date state surname firstname accept refuse infos';" v-if="requestType === 'inscription'">
<div class="bodu" style="width: 95%" v-if="filterType == 'None' || filterType == item.state">
<div class="container" style="grid-template-columns:11% 15% 20% 10% 10% 9% 9%;grid-template-areas:'date state equivalencestate surname firstname accept refuse infos';" v-if="requestType === 'inscription'">
<!--
The condition below avoids an error occuring because loadRequests() finishes after the vue refresh
then submissionDate is undefined an it triggers an error in the console despite the fact that it is working
properly at the end.
-->
<div class="date" v-if="item.submissionDate !== undefined">{{item.submissionDate.slice(0, 10)}}</div>
<div class="state">{{item.state}}</div>
<div class="state" style="font-size: 80%">Approval : {{item.state}}</div>
<div class="equivalencestate" style="font-size: 80%">Teacher approval : {{item.equivalenceState}}</div>
<div class="surname">{{item.lastName}}</div>
<div class="firstname">{{item.firstName}}</div>
<div class="accept" v-if="item.state === 'Pending'"><button @click="windowsState=2;targetId=item.id;" style="background-color:rgb(0,105,50);">{{i18n("request.accept")}}</button></div>
@ -110,6 +109,10 @@
column-gap:10px;
}
.equivalencestate{
grid-area: equivalencestate;
align-self: center;
}
.studentfirstname{
grid-area: studentfirstname;
align-self: center;

View File

@ -20,6 +20,7 @@
address:null,
country:null,
curriculum:null,
equivalenceState: "Unrequired"
})
const notcompletedCheck = ref(false);
@ -76,18 +77,12 @@
//This functions makes the distinction between a master cursus (year 4 or more) and a bachelor cursus (year 3 or less)
function getCursusDisplay(cursus){
if (cursus.year <= 3){
return cursus.curriculumId + " BAB " + cursus.year + " " + cursus.option;
return "BAB " + cursus.year + " " + cursus.option;
}else{
return cursus.curriculumId + " MA" + (parseInt(cursus.year)-3).toString() + " " + cursus.option;
return "MA" + (parseInt(cursus.year)-3).toString() + " " + cursus.option;
}
}
//This function return the id of a decorated cursus (decorated by the function up)
function parseDecoratedCursus(cursus){
let id = cursus.substring(0, cursus.indexOf(" ")+1);
return id;
}
async function getCurriculumYear(curriculumId){
const curriculum = await getcurriculum(curriculumId);
return parseInt(curriculum.year);
@ -97,7 +92,7 @@
//Post the register request and return the id of the newly created request and also post the external curriculum list in the database
async function postRegisterReq(){
const val = await register(outputs.firstname, outputs.surname, outputs.birthday, outputs.password, outputs.email, outputs.address, outputs.country, parseDecoratedCursus(outputs.curriculum), ppData, null, new Date());
const val = await register(outputs.firstname, outputs.surname, outputs.birthday, outputs.password, outputs.email, outputs.address, outputs.country, outputs.curriculum, ppData, null, new Date(), outputs.equivalenceState);
for (let item in externalCurrTab.value){
await createExternalCurriculum(val.id, externalCurrTab.value[item].school, externalCurrTab.value[item].formation, externalCurrTab.value[item].completion, externalCurrTab.value[item].startYear, externalCurrTab.value[item].endYear, externalCurrTab.value[item].justifdocUrl);
}
@ -198,7 +193,7 @@
<div class="inputBox">
<p>{{i18n("Curriculum").toUpperCase()}}</p>
<select v-model="outputs.curriculum">
<option v-for="item in curricula">{{getCursusDisplay(item)}}</option>
<option v-for="item in curricula" :value="item.curriculumId">{{getCursusDisplay(item)}}</option>
</select>
</div>
<p style="color:rgb(239,60,168);">

View File

@ -43,3 +43,4 @@ export async function getAllRegisters(){
export async function validateRegister(id, state){
return restPatch("/request/register/" + id, state);
}

View File

@ -26,7 +26,7 @@ export function disconnect(){
* @param curriculum
* @param imageId id of the image in database returned when uploaded
*/
export async function register(firstname, lastname, birthDate, password, email, address, country, curriculumId, imageId, identityCardId, submissionDate){
export async function register(firstname, lastname, birthDate, password, email, address, country, curriculumId, imageId, identityCardId, submissionDate, equivalence){
return restPost("/register", {
firstName: firstname,
lastName: lastname,
@ -38,7 +38,8 @@ export async function register(firstname, lastname, birthDate, password, email,
curriculumId: curriculumId,
profilePicture: imageId,
identityCard : identityCardId,
submissionDate : submissionDate
submissionDate : submissionDate,
equivalenceState : equivalence
});
}

View File

@ -1,4 +1,4 @@
import {restGet, restPost} from "@/rest/restConsumer.js";
import {restGet, restPatch, restPost} from "@/rest/restConsumer.js";
import {parseInteger} from "jsdom/lib/jsdom/living/helpers/strings.js";
export async function createExternalCurriculum(inscriptionRequestId,school, formation, completion, startYear, endYear, justifdocUrl){
@ -14,5 +14,9 @@ export async function createExternalCurriculum(inscriptionRequestId,school, form
}
export async function getExternalCurriculumByInscrReq(inscrReqId){
return restGet("/externalcurriculum/"+inscrReqId);
return restGet("/externalcurriculum/"+inscrReqId)
}
export async function editExternalCurriculum(extReqId, newState){
return restPatch("/externalcurriculum/"+extReqId+"/"+newState)
}

View File

@ -1,4 +1,4 @@
import {restGet, restPost} from "@/rest/restConsumer.js";
import {restGet, restPatch, restPost} from "@/rest/restConsumer.js";
export async function createExemptionsRequest(exempReq){
return restPost("/exemptionreq", exempReq)
@ -15,3 +15,7 @@ export async function getAllScholarShipsRequest(){
export async function getAllExemptionsRequest(){
return restGet("/exemptionsreq")
}
export async function editEquivalenceState(id, newstate){
return restPatch("/request/registerequiv/"+id+"/"+newstate)
}