25 Commits

Author SHA1 Message Date
801ecb3817 Merge branch 'main' into fix_cache
All checks were successful
Format / formatting (push) Successful in 5s
Build / build (push) Successful in 46s
CI / build (push) Successful in 11s
Format / formatting (pull_request) Successful in 6s
2025-04-06 20:56:22 +02:00
cc89d4c79f fix: reverted previous commit, cache juste does not work
Some checks are pending
Format / formatting (push) Waiting to run
Build / build (push) Waiting to run
CI / build (push) Waiting to run
Format / formatting (pull_request) Successful in 6s
2025-04-06 20:55:35 +02:00
adf9a93e2e Merge pull request 'feat: enabled graddle cache. Subsequent actions should be fasters' (#7) from fix_cache into main
Some checks failed
CI / build (push) Waiting to run
Format / formatting (push) Successful in 5s
Build / build (push) Has been cancelled
Reviewed-on: #7
2025-04-06 20:41:13 +02:00
37d8bcc719 feat: enabled graddle cache. Subsequent actions should be fasters
Some checks failed
Format / formatting (push) Successful in 5s
Build / build (push) Has been cancelled
CI / build (push) Successful in 12s
Format / formatting (pull_request) Successful in 6s
2025-04-06 20:39:45 +02:00
ead11215ba Merge pull request 'backend-api' (#6) from backend-api into main
All checks were successful
Format / formatting (push) Successful in 6s
Build / build (push) Successful in 38s
CI / build (push) Successful in 10s
Reviewed-on: #6
Reviewed-by: Theo <tlelez@enseirb-matmeca.fr>
Reviewed-by: adnane <adnane.alami@bordeaux-inp.fr>
Reviewed-by: mohamed_maoulainine <mohamed_maoulainine.maoulainine@bordeaux-inp.fr>
2025-03-26 19:04:08 +01:00
137bc84c21 Merge branch 'backend-api' of ssh://gitea.piair.dev:2222/piair/MyINPulse into backend-api
All checks were successful
Format / formatting (push) Successful in 6s
Build / build (push) Successful in 38s
CI / build (push) Successful in 10s
Format / formatting (pull_request) Successful in 5s
2025-03-19 12:06:00 +01:00
3c61fdca93 feat: finished implementing apiService functions 2025-03-19 12:05:56 +01:00
5ee3755548 feat: added new tests and fixed few issues
All checks were successful
Format / formatting (push) Successful in 7s
Build / build (push) Successful in 38s
CI / build (push) Successful in 12s
2025-03-19 12:05:01 +01:00
52511dd4c4 fix: Makefile now run everything needed to build the app 2025-03-19 10:42:46 +01:00
84b70f8f38 fix: sometimes, project administrators may be null. Fixing nullPointerException
All checks were successful
Format / formatting (push) Successful in 6s
Build / build (push) Successful in 37s
CI / build (push) Successful in 11s
2025-03-17 09:18:21 +01:00
834d68949c fix: tabulation error
Some checks failed
Format / formatting (push) Successful in 6s
Build / build (push) Failing after 39s
CI / build (push) Successful in 11s
2025-03-17 09:08:33 +01:00
fea8687664 feat: now running tests
All checks were successful
Format / formatting (push) Successful in 6s
CI / build (push) Successful in 10s
2025-03-17 09:05:24 +01:00
c94d3654ce fix: updating foreign keys when adding new entity to the db
All checks were successful
Format / formatting (push) Successful in 7s
Build / build (push) Successful in 31s
CI / build (push) Successful in 11s
2025-03-15 15:23:18 +01:00
d5c89bf8f4 fix: spelling
All checks were successful
Format / formatting (push) Successful in 5s
Build / build (push) Successful in 27s
CI / build (push) Successful in 11s
2025-03-12 12:25:26 +01:00
78c72bdd72 fix: linter
Some checks failed
Format / formatting (push) Failing after 5s
Build / build (push) Successful in 27s
CI / build (push) Successful in 10s
2025-03-12 12:23:27 +01:00
307c7e700b merge
Some checks failed
Format / formatting (push) Failing after 5s
Build / build (push) Successful in 27s
CI / build (push) Successful in 10s
2025-03-12 12:19:35 +01:00
8d486dce89 feat: continued implementing adminApiService 2025-03-12 12:16:01 +01:00
653f923693 fix: bugfix
All checks were successful
Format / formatting (push) Successful in 5s
Build / build (push) Successful in 27s
CI / build (push) Successful in 11s
2025-03-12 12:08:49 +01:00
64da3c9ab0 feat: tests on AdminApiService 2025-03-12 12:07:48 +01:00
419ceec1bc feat: switched from String to ProjectDecisionValues
All checks were successful
Format / formatting (push) Successful in 6s
Build / build (push) Successful in 28s
CI / build (push) Successful in 11s
2025-03-12 10:25:19 +01:00
e011a5534e feat: switched from String to ProjectDecisionValues
All checks were successful
Format / formatting (push) Successful in 6s
Build / build (push) Successful in 30s
CI / build (push) Successful in 15s
2025-03-12 10:21:08 +01:00
ef964c4d35 fix: removed id + renamed mainEmail to primaryEmail everywhere
All checks were successful
Format / formatting (push) Successful in 8s
Build / build (push) Successful in 28s
CI / build (push) Successful in 11s
2025-03-11 13:02:17 +01:00
5608b12f84 fix: removed id + renamed mainEmail to primaryEmail everywhere 2025-03-11 13:01:53 +01:00
467babab79 fix: removed id + renamed mainEmail to primaryEmail everywhere 2025-03-11 13:01:28 +01:00
a2e2395cc2 feat: added new tests and coverage report 2025-03-11 13:00:38 +01:00
26 changed files with 798 additions and 79 deletions

View File

@ -9,6 +9,14 @@ jobs:
steps:
- name: Checkout sources
uses: actions/checkout@v4
- name: Load .env file
uses: xom9ikk/dotenv@v2.3.0
with:
path: ./config/
mode: dev
load-mode: strict
- name: Setup Java
uses: actions/setup-java@v4
with:
@ -18,8 +26,8 @@ jobs:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
cache-disabled: true
cache-disabled: true # Once the code has been pushed once in main, this should be reenabled.
- name: init gradle
working-directory: ./MyINPulse-back/
run: ./gradlew build -x test # todo: run test, currently fail because no database is present
run: ./gradlew build # todo: run test, currently fail because no database is present

View File

@ -19,8 +19,14 @@ front/MyINPulse-front/.installed:
vite: ./front/MyINPulse-front/.installed
keycloak: ./keycloak/.installed
dev-front: clean vite
keycloak/.installed:
@echo "running one time install"
@cd keycloak/CAS && sudo sh build.sh
@touch ./keycloak/.installed
dev-front: clean vite keycloak
@cp config/frontdev.env front/MyINPulse-front/.env
@cp config/frontdev.env .env
@cp config/frontdev.env MyINPulse-back/.env
@ -28,7 +34,7 @@ dev-front: clean vite
@docker compose up -d --build
@cd ./front/MyINPulse-front/ && npm run dev
prod: clean
prod: clean keycloak
@cp config/prod.env front/MyINPulse-front/.env
@cp config/prod.env .env
@cp config/prod.env .env
@ -37,7 +43,7 @@ prod: clean
dev-back:
dev-back: keycloak
@cp config/backdev.env front/MyINPulse-front/.env
@cp config/backdev.env .env
@cp config/backdev.env MyINPulse-back/.env
@ -46,7 +52,7 @@ dev-back:
@echo "cd MyINPulse-back" && echo 'export $$(cat .env | xargs)'
@echo "./gradlew bootRun --args='--server.port=8081'"
dev: clean vite
dev: clean vite keycloak
@cp config/dev.env front/MyINPulse-front/.env
@cp config/dev.env .env
@cp config/dev.env MyINPulse-back/.env
@ -55,3 +61,13 @@ dev: clean vite
@echo "cd MyINPulse-back" && echo 'export $$(cat .env | xargs)'
@echo "./gradlew bootRun --args='--server.port=8081'"
@cd ./front/MyINPulse-front/ && npm run dev &
test-back: clean keycloak
@cp config/dev.env front/MyINPulse-front/.env
@cp config/dev.env .env
@cp config/dev.env MyINPulse-back/.env
@cp config/dev.docker-compose.yaml docker-compose.yaml
@docker compose up -d --build
@echo "cd MyINPulse-back" && echo 'export $$(cat .env | xargs)'
@cd ./MyINPulse-back/ && ./gradlew test && ./gradlew jacocoTestReport
@firefox ./MyINPulse-back/build/jacocoHtml/index.html

View File

@ -2,6 +2,7 @@ plugins {
id 'java'
id 'org.springframework.boot' version '3.4.2'
id 'io.spring.dependency-management' version '1.1.7'
id 'jacoco'
}
group = 'enseirb'
@ -26,6 +27,7 @@ dependencies {
implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.+'
implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.+'
implementation 'org.postgresql:postgresql'
implementation group: 'com.itextpdf', name: 'itextpdf', version: '5.5.13.3'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'com.h2database:h2'
@ -36,3 +38,22 @@ dependencies {
tasks.named('test') {
useJUnitPlatform()
}
test {
finalizedBy jacocoTestReport // report is always generated after tests run
}
jacocoTestReport {
dependsOn test // tests are required to run before generating the report
reports {
xml.required = false
csv.required = false
html.outputLocation = layout.buildDirectory.dir('jacocoHtml')
}
}
jacoco {
toolVersion = "0.8.12"
reportsDirectory = layout.buildDirectory.dir('customJacocoReportDir')
}

View File

@ -81,7 +81,7 @@ public class AdminApi {
*/
@PostMapping("/admin/appoitements/report/{appointmentId}")
public void createAppointmentReport(
@PathVariable String appointmentId,
@PathVariable long appointmentId,
@RequestBody Report report,
@AuthenticationPrincipal Jwt principal) {
adminApiService.createAppointmentReport(

View File

@ -1,5 +1,7 @@
package enseirb.myinpulse.controller;
import com.itextpdf.text.DocumentException;
import enseirb.myinpulse.model.*;
import enseirb.myinpulse.service.SharedApiService;
@ -9,6 +11,9 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.net.URISyntaxException;
@SpringBootApplication
@RestController
public class SharedApi {
@ -78,7 +83,15 @@ public class SharedApi {
@GetMapping("/shared/projects/appointments/report/{appointmentId}")
public void getPDFReport(
@PathVariable int appointmentId, @AuthenticationPrincipal Jwt principal) {
try {
sharedApiService.getPDFReport(appointmentId, principal.getClaimAsString("email"));
} catch (DocumentException e) {
System.out.println(e + "Document exception");
} catch (URISyntaxException e) {
System.out.println(e + "Error with URI");
} catch (IOException e) {
System.out.println(e + "Failed to access file");
}
}
/**

View File

@ -34,9 +34,33 @@ public class Administrator extends User {
public Administrator(
String userSurname,
String username,
String mainMail,
String primaryMail,
String secondaryMail,
String phoneNumber) {
super(null, userSurname, username, mainMail, secondaryMail, phoneNumber);
super(null, userSurname, username, primaryMail, secondaryMail, phoneNumber);
}
public List<Project> getListProject() {
return listProject;
}
public void updateListProject(Project project) {
listProject.add(project);
}
public List<Annotation> getListAnnotation() {
return listAnnotation;
}
public void updateListAnnotation(Annotation annotation) {
listAnnotation.add(annotation);
}
public MakeAppointment getMakeAppointment() {
return makeAppointment;
}
public void setMakeAppointment(MakeAppointment makeAppointment) {
this.makeAppointment = makeAppointment;
}
}

View File

@ -34,4 +34,28 @@ public class Annotation {
public void setComment(String comment) {
this.comment = comment;
}
public Long getIdAnnotation() {
return idAnnotation;
}
public void setIdAnnotation(Long idAnnotation) {
this.idAnnotation = idAnnotation;
}
public SectionCell getSectionCellAnnotation() {
return sectionCellAnnotation;
}
public void setSectionCellAnnotation(SectionCell sectionCellAnnotation) {
this.sectionCellAnnotation = sectionCellAnnotation;
}
public Administrator getAdministratorAnnotation() {
return administratorAnnotation;
}
public void setAdministratorAnnotation(Administrator administratorAnnotation) {
this.administratorAnnotation = administratorAnnotation;
}
}

View File

@ -112,7 +112,15 @@ public class Appointment {
return listSectionCell;
}
public void updateListSectionCell(SectionCell sectionCell) {
listSectionCell.add(sectionCell);
}
public Report getAppointmentReport() {
return report;
}
public void setAppointmentReport(Report report) {
this.report = report;
}
}

View File

@ -37,21 +37,42 @@ public class Entrepreneur extends User {
public Entrepreneur() {}
public Entrepreneur(
Long idUser,
String userSurname,
String username,
String mainMail,
String primaryMail,
String secondaryMail,
String phoneNumber,
String school,
String course,
boolean sneeStatus) {
super(idUser, userSurname, username, mainMail, secondaryMail, phoneNumber);
super(userSurname, username, primaryMail, secondaryMail, phoneNumber);
this.school = school;
this.course = course;
this.sneeStatus = sneeStatus;
}
public Entrepreneur(
Long idUser,
String userSurname,
String userName,
String primaryMail,
String secondaryMail,
String phoneNumber,
String school,
String course,
boolean sneeStatus,
Project projectParticipation,
Project projectProposed,
MakeAppointment makeAppointment) {
super(idUser, userSurname, userName, primaryMail, secondaryMail, phoneNumber);
this.school = school;
this.course = course;
this.sneeStatus = sneeStatus;
this.projectParticipation = projectParticipation;
this.projectProposed = projectProposed;
this.makeAppointment = makeAppointment;
}
public String getSchool() {
return school;
}
@ -79,4 +100,24 @@ public class Entrepreneur extends User {
public Project getProjectParticipation() {
return projectParticipation;
}
public void setProjectParticipation(Project projectParticipation) {
this.projectParticipation = projectParticipation;
}
public Project getProjectProposed() {
return projectProposed;
}
public void setProjectProposed(Project projectProposed) {
this.projectProposed = projectProposed;
}
public MakeAppointment getMakeAppointment() {
return makeAppointment;
}
public void setMakeAppointment(MakeAppointment makeAppointment) {
this.makeAppointment = makeAppointment;
}
}

View File

@ -26,8 +26,7 @@ public class Project {
private byte[] logo;
private LocalDate creationDate;
@Column(length = 255)
private String projectStatus;
@Column private ProjectDecisionValue projectStatus;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "idAdministrator")
@ -39,16 +38,32 @@ public class Project {
public Project() {}
public Project(
Long idProject,
String projectName,
byte[] logo,
LocalDate creationDate,
String projectStatus) {
this.idProject = idProject;
ProjectDecisionValue projectStatus,
Administrator projectAdministrator) {
this.projectName = projectName;
this.logo = logo;
this.creationDate = creationDate;
// this.projectStatus = (long) projectStatus.ordinal();
this.projectStatus = projectStatus;
this.projectAdministrator = projectAdministrator;
}
public Project(
String projectName,
byte[] logo,
LocalDate creationDate,
ProjectDecisionValue projectStatus,
Administrator projectAdministrator,
Entrepreneur entrepreneurProposed) {
this.projectName = projectName;
this.logo = logo;
this.creationDate = creationDate;
this.projectStatus = projectStatus;
this.projectAdministrator = projectAdministrator;
this.entrepreneurProposed = entrepreneurProposed;
}
public Long getIdProject() {
@ -83,19 +98,43 @@ public class Project {
this.creationDate = creationDate;
}
public String getProjectStatus() {
public ProjectDecisionValue getProjectStatus() {
return projectStatus;
}
public void setProjectStatus(String projectStatus) {
public void setProjectStatus(ProjectDecisionValue projectStatus) {
this.projectStatus = projectStatus;
}
public Administrator getAdministrator() {
return this.projectAdministrator;
public List<Entrepreneur> getListEntrepreneurParticipation() {
return listEntrepreneurParticipation;
}
public void setAdministrator(Administrator administrator) {
this.projectAdministrator = administrator;
public void updateListEntrepreneurParticipation(Entrepreneur projectParticipant) {
listEntrepreneurParticipation.add(projectParticipant);
}
public List<SectionCell> getListSectionCell() {
return listSectionCell;
}
public void updateListSectionCell(SectionCell projectSectionCell) {
listSectionCell.add(projectSectionCell);
}
public Administrator getProjectAdministrator() {
return projectAdministrator;
}
public void setProjectAdministrator(Administrator projectAdministrator) {
this.projectAdministrator = projectAdministrator;
}
public Entrepreneur getEntrepreneurProposed() {
return entrepreneurProposed;
}
public void setEntrepreneurProposed(Entrepreneur entrepreneurProposed) {
this.entrepreneurProposed = entrepreneurProposed;
}
}

View File

@ -4,4 +4,22 @@ public class ProjectDecision {
public long projectId;
public long adminId;
public long isAccepted;
public ProjectDecision(long projectId, long adminId, long isAccepted) {
this.projectId = projectId;
this.adminId = adminId;
this.isAccepted = isAccepted;
}
@Override
public String toString() {
return "ProjectDecision{"
+ "projectId="
+ projectId
+ ", adminId="
+ adminId
+ ", isAccepted="
+ isAccepted
+ '}';
}
}

View File

@ -0,0 +1,9 @@
package enseirb.myinpulse.model;
public enum ProjectDecisionValue {
PENDING,
ACTIVE,
ENDED,
ABORTED,
REJECTED,
}

View File

@ -37,4 +37,12 @@ public class Report {
public void setReportContent(String reportContent) {
this.reportContent = reportContent;
}
public Appointment getAppointmentReport() {
return appointmentReport;
}
public void setAppointmentReport(Appointment appointmentReport) {
this.appointmentReport = appointmentReport;
}
}

View File

@ -11,7 +11,7 @@ import java.util.List;
public class SectionCell {
@ManyToMany(mappedBy = "listSectionCell")
private final List<Appointment> appointment = new ArrayList<>();
private final List<Appointment> listAppointment = new ArrayList<>();
@OneToMany(mappedBy = "sectionCellAnnotation", fetch = FetchType.LAZY, orphanRemoval = true)
private final List<Annotation> listAnnotation = new ArrayList<>();
@ -39,11 +39,13 @@ public class SectionCell {
Long idSectionCell,
Long sectionId,
String contentSectionCell,
LocalDateTime modificationDate) {
LocalDateTime modificationDate,
Project projectSectionCell) {
this.idSectionCell = idSectionCell;
this.sectionId = sectionId;
this.contentSectionCell = contentSectionCell;
this.modificationDate = modificationDate;
this.projectSectionCell = projectSectionCell;
}
public Long getIdSectionCell() {
@ -83,6 +85,26 @@ public class SectionCell {
}
public List<Appointment> getAppointmentSectionCell() {
return appointment;
return listAppointment;
}
public void updateAppointmentSectionCell(Appointment appointment) {
listAppointment.add(appointment);
}
public List<Annotation> getListAnnotation() {
return listAnnotation;
}
public void updateListAnnotation(Annotation annotation) {
listAnnotation.add(annotation);
}
public void setSectionId(long sectionId) {
this.sectionId = sectionId;
}
public void setProjectSectionCell(Project projectSectionCell) {
this.projectSectionCell = projectSectionCell;
}
}

View File

@ -28,6 +28,8 @@ public class User {
public User() {}
// TODO: this should be removed as we shouldn't be able to chose the ID. Leaving it for
// compatibility purposes, as soon as it's not used anymore, delete it
public User(
Long idUser,
String userSurname,
@ -43,6 +45,19 @@ public class User {
this.phoneNumber = phoneNumber;
}
public User(
String userSurname,
String userName,
String primaryMail,
String secondaryMail,
String phoneNumber) {
this.userSurname = userSurname;
this.userName = userName;
this.primaryMail = primaryMail;
this.secondaryMail = secondaryMail;
this.phoneNumber = phoneNumber;
}
public Long getIdUser() {
return idUser;
}
@ -71,8 +86,8 @@ public class User {
return primaryMail;
}
public void setPrimaryMail(String mainMail) {
this.primaryMail = mainMail;
public void setPrimaryMail(String primaryMail) {
this.primaryMail = primaryMail;
}
public String getSecondaryMail() {

View File

@ -5,10 +5,13 @@ import enseirb.myinpulse.model.Administrator;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import java.util.Optional;
@RepositoryRestResource
public interface AdministratorRepository extends JpaRepository<Administrator, Long> {
/* @Query("SELECT a from Administrators a")
Administrator findAllAdministrator(); */
Optional<Administrator> findByPrimaryMail(String PrimaryMail);
}

View File

@ -2,13 +2,18 @@ package enseirb.myinpulse.repository;
import enseirb.myinpulse.model.Administrator;
import enseirb.myinpulse.model.Project;
import enseirb.myinpulse.model.ProjectDecisionValue;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import java.util.Optional;
@RepositoryRestResource
public interface ProjectRepository extends JpaRepository<Project, Long> {
Iterable<Project> findByProjectAdministrator(Administrator administrator);
Iterable<Project> findByProjectStatus(String status);
Iterable<Project> findByProjectStatus(ProjectDecisionValue status);
Optional<Project> findByProjectName(String projectName);
}

View File

@ -1,68 +1,162 @@
package enseirb.myinpulse.service;
import enseirb.myinpulse.model.*;
import enseirb.myinpulse.service.database.AdministratorService;
import enseirb.myinpulse.service.database.ProjectService;
import enseirb.myinpulse.service.database.UserService;
import static enseirb.myinpulse.model.ProjectDecisionValue.ACTIVE;
import static enseirb.myinpulse.model.ProjectDecisionValue.REJECTED;
import enseirb.myinpulse.model.*;
import enseirb.myinpulse.service.database.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import java.util.ArrayList;
import java.util.List;
@Service
public class AdminApiService {
protected static final Logger logger = LogManager.getLogger();
private final ProjectService projectService;
private final UserService userService;
private final AdministratorService administratorService;
private final UtilsService utilsService;
private final AppointmentService appointmentService;
private final ReportService reportService;
private final SectionCellService sectionCellService;
@Autowired
AdminApiService(
ProjectService projectService,
UserService userService,
AdministratorService administratorService) {
AdministratorService administratorService,
UtilsService utilsService,
AppointmentService appointmentService,
ReportService reportService,
SectionCellService sectionCellService) {
this.projectService = projectService;
this.userService = userService;
this.administratorService = administratorService;
this.utilsService = utilsService;
this.appointmentService = appointmentService;
this.reportService = reportService;
this.sectionCellService = sectionCellService;
}
// TODO: test
public Iterable<Project> getProjectsOfAdmin(String email) {
// TODO: check if tests are sufficient - peer verification required
public Iterable<Project> getProjectsOfAdmin(String mail) {
return projectService.getProjectsByAdminId(
administratorService.getAdministratorById(
this.userService.getUserByEmail(email).getIdUser()));
this.userService.getUserByEmail(mail).getIdUser()));
}
// TODO
public Iterable<Appointment> getUpcomingAppointments(String email) {
throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED, "Not implemented yet");
public Iterable<Appointment> getUpcomingAppointments(String mail) {
logger.info("User {} check their upcoming appointments", mail);
User user = this.userService.getUserByEmail(mail);
List<Appointment> appointments = new ArrayList<>();
if (user instanceof Administrator) {
List<Project> projects = new ArrayList<>(((Administrator) user).getListProject());
projects.forEach(
project -> {
project.getListSectionCell()
.forEach(
sectionCell -> {
appointments.addAll(
this.sectionCellService
.getAppointmentsBySectionCellId(
sectionCell
.getIdSectionCell()));
});
});
}
if (user instanceof Entrepreneur) {
Project project = ((Entrepreneur) user).getProjectParticipation();
project.getListSectionCell()
.forEach(
sectionCell -> {
appointments.addAll(
this.sectionCellService.getAppointmentsBySectionCellId(
sectionCell.getIdSectionCell()));
});
}
return appointments;
}
// TODO: test
// TODO: check if tests are sufficient - peer verification required
public Iterable<Project> getPendingProjects() {
return this.projectService.getPendingProjects();
}
// TODO: test
// TODO: check if tests are sufficient - peer verification required
public void validateProject(ProjectDecision decision) {
projectService.updateProject(
decision.projectId,
null,
null,
null,
"ACTIVE",
this.administratorService.getAdministratorById(decision.projectId));
(decision.isAccepted == 1) ? ACTIVE : REJECTED,
this.administratorService.getAdministratorById(decision.adminId));
}
// TODO: solve todo + test
// TODO: check if tests are sufficient - peer verification required
public void addNewProject(Project project) {
projectService.addNewProject(project); // TODO: how can the front know the ID ?
project.setIdProject(null);
// We remove the ID from the request to be sure that it will be auto generated
try {
this.projectService.getProjectByName(project.getProjectName(), true);
throw new ResponseStatusException(HttpStatus.CONFLICT, "Project already exists");
} catch (ResponseStatusException e) {
if (e.getStatusCode() == HttpStatus.CONFLICT) {
throw new ResponseStatusException(HttpStatus.CONFLICT, "Project already exists");
}
}
Project newProject = projectService.addNewProject(project);
if (project.getProjectAdministrator() != null) {
newProject.getProjectAdministrator().updateListProject(newProject);
}
if (newProject.getEntrepreneurProposed() != null) {
Entrepreneur proposed = newProject.getEntrepreneurProposed();
proposed.setProjectProposed(newProject);
proposed.setProjectParticipation(newProject);
}
newProject
.getListEntrepreneurParticipation()
.forEach(
participation -> {
participation.setProjectParticipation(newProject);
});
newProject
.getListSectionCell()
.forEach(
sectionCell -> {
sectionCell.setProjectSectionCell(newProject);
});
}
// TODO
public void createAppointmentReport(String appointmentId, Report report, String email) {
throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED, "Not implemented yet");
public void createAppointmentReport(long appointmentId, Report report, String mail) {
long projectId =
this.appointmentService
.getAppointmentById(appointmentId)
.getAppointmentListSectionCell()
.getFirst()
.getProjectSectionCell()
.getIdProject();
if (!utilsService.isAllowedToCheckProject(mail, projectId)) {
logger.warn(
"User {} tried to add an report for appointment {} but is not allowed to.",
mail,
projectId);
throw new ResponseStatusException(
HttpStatus.UNAUTHORIZED, "You're not allowed to check this project");
}
logger.info("User {} added a report for appointment {}", mail, projectId);
Report addedReport = this.reportService.addNewReport(report);
addedReport.setAppointmentReport(this.appointmentService.getAppointmentById(appointmentId));
this.appointmentService.getAppointmentById(appointmentId).setAppointmentReport(addedReport);
}
// TODO: test

View File

@ -1,5 +1,7 @@
package enseirb.myinpulse.service;
import static enseirb.myinpulse.model.ProjectDecisionValue.PENDING;
import enseirb.myinpulse.model.Project;
import enseirb.myinpulse.model.SectionCell;
import enseirb.myinpulse.service.database.ProjectService;
@ -105,7 +107,20 @@ public class EntrepreneurApiService {
mail,
sectionCell.getIdSectionCell(),
this.sectionCellService.getProjectId(sectionCell.getIdSectionCell()));
sectionCellService.addNewSectionCell(sectionCell);
SectionCell newSectionCell = sectionCellService.addNewSectionCell(sectionCell);
newSectionCell.getProjectSectionCell().updateListSectionCell(newSectionCell);
newSectionCell
.getAppointmentSectionCell()
.forEach(
appointment -> {
appointment.updateListSectionCell(newSectionCell);
});
newSectionCell
.getListAnnotation()
.forEach(
annotation -> {
annotation.setSectionCellAnnotation(newSectionCell);
});
}
public void requestNewProject(Project project, String mail) {
@ -114,7 +129,7 @@ public class EntrepreneurApiService {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Le projet fourni est vide");
}
logger.info("User {} created a new project with id {}", mail, project.getIdProject());
project.setProjectStatus("PENDING");
project.setProjectStatus(PENDING);
projectService.addNewProject(project);
}
}

View File

@ -1,5 +1,8 @@
package enseirb.myinpulse.service;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.PdfWriter;
import enseirb.myinpulse.model.*;
import enseirb.myinpulse.service.database.*;
@ -10,6 +13,14 @@ import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
@ -87,10 +98,9 @@ public class SharedApiService {
HttpStatus.UNAUTHORIZED, "You're not allowed to check this project");
}
Project project = this.projectService.getProjectById(projectId);
return project.getAdministrator();
return project.getProjectAdministrator();
}
// TODO
public Iterable<Appointment> getAppointmentsByProjectId(long projectId, String mail) {
if (!utilsService.isAllowedToCheckProject(mail, projectId)) {
logger.warn(
@ -118,8 +128,8 @@ public class SharedApiService {
return appointments;
}
// TODO
public void getPDFReport(long appointmentId, String mail) {
public void getPDFReport(long appointmentId, String mail)
throws DocumentException, URISyntaxException, IOException {
long projectId =
this.appointmentService
.getAppointmentById(appointmentId)
@ -139,15 +149,104 @@ public class SharedApiService {
throw new ResponseStatusException(
HttpStatus.UNAUTHORIZED, "You're not allowed to check this project");
}
/* return this.appointmentService
logger.info(
"User {} generated the PDF report related to appointment {}", mail, appointmentId);
String reportContent =
this.appointmentService
.getAppointmentById(appointmentId)
.getAppointmentReport().getReportContent(); */
// generate pdf from this string, and format it to be decent looking
throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED, "Not implemented yet");
.getAppointmentReport()
.getReportContent();
// PDF generation
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream("Report" + appointmentId + ".pdf"));
document.open();
Paragraph title =
new Paragraph(
new Phrase(
"Compte Rendu - Réunion du "
+ this.appointmentService
.getAppointmentById(appointmentId)
.getAppointmentDate()
.toString(),
FontFactory.getFont(
FontFactory.HELVETICA,
20,
Font.BOLDITALIC,
BaseColor.BLACK)));
title.setAlignment(Element.ALIGN_CENTER);
document.add(title);
Font subsection =
FontFactory.getFont(FontFactory.HELVETICA, 14, Font.UNDERLINE, BaseColor.DARK_GRAY);
Font body = FontFactory.getFont(FontFactory.COURIER, 12, BaseColor.BLACK);
String[] split = reportContent.split(" ");
String tmp = "";
int counter = 1;
for (String s : split) {
if (s.equals("//")) {
Chunk chunk = new Chunk(tmp, body);
document.add(chunk);
document.add(new Paragraph("\n"));
tmp = "";
Paragraph paragraph = new Paragraph("Point n°" + counter + " : ", subsection);
document.add(paragraph);
document.add(new Paragraph("\n"));
counter++;
} else {
tmp = tmp.concat(s + " ");
}
}
Chunk chunk = new Chunk(tmp, body);
document.add(chunk);
document.add(new Paragraph("\n"));
document.close();
// Replace uri with website address
Files.copy(
new URI(
"http://localhost:8080/shared/projects/appointments/report/"
+ appointmentId)
.toURL()
.openStream(),
Paths.get("Report" + appointmentId + ".pdf"),
StandardCopyOption.REPLACE_EXISTING);
// delete file, we don't want to stock all reports on the server
File file = new File("Report" + appointmentId + ".pdf");
if (!file.delete()) {
logger.warn("Failed to delete report {}", file.getAbsolutePath());
}
}
// TODO
public void createAppointmentRequest(Appointment appointment, String mail) {
throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED, "Not implemented yet");
long projectId =
appointment
.getAppointmentListSectionCell()
.getFirst()
.getProjectSectionCell()
.getIdProject();
if (!utilsService.isAllowedToCheckProject(mail, projectId)) {
logger.warn(
"User {} tried to create for the project {} but is not allowed to.",
mail,
projectId);
throw new ResponseStatusException(
HttpStatus.UNAUTHORIZED, "You're not allowed to check this project");
}
logger.info("User {} tried to create an appointment for project {}", mail, projectId);
Appointment newAppointment = this.appointmentService.addNewAppointment(appointment);
newAppointment
.getAppointmentListSectionCell()
.forEach(
sectionCell -> {
sectionCell.updateAppointmentSectionCell(newAppointment);
});
newAppointment.getAppointmentReport().setAppointmentReport(newAppointment);
}
}

View File

@ -37,6 +37,17 @@ public class AdministratorService {
return administrator.get();
}
public Administrator getAdministratorByPrimaryMain(String primaryMail) {
Optional<Administrator> administrator =
this.administratorRepository.findByPrimaryMail(primaryMail);
if (administrator.isEmpty()) {
logger.error("No administrator found with the mail {}", primaryMail);
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "Cet administrateur n'existe pas");
}
return administrator.get();
}
public Administrator addAdministrator(Administrator administrator) {
return this.administratorRepository.save(administrator);
}

View File

@ -1,7 +1,10 @@
package enseirb.myinpulse.service.database;
import static enseirb.myinpulse.model.ProjectDecisionValue.PENDING;
import enseirb.myinpulse.model.Administrator;
import enseirb.myinpulse.model.Project;
import enseirb.myinpulse.model.ProjectDecisionValue;
import enseirb.myinpulse.repository.ProjectRepository;
import org.apache.logging.log4j.LogManager;
@ -54,7 +57,7 @@ public class ProjectService {
String projectName,
byte[] logo,
LocalDate creationDate,
String projectStatus,
ProjectDecisionValue projectStatus,
Administrator administrator) {
Optional<Project> project = this.projectRepository.findById(id);
@ -76,16 +79,19 @@ public class ProjectService {
}
if (projectStatus != null) {
// TODO: check if this is really useful
/*
if (!validateStatus(projectStatus)) {
logger.error("updateProjectStatus: Invalid status {}", projectStatus);
throw new ResponseStatusException(
HttpStatus.NOT_ACCEPTABLE, "Ce status n'est pas accepté");
}
*/
project.get().setProjectStatus(projectStatus);
}
if (administrator != null) {
project.get().setAdministrator(administrator);
project.get().setProjectAdministrator(administrator);
}
return this.projectRepository.save(project.get());
@ -96,10 +102,23 @@ public class ProjectService {
}
public Iterable<Project> getPendingProjects() {
return this.projectRepository.findByProjectStatus("PENDING");
return this.projectRepository.findByProjectStatus(PENDING);
}
public void deleteProjectById(Long id) {
this.projectRepository.deleteById(id);
}
public Project getProjectByName(String name, boolean noerror) {
Optional<Project> project = this.projectRepository.findByProjectName(name);
if (project.isEmpty()) {
if (noerror) logger.error("No project found with name {}", name);
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ce projet n'existe pas");
}
return project.get();
}
public Project getProjectByName(String name) {
return getProjectByName(name, false);
}
}

View File

@ -53,7 +53,7 @@ public class UserService {
@PathVariable Long id,
String userSurname,
String userName,
String mainMail,
String primaryMail,
String secondaryMail,
String phoneNumber) {
Optional<User> user = userRepository.findById(id);
@ -67,8 +67,8 @@ public class UserService {
if (userSurname != null) {
user.get().setUserSurname(userSurname);
}
if (mainMail != null) {
user.get().setPrimaryMail(mainMail);
if (primaryMail != null) {
user.get().setPrimaryMail(primaryMail);
}
if (secondaryMail != null) {
user.get().setSecondaryMail(secondaryMail);

View File

@ -1,12 +1,17 @@
package enseirb.myinpulse;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static enseirb.myinpulse.model.ProjectDecisionValue.*;
import static org.junit.jupiter.api.Assertions.*;
import enseirb.myinpulse.model.Administrator;
import enseirb.myinpulse.model.Entrepreneur;
import enseirb.myinpulse.model.Project;
import enseirb.myinpulse.model.ProjectDecision;
import enseirb.myinpulse.service.AdminApiService;
import enseirb.myinpulse.service.database.AdministratorService;
import enseirb.myinpulse.service.database.EntrepreneurService;
import enseirb.myinpulse.service.database.ProjectService;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@ -15,31 +20,76 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
@Transactional
public class AdminApiServiceTest {
private static long administratorid;
private static Administrator administrator;
private static Entrepreneur entrepreneur;
@Autowired private AdminApiService adminApiService;
@Autowired private ProjectService projectService;
@BeforeAll
static void setup(@Autowired AdministratorService administratorService) {
static void setup(
@Autowired AdministratorService administratorService,
@Autowired ProjectService projectService,
@Autowired EntrepreneurService entrepreneurService) {
administratorService.addAdministrator(
new Administrator(
"admin", "admin", "testAdmin@example.com", "testAdmin@example.com", ""));
"admin",
"admin",
"testAdminEmpty@example.com",
"testAdmin@example.com",
""));
administrator =
administratorService.addAdministrator(
new Administrator(
"admin2",
"admin2",
"testAdminFull@example.com",
"testAdmin@example.com",
""));
administratorid = administrator.getIdUser();
entrepreneur =
new Entrepreneur(
"JeSuisUnEntrepreneurDeCompet",
"EtUé",
"Entrepreneur@inpulse.com",
"mail2",
"phone",
"Ensimag nan jdeconne ENSEIRB (-matmeca mais on s'en fout)",
"info ofc",
false);
entrepreneurService.addEntrepreneur(entrepreneur);
projectService.addNewProject(
new Project(
"sampleProjectAdminApiService",
null,
LocalDate.now(),
ACTIVE,
administratorService.getAdministratorByPrimaryMain(
"testAdminFull@example.com")));
}
private <T> List<T> IterableToList(Iterable<T> iterable) {
List<T> l = new ArrayList<>();
iterable.forEach(l::add);
return l;
}
@Test
void getProjectOfAdminIsEmpty() throws Exception {
Iterable<Project> projects = adminApiService.getProjectsOfAdmin("testAdmin@example.com");
List<Project> l = new ArrayList<>();
projects.forEach(l::add);
assertEquals(0, l.size());
void getProjectOfAdminIsEmpty() {
Iterable<Project> projects =
adminApiService.getProjectsOfAdmin("testAdminEmpty@example.com");
assertEquals(0, IterableToList(projects).size());
}
@Test
void getProjectOfInexistantAdminFails() throws Exception {
void getProjectOfInexistantAdminFails() {
String nonExistentAdminEmail = "testInexistantAdmin@example.com";
assertThrows(
@ -48,4 +98,127 @@ public class AdminApiServiceTest {
adminApiService.getProjectsOfAdmin(nonExistentAdminEmail);
});
}
@Test
void getProjectOfAdminNotEmpty() {
Iterable<Project> projects =
adminApiService.getProjectsOfAdmin("testAdminFull@example.com");
List<Project> l = IterableToList(projects);
assertEquals(1, l.size());
Project p = l.getFirst();
assertEquals(p.getProjectName(), "sampleProjectAdminApiService");
}
@Test
void getPendingProjectsEmpty() {
assertEquals(0, IterableToList(this.adminApiService.getPendingProjects()).size());
}
@Test
void getPendingProjectsNotEmpty() {
this.projectService.addNewProject(
new Project(
"PendingProjectAdminApiService1", null, LocalDate.now(), PENDING, null));
this.projectService.addNewProject(
new Project(
"PendingProjectAdminApiService2", null, LocalDate.now(), PENDING, null));
Iterable<Project> pendingProjects = this.adminApiService.getPendingProjects();
List<Project> pendingProjectsList = IterableToList(pendingProjects);
assertEquals(2, pendingProjectsList.size());
assertTrue(
List.of("PendingProjectAdminApiService1", "PendingProjectAdminApiService2")
.contains(pendingProjectsList.getFirst().getProjectName()));
assertTrue(
List.of("PendingProjectAdminApiService1", "PendingProjectAdminApiService2")
.contains(pendingProjectsList.getLast().getProjectName()));
}
@Test
void validateInexistantProject() {
ProjectDecision d = new ProjectDecision(-1, 0, 1);
assertThrows(ResponseStatusException.class, () -> this.adminApiService.validateProject(d));
}
@Test
void validateExistantProject() {
Project p =
new Project("PendingProjectAdminApiService2", null, LocalDate.now(), PENDING, null);
this.projectService.addNewProject(p);
assertEquals(PENDING, p.getProjectStatus());
ProjectDecision d = new ProjectDecision(p.getIdProject(), administratorid, 1);
this.adminApiService.validateProject(d);
assertEquals(ACTIVE, p.getProjectStatus());
// Check if the project was really updated in the database
assertEquals(0, IterableToList(this.adminApiService.getPendingProjects()).size());
}
@Test
void refuseExistantProject() {
Project p =
new Project("PendingProjectAdminApiService2", null, LocalDate.now(), PENDING, null);
this.projectService.addNewProject(p);
assertEquals(PENDING, p.getProjectStatus());
ProjectDecision d = new ProjectDecision(p.getIdProject(), administratorid, 0);
this.adminApiService.validateProject(d);
assertEquals(REJECTED, p.getProjectStatus());
// Check if the project was really updated in the database
assertEquals(0, IterableToList(this.adminApiService.getPendingProjects()).size());
}
@Test
void addProject() {
assertEquals(0, IterableToList(this.adminApiService.getPendingProjects()).size());
Project p1 =
new Project("PendingProjectAdminApiService2", null, LocalDate.now(), PENDING, null);
this.adminApiService.addNewProject(p1);
assertEquals(1, IterableToList(this.adminApiService.getPendingProjects()).size());
}
@Test
void addProjectToAdmin() {
assertEquals(0, administrator.getListProject().size());
Project p1 = new Project("assProjectToAdmin", null, LocalDate.now(), ACTIVE, administrator);
this.adminApiService.addNewProject(p1);
assertEquals(1, administrator.getListProject().size());
}
@Test
void addProjectToUser() {
assertNull(entrepreneur.getProjectParticipation());
Project p1 =
new Project("assProjectToAdmin", null, LocalDate.now(), ACTIVE, null, entrepreneur);
this.adminApiService.addNewProject(p1);
assertEquals(p1, entrepreneur.getProjectParticipation());
}
@Test
void addProjectWithManyUsers() {
Entrepreneur e1 = new Entrepreneur();
Entrepreneur e2 = new Entrepreneur();
Entrepreneur e3 = new Entrepreneur();
assertNull(e1.getProjectParticipation());
assertNull(e2.getProjectParticipation());
assertNull(e3.getProjectParticipation());
Project p1 = new Project("assProjectToAdmin", null, LocalDate.now(), ACTIVE, null, null);
p1.updateListEntrepreneurParticipation(e1);
p1.updateListEntrepreneurParticipation(e2);
p1.updateListEntrepreneurParticipation(e3);
this.adminApiService.addNewProject(p1);
assertEquals(p1, e1.getProjectParticipation());
assertEquals(p1, e2.getProjectParticipation());
assertEquals(p1, e3.getProjectParticipation());
}
@Test
void addDuplicateProject() {
Project p1 =
new Project("PendingProjectAdminApiService2", null, LocalDate.now(), PENDING, null);
Project p2 =
new Project("PendingProjectAdminApiService2", null, LocalDate.now(), PENDING, null);
this.adminApiService.addNewProject(p1);
assertThrows(ResponseStatusException.class, () -> this.adminApiService.addNewProject(p2));
}
}

22
config/.env.dev Normal file
View File

@ -0,0 +1,22 @@
POSTGRES_DB=postgres_db
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres_db_user_password
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin
KEYCLOAK_HOSTNAME=localhost
KEYCLOAK_DB=keycloak_db
KEYCLOAK_USER=keycloak_db_user
KEYCLOAK_PASSWORD=keycloak_db_user_password
BACKEND_DB=backend_db
BACKEND_USER=backend_db_user
BACKEND_PASSWORD=backend_db_user_password
DATABASE_URL=localhost:5433
VITE_KEYCLOAK_URL=http://localhost:7080
VITE_KEYCLOAK_CLIENT_ID=myinpulse-dev
VITE_KEYCLOAK_REALM=test
VITE_APP_URL=http://localhost:5173
VITE_BACKEND_URL=http://localhost:8081/

12
documentation/Doc.txt Normal file
View File

@ -0,0 +1,12 @@
Format des comptes rendus de réunion :
Texte organisé par bullet point, chaque bullet point est séparé par "//" pour pouvoir être correctement généré.
Exemple :
Le texte "// blablabla // oui bonjour"
donne le résultat
Point n°1 :
blablabla
Point n°2 :
oui bonjour