Skip to content
Snippets Groups Projects
Commit 4996a7d7 authored by Pierre Fleutot's avatar Pierre Fleutot
Browse files

Ajout requêtes Edit Entry et Copy Entries

parent e5e00395
No related branches found
No related tags found
No related merge requests found
...@@ -12,6 +12,9 @@ nelmio_api_doc: ...@@ -12,6 +12,9 @@ nelmio_api_doc:
bearerFormat: JWT bearerFormat: JWT
security: security:
- OAuth2: [] # On répète le Nom arbitraire, et il faut l'utiliser dans les controllers @Security(name="OAuth2") - OAuth2: [] # On répète le Nom arbitraire, et il faut l'utiliser dans les controllers @Security(name="OAuth2")
# definitions:
# entry:
# $ref: ../JsonSchema/entrySchema.json
areas: # to filter documented areas areas: # to filter documented areas
path_patterns: path_patterns:
- ^/api(?!/doc$) # Accepts routes under /api except /api/doc - ^/api(?!/doc$) # Accepts routes under /api except /api/doc
......
...@@ -293,6 +293,33 @@ class ApiBaseController extends AbstractController ...@@ -293,6 +293,33 @@ class ApiBaseController extends AbstractController
return $entry; return $entry;
} }
/**
* - On ajoute les commentaires de l'entrée source à la suite de ceux de l'entrée cible
* - On ajoute les items de l'entrée source à la suite de ceux de l'entrée cible
* - On ajoute le label 'merged' au headword commun aux deux entrées
*
* @param Entry $targetEntry
* @param Entry $sourceEntry
* @return Entry
*/
public function mergeEntries(Entry $targetEntry, Entry $sourceEntry)
{
$targetEntry->setComments($targetEntry->getComments() . "\n\nCommentaires ajoutés suite à une fusion:\n" . $sourceEntry->getComments());
$sourceAttributes = $sourceEntry->getAttributes();
$targetAttributes = $targetEntry->getAttributes();
$targetAttributes['Items'] = array_merge_recursive($targetAttributes['Items'], $sourceAttributes['Items']);
$targetEntry->setAttributes($targetAttributes);
$mergedLabel = $this->doctrine->getRepository(Label::class)->findOneBy([
'category' => Label::LABEL_CATEGORY_SYSTEM,
'name' => Label::LABEL_MERGED,
]);
$targetEntry->getHeadword()->addLabel($mergedLabel);
return $targetEntry;
}
/** /**
* @param Headword $headword * @param Headword $headword
* @return Entry|null * @return Entry|null
......
...@@ -9,6 +9,7 @@ use App\Entity\Lexicon; ...@@ -9,6 +9,7 @@ use App\Entity\Lexicon;
use App\Entity\User; use App\Entity\User;
use App\Manager\WiktionaryManager; use App\Manager\WiktionaryManager;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
use JsonSchema\Validator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
...@@ -61,7 +62,7 @@ class ApiEntryController extends ApiBaseController ...@@ -61,7 +62,7 @@ class ApiEntryController extends ApiBaseController
/** /**
* Recherche d'une entrée par graphie et par langue, dans un ensemble de lexiques * Recherche d'une entrée par graphie et par langue, dans un ensemble de lexiques
* *
* @Route("/search", name="search", methods={"POST"}) * @Route("/search", name="api_search", methods={"POST"})
* *
* @OA\Response(response=200, description="success", * @OA\Response(response=200, description="success",
* @OA\JsonContent(type="array", * @OA\JsonContent(type="array",
...@@ -115,7 +116,7 @@ class ApiEntryController extends ApiBaseController ...@@ -115,7 +116,7 @@ class ApiEntryController extends ApiBaseController
/** /**
* Crée une entrée dans les lexiques spécifiés à partir d'un mot. Si force=true, on crée l'entrée même si le mot n'est pas trouvé dans le wiktionnaire * Crée une entrée dans les lexiques spécifiés à partir d'un mot. Si force=true, on crée l'entrée même si le mot n'est pas trouvé dans le wiktionnaire
* *
* @Route("/create", name="create_entry", methods={"POST"}) * @Route("/create", name="api_create_entry", methods={"POST"})
* *
* @OA\Response(response=200, description="success", @OA\JsonContent(type="string")) * @OA\Response(response=200, description="success", @OA\JsonContent(type="string"))
* @OA\Response(response=401, description="error", @OA\JsonContent(type="string")) * @OA\Response(response=401, description="error", @OA\JsonContent(type="string"))
...@@ -184,7 +185,7 @@ class ApiEntryController extends ApiBaseController ...@@ -184,7 +185,7 @@ class ApiEntryController extends ApiBaseController
} }
/** /**
* Copie une sélection d'entrée vers des lexiques cibles. Copie également les labels spécifiés à partir d'un mot. Si force=true, on crée l'entrée même si le mot n'est pas trouvé dans le wiktionnaire * Copie une sélection d'entrée vers des lexiques cibles. Si force=true, on crée l'entrée même si le mot n'est pas trouvé dans le wiktionnaire
* *
* @Route("/copy", name="api_copy_entries", methods={"POST"}) * @Route("/copy", name="api_copy_entries", methods={"POST"})
* *
...@@ -202,7 +203,7 @@ class ApiEntryController extends ApiBaseController ...@@ -202,7 +203,7 @@ class ApiEntryController extends ApiBaseController
* @OA\Items( * @OA\Items(
* type="integer" * type="integer"
* ) * )
* ) * ),
* @OA\Property(property="entries", type="array", * @OA\Property(property="entries", type="array",
* example={1, 2}, * example={1, 2},
* @OA\Items( * @OA\Items(
...@@ -233,7 +234,7 @@ class ApiEntryController extends ApiBaseController ...@@ -233,7 +234,7 @@ class ApiEntryController extends ApiBaseController
if (!in_array($data['merge'], Lexicon::MERGE_MODES)) { if (!in_array($data['merge'], Lexicon::MERGE_MODES)) {
return $this->createJsonResponse(401, ['error' => sprintf("Le mode de fusion « merge » doit être overwrite OU ignore OU merge")]); return $this->createJsonResponse(401, ['error' => sprintf("Le mode de fusion « merge » doit être overwrite OU ignore OU merge")]);
} }
$entries = $this->doctrine->getRepository(Entry::class)->find($data['entries']); $entries = $this->doctrine->getRepository(Entry::class)->findById($data['entries']);
if (!$entries) { if (!$entries) {
return $this->createJsonResponse(401, ['error' => sprintf("Aucune entrée trouvée")]); return $this->createJsonResponse(401, ['error' => sprintf("Aucune entrée trouvée")]);
} }
...@@ -241,32 +242,71 @@ class ApiEntryController extends ApiBaseController ...@@ -241,32 +242,71 @@ class ApiEntryController extends ApiBaseController
if (!$lexicons) { if (!$lexicons) {
return $this->createJsonResponse(401); return $this->createJsonResponse(401);
} }
$language = $this->getLexiconsLanguage($lexicons);
// On récupère ou on crée le mot-vedette if (!$language) {
if (!$headword) { return $this->createJsonResponse(401, ['error' => sprintf("Les lexiques cibles doivent tous appartenir à la même langue")]);
if ($forceCreation || $wiktionaryManager->search($data['graphy'], $language)) {
$headword = $this->newHeadword($data['graphy'], $language);
} else {
return $this->createJsonResponse(401, ['warning' => sprintf("Le mot «%s» n'existe ni dans Balex ni dans le wiktionnaire.", $data['graphy'])]);
}
} }
// On crée l'entrée dans chaque lexique si elle n'y existe pas (et dans le lexique Zéro si possible) $added = [];
$overwritten = [];
$ignored = [];
$merged = [];
// On crée l'entrée dans chaque lexique si elle n'y existe pas
foreach ($lexicons as $lexicon) { foreach ($lexicons as $lexicon) {
if (!$this->doctrine->getRepository(Entry::class)->findBy(['lexicon' => $lexicon, 'headword' => $headword])) {
$entry = $this->createEntryInLexicon($headword, $lexicon); foreach ($entries as $entry) {
$this->success[] = sprintf("Entrée créée dans le lexique : %s.", $lexicon);
} else { if ($language !== $entry->getLanguage()) {
$this->warning[] = sprintf("Cette entrée est déjà présente dans le lexique %s.", $lexicon); return $this->createJsonResponse(401, ['error' => sprintf("L'entrée %s n'est pas dans la même langue que les lexiques cibles", $entry->getId())]);
}
$targetEntry = $this->doctrine->getRepository(Entry::class)->findOneBy([
'headword' => $entry->getHeadword(),
'lexicon' => $lexicon,
]);
// Si l'entrée existe déjà
if ($targetEntry) {
switch ($data['merge']) {
case Lexicon::MERGE_OVERWRITE: // On remplace l'entrée cible si elle existe
$targetEntry->setAttributes($entry->getAttributes());
$targetEntry->setComments($entry->getComments());
$overwritten[] = $targetEntry->getId();
break;
case Lexicon::MERGE_IGNORE: // On ne fait rien si l'entrée cible existe
$ignored[] = $targetEntry->getId();
break;
case Lexicon::MERGE_MERGE: // On fusionne les 2 entrées
$targetEntry = $this->mergeEntries($targetEntry, $entry);
$merged[] = $targetEntry->getId();
break;
}
} else {
$newEntry = clone($entry);
$newEntry->setLexicon($lexicon);
$newEntry->setCreatedAt(new \DateTimeImmutable());
$newEntry->setUpdatedAt(null);
$this->doctrine->getManager()->persist($newEntry);
$this->doctrine->getManager()->flush();
$added[] = $newEntry->getId();
}
} }
} }
$this->doctrine->getManager()->flush(); $this->doctrine->getManager()->flush();
return $this->createJsonResponse(200); $result = array_filter([
'added' => $added,
'overwritten' => $overwritten,
'ignored' => $ignored,
'merged' => $merged,
]);
return $this->createJsonResponse(200, $result);
} }
/** /**
* @Route("/get/{id}", name="get_entry", methods={"GET"}) * @Route("/get/{id}", name="api_get_entry", methods={"GET"})
* *
* @OA\Response( * @OA\Response(
* response=200, * response=200,
...@@ -285,6 +325,7 @@ class ApiEntryController extends ApiBaseController ...@@ -285,6 +325,7 @@ class ApiEntryController extends ApiBaseController
* description="id of the entry to be returned", * description="id of the entry to be returned",
* @OA\Schema(type="string") * @OA\Schema(type="string")
* ) * )
*
* @OA\Tag(name="Entries") * @OA\Tag(name="Entries")
* @Security(name="OAuth2") * @Security(name="OAuth2")
*/ */
...@@ -300,7 +341,72 @@ class ApiEntryController extends ApiBaseController ...@@ -300,7 +341,72 @@ class ApiEntryController extends ApiBaseController
} }
/** /**
* @Route("/remove", name="entry_delete", methods={"DELETE"}) * @Route("/edit/{id}", name="api_edit_entry", methods={"POST"})
*
* @OA\Response(
* response=200,
* description="Success",
* @OA\JsonContent(
* ref=@Model(type=Entry::class, groups={"entry:read"})
* )
* )
* @OA\Response(response=401, description="error", @OA\JsonContent(type="string"))
* @OA\Response(response=403, description="error", @OA\JsonContent(type="string"))
* @OA\Response(response=500, description="error", @OA\JsonContent(type="string"))
*
* @OA\Parameter(
* name="id",
* in="path",
* description="id of the entry to edit",
* @OA\Schema(type="string")
* )
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* required={"attributes"},
* @OA\Property(property="comment", type="string", example="commentaire optionnel ajouté à la suite des commentaires existants"),
* @OA\Property(property="attributes", type="object")
* )
* )
* @OA\Tag(name="Entries")
* @Security(name="OAuth2")
*/
public function editEntry(Request $request, SerializerInterface $serializer, Entry $entry = null)
{
if ($entry === null) {
return $this->createJsonResponse(401, ['error' => sprintf("Pas d'entrée trouvée pour cette Id")]);
}
$data = json_decode($request->getContent(), true);
if (!$data) {
return $this->createJsonResponse(401, ['error' => sprintf("Json non valide")]);
}
if ($missingFields = $this->getMissingFields($data, ['attributes'])) {
return $this->createJsonResponse(401, ['error' => sprintf("Veuillez fournir une valeur pour: %s", implode(', ', $missingFields))]);
}
$attributes = json_decode(json_encode($data['attributes']));
$validator = new Validator();
$validator->validate($attributes, (object) ['$ref' => __DIR__ . "/../JsonSchema/entrySchema.json"]);
if (!$validator->isValid()) {
$message = "JSON does not validate. Violations:\n";
foreach ($validator->getErrors() as $error) {
$message .= sprintf("[%s] %s\n", $error['property'], $error['message']);
}
return $this->createJsonResponse(401, ['error' => $message]);
} else {
$entry->setAttributes($data['attributes']);
if ($data['comment'] ?? null) {
$entry->setComments($entry->getComments() . "\n" . $data['comment']);
}
$this->doctrine->getManager()->flush();
}
$updatedEntryData = $serializer->serialize($entry, 'json', ['groups' => ["entry:read", "headword:read", "lexicon:read"]]);
return new JsonResponse($updatedEntryData, 200, [], true);
}
/**
* @Route("/remove", name="api_entry_delete", methods={"DELETE"})
* *
* @OA\Response( * @OA\Response(
* response=200, * response=200,
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
namespace App\Controller; namespace App\Controller;
use App\Entity\Entry;
use App\Entity\Lexicon; use App\Entity\Lexicon;
use App\Repository\LexiconRepository; use App\Repository\LexiconRepository;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
...@@ -22,6 +23,12 @@ class LexiconController extends AbstractController ...@@ -22,6 +23,12 @@ class LexiconController extends AbstractController
*/ */
public function index(LexiconRepository $lexiconRepository): Response public function index(LexiconRepository $lexiconRepository): Response
{ {
// $entry1 = $this->getDoctrine()->getRepository(Entry::class)->find(1);
// $entry2 = $this->getDoctrine()->getRepository(Entry::class)->find(9);
//
//
// dump($entry1->getAttributes(), $entry2->getAttributes(), array_merge_recursive($entry1->getAttributes()['Items'], $entry2->getAttributes()['Items']));die();
return $this->render('lexicon/index.html.twig', [ return $this->render('lexicon/index.html.twig', [
'lexicons' => $lexiconRepository->findAll(), 'lexicons' => $lexiconRepository->findAll(),
]); ]);
......
...@@ -34,6 +34,7 @@ class Entry ...@@ -34,6 +34,7 @@ class Entry
private $attributes = []; private $attributes = [];
/** /**
* @Groups({"entry:read"})
* @ORM\Column(type="text", nullable=true) * @ORM\Column(type="text", nullable=true)
*/ */
private $comments; private $comments;
...@@ -68,12 +69,6 @@ class Entry ...@@ -68,12 +69,6 @@ class Entry
*/ */
private $lexicon; private $lexicon;
/**
* @Groups({"entry:write"})
* @var Lexicon[]
*/
private $lexicons;
public function __construct() public function __construct()
{ {
} }
...@@ -195,20 +190,4 @@ class Entry ...@@ -195,20 +190,4 @@ class Entry
return $this; return $this;
} }
/**
* @return Lexicon[]
*/
public function getLexicons(): array
{
return $this->lexicons;
}
/**
* @param Lexicon[] $lexicons
*/
public function setLexicons(array $lexicons): void
{
$this->lexicons = $lexicons;
}
} }
...@@ -59,7 +59,7 @@ class LexiconVoter extends Voter ...@@ -59,7 +59,7 @@ class LexiconVoter extends Voter
return true; return true;
} }
if (in_array($user, $lexicon->getGroup()->getMembers())) { if ($lexicon->getGroup() && in_array($user, $lexicon->getGroup()->getMembers())) {
return true; return true;
} }
...@@ -77,7 +77,7 @@ class LexiconVoter extends Voter ...@@ -77,7 +77,7 @@ class LexiconVoter extends Voter
return true; return true;
} }
if (in_array($user, $lexicon->getGroup()->getMembers())) { if ($lexicon->getGroup() && in_array($user, $lexicon->getGroup()->getMembers())) {
return true; return true;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment