init : scripts BD + entités

This commit is contained in:
jgetu
2025-10-23 18:31:42 +02:00
parent f1c21830ea
commit 45b1da6085
8 changed files with 817 additions and 1 deletions

2
.env
View File

@@ -32,7 +32,7 @@ DEFAULT_URI=http://localhost
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_%kernel.environment%.db" # DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_%kernel.environment%.db"
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4" # DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4"
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" # DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8" # DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
###< doctrine/doctrine-bundle ### ###< doctrine/doctrine-bundle ###
###> symfony/messenger ### ###> symfony/messenger ###

View File

@@ -0,0 +1,3 @@
create user 'appcontrib'@'%' identified by 'abc123';
grant all on contribV2.* to 'appcontrib'@'%';
flush privileges;

View File

@@ -0,0 +1,40 @@
create table membre(
id int auto_increment primary key,
nom varchar(50) not null,
prenom varchar(50) not null,
email varchar(100) not null unique
);
create table projet(
id int auto_increment primary key,
nom varchar(50) not null,
commentaire text,
date_lancement date,
date_cloture date,
statut varchar(20) not null
);
create table contribution(
id int auto_increment primary key,
membre_id int not null references membre(id),
projet_id int not null references projet(id),
date_contribution date not null,
commentaire text,
duree int default 0
);
create table assistant_ia(
id int auto_increment primary key,
nom varchar(50) not null
);
create table contrib_ia(
id int auto_increment primary key,
assistant_ia_id int not null references assistant_ia(id),
contribution_id int not null references contribution(id),
evaluation_pertinence int check (evaluation_pertinence >= 1 and evaluation_pertinence <= 5),
evaluation_temps int check (evaluation_temps >= 1 and evaluation_temps <= 5),
commentaire text
);

128
src/Entity/AssistantIa.php Normal file
View File

@@ -0,0 +1,128 @@
<?php
namespace App\Entity;
use App\Repository\AssistantIaRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: AssistantIaRepository::class)]
#[ORM\Table(name: 'assistant_ia')]
class AssistantIa
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private ?int $id = null;
#[ORM\Column(type: 'string', length: 50)]
#[Assert\NotBlank(message: 'Le nom de l\'assistant IA est obligatoire.')]
#[Assert\Length(max: 50, maxMessage: 'Le nom ne peut pas dépasser {{ limit }} caractères.')]
private ?string $nom = null;
#[ORM\OneToMany(targetEntity: ContribIa::class, mappedBy: 'assistantIa', cascade: ['persist'], orphanRemoval: true)]
private Collection $contribIas;
public function __construct()
{
$this->contribIas = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getNom(): ?string
{
return $this->nom;
}
public function setNom(string $nom): static
{
$this->nom = $nom;
return $this;
}
/**
* @return Collection<int, ContribIa>
*/
public function getContribIas(): Collection
{
return $this->contribIas;
}
public function addContribIa(ContribIa $contribIa): static
{
if (!$this->contribIas->contains($contribIa)) {
$this->contribIas->add($contribIa);
$contribIa->setAssistantIa($this);
}
return $this;
}
public function removeContribIa(ContribIa $contribIa): static
{
if ($this->contribIas->removeElement($contribIa)) {
// set the owning side to null (unless already changed)
if ($contribIa->getAssistantIa() === $this) {
$contribIa->setAssistantIa(null);
}
}
return $this;
}
/**
* Calcule la moyenne des évaluations de pertinence
*/
public function getMoyennePertinence(): ?float
{
$total = 0;
$count = 0;
foreach ($this->contribIas as $contribIa) {
if ($contribIa->getEvaluationPertinence() !== null) {
$total += $contribIa->getEvaluationPertinence();
$count++;
}
}
return $count > 0 ? round($total / $count, 2) : null;
}
/**
* Calcule la moyenne des évaluations de temps
*/
public function getMoyenneTemps(): ?float
{
$total = 0;
$count = 0;
foreach ($this->contribIas as $contribIa) {
if ($contribIa->getEvaluationTemps() !== null) {
$total += $contribIa->getEvaluationTemps();
$count++;
}
}
return $count > 0 ? round($total / $count, 2) : null;
}
/**
* Retourne le nombre total de contributions IA
*/
public function getNombreContributions(): int
{
return $this->contribIas->count();
}
public function __toString(): string
{
return $this->nom ?? '';
}
}

186
src/Entity/ContribIa.php Normal file
View File

@@ -0,0 +1,186 @@
<?php
namespace App\Entity;
use App\Repository\ContribIaRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: ContribIaRepository::class)]
#[ORM\Table(name: 'contrib_ia')]
#[ORM\CheckConstraints([
new ORM\CheckConstraint(
name: 'check_evaluation_pertinence',
expression: 'evaluation_pertinence >= 1 AND evaluation_pertinence <= 5'
),
new ORM\CheckConstraint(
name: 'check_evaluation_temps',
expression: 'evaluation_temps >= 1 AND evaluation_temps <= 5'
),
])]
class ContribIa
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private ?int $id = null;
#[ORM\ManyToOne(targetEntity: AssistantIa::class, inversedBy: 'contribIas')]
#[ORM\JoinColumn(nullable: false)]
#[Assert\NotNull(message: 'L\'assistant IA est obligatoire.')]
private ?AssistantIa $assistantIa = null;
#[ORM\ManyToOne(targetEntity: Contribution::class, inversedBy: 'contribIas')]
#[ORM\JoinColumn(nullable: false)]
#[Assert\NotNull(message: 'La contribution est obligatoire.')]
private ?Contribution $contribution = null;
#[ORM\Column(type: 'integer', nullable: true)]
#[Assert\Range(
min: 1,
max: 5,
notInRangeMessage: 'L\'évaluation de pertinence doit être comprise entre {{ min }} et {{ max }}.'
)]
private ?int $evaluationPertinence = null;
#[ORM\Column(type: 'integer', nullable: true)]
#[Assert\Range(
min: 1,
max: 5,
notInRangeMessage: 'L\'évaluation de temps doit être comprise entre {{ min }} et {{ max }}.'
)]
private ?int $evaluationTemps = null;
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $commentaire = null;
public function getId(): ?int
{
return $this->id;
}
public function getAssistantIa(): ?AssistantIa
{
return $this->assistantIa;
}
public function setAssistantIa(?AssistantIa $assistantIa): static
{
$this->assistantIa = $assistantIa;
return $this;
}
public function getContribution(): ?Contribution
{
return $this->contribution;
}
public function setContribution(?Contribution $contribution): static
{
$this->contribution = $contribution;
return $this;
}
public function getEvaluationPertinence(): ?int
{
return $this->evaluationPertinence;
}
public function setEvaluationPertinence(?int $evaluationPertinence): static
{
$this->evaluationPertinence = $evaluationPertinence;
return $this;
}
public function getEvaluationTemps(): ?int
{
return $this->evaluationTemps;
}
public function setEvaluationTemps(?int $evaluationTemps): static
{
$this->evaluationTemps = $evaluationTemps;
return $this;
}
public function getCommentaire(): ?string
{
return $this->commentaire;
}
public function setCommentaire(?string $commentaire): static
{
$this->commentaire = $commentaire;
return $this;
}
/**
* Retourne la moyenne des deux évaluations
*/
public function getMoyenneEvaluation(): ?float
{
if ($this->evaluationPertinence === null && $this->evaluationTemps === null) {
return null;
}
$count = 0;
$total = 0;
if ($this->evaluationPertinence !== null) {
$total += $this->evaluationPertinence;
$count++;
}
if ($this->evaluationTemps !== null) {
$total += $this->evaluationTemps;
$count++;
}
return round($total / $count, 2);
}
/**
* Retourne un libellé pour l'évaluation de pertinence
*/
public function getLibellePertinence(): string
{
return match ($this->evaluationPertinence) {
1 => 'Très faible',
2 => 'Faible',
3 => 'Moyen',
4 => 'Bon',
5 => 'Excellent',
default => 'Non évalué',
};
}
/**
* Retourne un libellé pour l'évaluation de temps
*/
public function getLibelleTemps(): string
{
return match ($this->evaluationTemps) {
1 => 'Très lent',
2 => 'Lent',
3 => 'Moyen',
4 => 'Rapide',
5 => 'Très rapide',
default => 'Non évalué',
};
}
public function __toString(): string
{
return sprintf(
'IA: %s - Contribution: %s',
$this->assistantIa ? $this->assistantIa->getNom() : '',
$this->contribution ? $this->contribution->__toString() : ''
);
}
}

171
src/Entity/Contribution.php Normal file
View File

@@ -0,0 +1,171 @@
<?php
namespace App\Entity;
use App\Repository\ContributionRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: ContributionRepository::class)]
#[ORM\Table(name: 'contribution')]
class Contribution
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private ?int $id = null;
#[ORM\ManyToOne(targetEntity: Membre::class, inversedBy: 'contributions')]
#[ORM\JoinColumn(nullable: false)]
#[Assert\NotNull(message: 'Le membre est obligatoire.')]
private ?Membre $membre = null;
#[ORM\ManyToOne(targetEntity: Projet::class, inversedBy: 'contributions')]
#[ORM\JoinColumn(nullable: false)]
#[Assert\NotNull(message: 'Le projet est obligatoire.')]
private ?Projet $projet = null;
#[ORM\Column(type: Types::DATE_MUTABLE)]
#[Assert\NotNull(message: 'La date de contribution est obligatoire.')]
private ?\DateTimeInterface $dateContribution = null;
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $commentaire = null;
#[ORM\Column(type: 'integer', options: ['default' => 0])]
#[Assert\PositiveOrZero(message: 'La durée doit être positive ou nulle.')]
private ?int $duree = 0;
#[ORM\OneToMany(targetEntity: ContribIa::class, mappedBy: 'contribution', cascade: ['persist', 'remove'], orphanRemoval: true)]
private Collection $contribIas;
public function __construct()
{
$this->contribIas = new ArrayCollection();
$this->dateContribution = new \DateTime();
$this->duree = 0;
}
public function getId(): ?int
{
return $this->id;
}
public function getMembre(): ?Membre
{
return $this->membre;
}
public function setMembre(?Membre $membre): static
{
$this->membre = $membre;
return $this;
}
public function getProjet(): ?Projet
{
return $this->projet;
}
public function setProjet(?Projet $projet): static
{
$this->projet = $projet;
return $this;
}
public function getDateContribution(): ?\DateTimeInterface
{
return $this->dateContribution;
}
public function setDateContribution(\DateTimeInterface $dateContribution): static
{
$this->dateContribution = $dateContribution;
return $this;
}
public function getCommentaire(): ?string
{
return $this->commentaire;
}
public function setCommentaire(?string $commentaire): static
{
$this->commentaire = $commentaire;
return $this;
}
public function getDuree(): ?int
{
return $this->duree;
}
public function setDuree(int $duree): static
{
$this->duree = $duree;
return $this;
}
/**
* @return Collection<int, ContribIa>
*/
public function getContribIas(): Collection
{
return $this->contribIas;
}
public function addContribIa(ContribIa $contribIa): static
{
if (!$this->contribIas->contains($contribIa)) {
$this->contribIas->add($contribIa);
$contribIa->setContribution($this);
}
return $this;
}
public function removeContribIa(ContribIa $contribIa): static
{
if ($this->contribIas->removeElement($contribIa)) {
// set the owning side to null (unless already changed)
if ($contribIa->getContribution() === $this) {
$contribIa->setContribution(null);
}
}
return $this;
}
/**
* Retourne la durée formatée en heures et minutes
*/
public function getDureeFormatee(): string
{
$heures = floor($this->duree / 60);
$minutes = $this->duree % 60;
if ($heures > 0) {
return sprintf('%dh%02d', $heures, $minutes);
}
return sprintf('%d min', $minutes);
}
public function __toString(): string
{
return sprintf(
'%s - %s (%s)',
$this->membre ? $this->membre->__toString() : '',
$this->projet ? $this->projet->getNom() : '',
$this->dateContribution ? $this->dateContribution->format('d/m/Y') : ''
);
}
}

121
src/Entity/Membre.php Normal file
View File

@@ -0,0 +1,121 @@
<?php
namespace App\Entity;
use App\Repository\MembreRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity(repositoryClass: MembreRepository::class)]
#[ORM\Table(name: 'membre')]
#[UniqueEntity(fields: ['email'], message: 'Cet email est déjà utilisé.')]
class Membre
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private ?int $id = null;
#[ORM\Column(type: 'string', length: 50)]
#[Assert\NotBlank(message: 'Le nom est obligatoire.')]
#[Assert\Length(max: 50, maxMessage: 'Le nom ne peut pas dépasser {{ limit }} caractères.')]
private ?string $nom = null;
#[ORM\Column(type: 'string', length: 50)]
#[Assert\NotBlank(message: 'Le prénom est obligatoire.')]
#[Assert\Length(max: 50, maxMessage: 'Le prénom ne peut pas dépasser {{ limit }} caractères.')]
private ?string $prenom = null;
#[ORM\Column(type: 'string', length: 100, unique: true)]
#[Assert\NotBlank(message: 'L\'email est obligatoire.')]
#[Assert\Email(message: 'L\'email {{ value }} n\'est pas valide.')]
#[Assert\Length(max: 100, maxMessage: 'L\'email ne peut pas dépasser {{ limit }} caractères.')]
private ?string $email = null;
#[ORM\OneToMany(targetEntity: Contribution::class, mappedBy: 'membre', cascade: ['persist'], orphanRemoval: true)]
private Collection $contributions;
public function __construct()
{
$this->contributions = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getNom(): ?string
{
return $this->nom;
}
public function setNom(string $nom): static
{
$this->nom = $nom;
return $this;
}
public function getPrenom(): ?string
{
return $this->prenom;
}
public function setPrenom(string $prenom): static
{
$this->prenom = $prenom;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): static
{
$this->email = $email;
return $this;
}
/**
* @return Collection<int, Contribution>
*/
public function getContributions(): Collection
{
return $this->contributions;
}
public function addContribution(Contribution $contribution): static
{
if (!$this->contributions->contains($contribution)) {
$this->contributions->add($contribution);
$contribution->setMembre($this);
}
return $this;
}
public function removeContribution(Contribution $contribution): static
{
if ($this->contributions->removeElement($contribution)) {
// set the owning side to null (unless already changed)
if ($contribution->getMembre() === $this) {
$contribution->setMembre(null);
}
}
return $this;
}
public function __toString(): string
{
return $this->prenom . ' ' . $this->nom;
}
}

167
src/Entity/Projet.php Normal file
View File

@@ -0,0 +1,167 @@
<?php
namespace App\Entity;
use App\Repository\ProjetRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: ProjetRepository::class)]
#[ORM\Table(name: 'projet')]
class Projet
{
public const STATUT_EN_COURS = 'en_cours';
public const STATUT_TERMINE = 'termine';
public const STATUT_ANNULE = 'annule';
public const STATUT_EN_ATTENTE = 'en_attente';
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private ?int $id = null;
#[ORM\Column(type: 'string', length: 50)]
#[Assert\NotBlank(message: 'Le nom du projet est obligatoire.')]
#[Assert\Length(max: 50, maxMessage: 'Le nom ne peut pas dépasser {{ limit }} caractères.')]
private ?string $nom = null;
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $commentaire = null;
#[ORM\Column(type: Types::DATE_MUTABLE, nullable: true)]
private ?\DateTimeInterface $dateLancement = null;
#[ORM\Column(type: Types::DATE_MUTABLE, nullable: true)]
#[Assert\GreaterThanOrEqual(propertyPath: 'dateLancement', message: 'La date de clôture doit être après la date de lancement.')]
private ?\DateTimeInterface $dateCloture = null;
#[ORM\Column(type: 'string', length: 20)]
#[Assert\NotBlank(message: 'Le statut est obligatoire.')]
#[Assert\Choice(
choices: [self::STATUT_EN_COURS, self::STATUT_TERMINE, self::STATUT_ANNULE, self::STATUT_EN_ATTENTE],
message: 'Le statut doit être valide.'
)]
private ?string $statut = null;
#[ORM\OneToMany(targetEntity: Contribution::class, mappedBy: 'projet', cascade: ['persist'], orphanRemoval: true)]
private Collection $contributions;
public function __construct()
{
$this->contributions = new ArrayCollection();
$this->statut = self::STATUT_EN_ATTENTE;
}
public function getId(): ?int
{
return $this->id;
}
public function getNom(): ?string
{
return $this->nom;
}
public function setNom(string $nom): static
{
$this->nom = $nom;
return $this;
}
public function getCommentaire(): ?string
{
return $this->commentaire;
}
public function setCommentaire(?string $commentaire): static
{
$this->commentaire = $commentaire;
return $this;
}
public function getDateLancement(): ?\DateTimeInterface
{
return $this->dateLancement;
}
public function setDateLancement(?\DateTimeInterface $dateLancement): static
{
$this->dateLancement = $dateLancement;
return $this;
}
public function getDateCloture(): ?\DateTimeInterface
{
return $this->dateCloture;
}
public function setDateCloture(?\DateTimeInterface $dateCloture): static
{
$this->dateCloture = $dateCloture;
return $this;
}
public function getStatut(): ?string
{
return $this->statut;
}
public function setStatut(string $statut): static
{
$this->statut = $statut;
return $this;
}
/**
* @return Collection<int, Contribution>
*/
public function getContributions(): Collection
{
return $this->contributions;
}
public function addContribution(Contribution $contribution): static
{
if (!$this->contributions->contains($contribution)) {
$this->contributions->add($contribution);
$contribution->setProjet($this);
}
return $this;
}
public function removeContribution(Contribution $contribution): static
{
if ($this->contributions->removeElement($contribution)) {
// set the owning side to null (unless already changed)
if ($contribution->getProjet() === $this) {
$contribution->setProjet(null);
}
}
return $this;
}
public function __toString(): string
{
return $this->nom ?? '';
}
public static function getStatutChoices(): array
{
return [
'En attente' => self::STATUT_EN_ATTENTE,
'En cours' => self::STATUT_EN_COURS,
'Terminé' => self::STATUT_TERMINE,
'Annulé' => self::STATUT_ANNULE,
];
}
}