From 40a3208a7a5e1075ef5fc5d1216800942f399dd3 Mon Sep 17 00:00:00 2001 From: Charles Perier <82757576+perierc@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:35:03 +0100 Subject: [PATCH] fix(backend): fix create_node function (#393) * fix(backend): fix create_node function * refactor to use models and controller * refactor further * resolve comments --- backend/editor/api.py | 28 +++++------- backend/editor/controllers/node_controller.py | 31 +++++++++++++ backend/editor/entries.py | 44 +++++-------------- backend/editor/models/node_models.py | 18 +++++--- .../components/CreateNodeDialogContent.tsx | 11 ++--- .../src/pages/root-nodes/index.tsx | 2 +- 6 files changed, 71 insertions(+), 63 deletions(-) diff --git a/backend/editor/api.py b/backend/editor/api.py index 2ec22a79..4ccca373 100644 --- a/backend/editor/api.py +++ b/backend/editor/api.py @@ -38,7 +38,7 @@ from .exceptions import GithubBranchExistsError, GithubUploadError # Data model imports -from .models.node_models import Footer, Header +from .models.node_models import EntryNodeCreate, Footer, Header, NodeType from .models.project_models import Project, ProjectEdit, ProjectStatus from .scheduler import scheduler_lifespan @@ -403,26 +403,18 @@ async def upload_taxonomy( return result -@app.post("/{taxonomy_name}/{branch}/nodes") -async def create_node(request: Request, branch: str, taxonomy_name: str): +@app.post("/{taxonomy_name}/{branch}/entry", status_code=status.HTTP_201_CREATED) +async def create_entry_node( + branch: str, taxonomy_name: str, new_entry_node: EntryNodeCreate +) -> None: """ - Creating a new node in a taxonomy + Creating a new entry node in a taxonomy """ taxonomy = TaxonomyGraph(branch, taxonomy_name) - incoming_data = await request.json() - id = incoming_data["id"] - main_language = incoming_data["main_language"] - if id is None: - raise HTTPException(status_code=400, detail="Invalid id") - if main_language is None: - raise HTTPException(status_code=400, detail="Invalid main language code") - - label = taxonomy.get_label(id) - normalized_id = await taxonomy.create_node(label, id, main_language) - if label == "ENTRY": - await taxonomy.add_node_to_end(label, normalized_id) - else: - await taxonomy.add_node_to_beginning(label, normalized_id) + normalized_id = await taxonomy.create_entry_node( + new_entry_node.name, new_entry_node.main_language_code + ) + await taxonomy.add_node_to_end(NodeType.ENTRY, normalized_id) @app.post("/{taxonomy_name}/{branch}/entry/{entry}") diff --git a/backend/editor/controllers/node_controller.py b/backend/editor/controllers/node_controller.py index b82cc3e9..29d871f6 100644 --- a/backend/editor/controllers/node_controller.py +++ b/backend/editor/controllers/node_controller.py @@ -1,4 +1,7 @@ +from openfoodfacts_taxonomy_parser import utils as parser_utils + from ..graph_db import get_current_transaction +from ..models.node_models import EntryNodeCreate async def delete_project_nodes(project_id: str): @@ -12,3 +15,31 @@ async def delete_project_nodes(project_id: str): DETACH DELETE n """ await get_current_transaction().run(query) + + +async def create_entry_node( + project_id: str, entry_node: EntryNodeCreate, stopwords: dict[str, list[str]] +) -> str: + """ + Creates a new entry node in the database + """ + name, language_code = entry_node.name, entry_node.main_language_code + normalized_name = parser_utils.normalize_text(name, language_code, stopwords=stopwords) + + # Create params dict + entry_node_data = { + "main_language": language_code, + "preceding_lines": [], + "id": language_code + ":" + normalized_name, + f"tags_{language_code}": [name], + f"tags_ids_{language_code}": [normalized_name], + } + params = {"entry_node": entry_node_data} + + query = f""" + CREATE (n:{project_id}:ENTRY $entry_node) + RETURN n.id + """ + + result = await get_current_transaction().run(query, params) + return (await result.data())[0]["n.id"] diff --git a/backend/editor/entries.py b/backend/editor/entries.py index 5c832e11..bec50097 100644 --- a/backend/editor/entries.py +++ b/backend/editor/entries.py @@ -16,6 +16,7 @@ from openfoodfacts_taxonomy_parser import utils as parser_utils from . import settings +from .controllers.node_controller import create_entry_node from .controllers.project_controller import create_project, edit_project, get_project from .exceptions import GithubBranchExistsError # Custom exceptions from .exceptions import ( @@ -30,6 +31,7 @@ TransactionCtx, get_current_transaction, ) +from .models.node_models import EntryNodeCreate from .models.project_models import ProjectCreate, ProjectEdit, ProjectStatus from .utils import file_cleanup @@ -59,40 +61,17 @@ def get_label(self, id): else: return "ENTRY" - async def create_node(self, label, entry, main_language_code): + async def create_entry_node(self, name, main_language_code) -> str: """ - Helper function used for creating a node with given id and label + Helper function used to create an entry node with given name and main language """ - params = {"id": entry} - query = [f"""CREATE (n:{self.project_name}:{label})\n"""] stopwords = await self.get_stopwords_dict() - # Build all basic keys of a node - if label == "ENTRY": - # Normalizing new canonical tag - language_code, canonical_tag = entry.split(":", 1) - normalised_canonical_tag = parser_utils.normalize_text( - canonical_tag, main_language_code, stopwords=stopwords - ) - - # Reconstructing and updation of node ID - params["id"] = language_code + ":" + normalised_canonical_tag - params["main_language_code"] = main_language_code - - query.append( - """ SET n.main_language = $main_language_code """ - ) # Required for only an entry - else: - canonical_tag = "" - - query.append(""" SET n.id = $id """) - query.append(f""" SET n.tags_{main_language_code} = [$canonical_tag] """) - query.append(""" SET n.preceding_lines = [] """) - query.append(""" RETURN n.id """) - - params["canonical_tag"] = canonical_tag - result = await get_current_transaction().run(" ".join(query), params) - return (await result.data())[0]["n.id"] + return await create_entry_node( + self.project_name, + EntryNodeCreate(name=name, main_language_code=main_language_code), + stopwords, + ) async def get_local_taxonomy_file(self, tmpdir: str, uploadfile: UploadFile): filename = uploadfile.filename @@ -345,6 +324,7 @@ async def add_node_to_end(self, label, entry): """ await get_current_transaction().run(query, {"id": entry, "endnodeid": end_node["id"]}) + # UNUSED FOR NOW async def add_node_to_beginning(self, label, entry): """ Helper function which adds an existing node to beginning of taxonomy @@ -609,8 +589,8 @@ async def update_node_children(self, entry, new_children_ids): created_child_ids = [] for child in to_create: - main_language_code = child.split(":", 1)[0] - created_node_id = await self.create_node("ENTRY", child, main_language_code) + main_language_code, child_name = child.split(":", 1) + created_node_id = await self.create_entry_node(child_name, main_language_code) created_child_ids.append(created_node_id) # TODO: We would prefer to add the node just after its parent entry diff --git a/backend/editor/models/node_models.py b/backend/editor/models/node_models.py index 80923990..b8b84e3f 100644 --- a/backend/editor/models/node_models.py +++ b/backend/editor/models/node_models.py @@ -1,18 +1,26 @@ """ Required pydantic models for API """ -from typing import List +from enum import Enum from .base_models import BaseModel -class Marginal(BaseModel): - preceding_lines: List +class NodeType(str, Enum): + TEXT = "TEXT" + SYNONYMS = "SYNONYMS" + STOPWORDS = "STOPWORDS" + ENTRY = "ENTRY" -class Header(Marginal): +class Header(BaseModel): pass -class Footer(Marginal): +class Footer(BaseModel): pass + + +class EntryNodeCreate(BaseModel): + name: str + main_language_code: str diff --git a/taxonomy-editor-frontend/src/components/CreateNodeDialogContent.tsx b/taxonomy-editor-frontend/src/components/CreateNodeDialogContent.tsx index 3a528138..5dcc0c18 100644 --- a/taxonomy-editor-frontend/src/components/CreateNodeDialogContent.tsx +++ b/taxonomy-editor-frontend/src/components/CreateNodeDialogContent.tsx @@ -30,15 +30,12 @@ const CreateNodeDialogContent = ({ const baseUrl = createBaseURL(taxonomyName, branchName); - const handleAddNode = () => { + const handleAddEntryNode = () => { setIsFailedSubmit(false); - // TODO: Add support for synonyms and stopwords + const data = { name: newNodeName, main_language_code: newLanguageCode }; - const newNodeID = newLanguageCode + ":" + newNodeName; // Reconstructing node ID - const data = { id: newNodeID, main_language: newLanguageCode }; - - fetch(`${baseUrl}nodes`, { + fetch(`${baseUrl}entry`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), @@ -95,7 +92,7 @@ const CreateNodeDialogContent = ({ diff --git a/taxonomy-editor-frontend/src/pages/root-nodes/index.tsx b/taxonomy-editor-frontend/src/pages/root-nodes/index.tsx index 2a250c67..6d02a3db 100644 --- a/taxonomy-editor-frontend/src/pages/root-nodes/index.tsx +++ b/taxonomy-editor-frontend/src/pages/root-nodes/index.tsx @@ -169,7 +169,7 @@ const RootNodes = ({ - Taxonony Name + Taxonomy Name Branch Name