From 66296eb9b5afbb6fe29fa374c6983bb765304bf0 Mon Sep 17 00:00:00 2001 From: EdblackGitHub Date: Wed, 4 Dec 2024 15:42:26 -0500 Subject: [PATCH 01/11] Create search.html Search.html --- app/uid/templates/search.html | 122 ++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 app/uid/templates/search.html diff --git a/app/uid/templates/search.html b/app/uid/templates/search.html new file mode 100644 index 0000000..fee79fe --- /dev/null +++ b/app/uid/templates/search.html @@ -0,0 +1,122 @@ + + + + + + Search + + + +
+

Search CCV/LCV Definitions

+ +
+ {% csrf_token %} +
+ + +
+ +
+ + +
+ +
+ +
+
+ + + {% if results_data %} +
+

Search Results

+ + + + + + + + + + + {% for record in results_data %} + + + + + + + {% endfor %} + +
UIDAliasDefinitionContext
{{ record.0 }} {{ record.1 }} {{ record.2 }} {{ record.3|default:"No context" }}
+
+ {% endif %} + + {% if error %} +

{{ error }}

+ {% endif %} +
+ + From b3eab71f85ea4f0e6bf32854ea7db326716e7313 Mon Sep 17 00:00:00 2001 From: EdblackGitHub Date: Wed, 4 Dec 2024 15:44:29 -0500 Subject: [PATCH 02/11] Update urls.py added search and create alias --- app/uid/urls.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/uid/urls.py b/app/uid/urls.py index 0e75247..2b3b688 100644 --- a/app/uid/urls.py +++ b/app/uid/urls.py @@ -16,6 +16,9 @@ path('api/log', report_generated_uids, name='uid-generated'), path('api/generate', api_generate_uid, name='uid-generated'), + + path('search/', views.search, name='search'), # For the search endpoint + path('create_alias/', views.create_alias, name='create_alias'), # path('api/uid-repo/', UIDRepoViewSet.as_view({'get': 'list'}), name='uid-repo'), # path('api/uid/all', UIDTermViewSet.as_view({'get': 'list'}), name='uid-all'), From 09f6c759fab2ea8a172a0053b9ec78ea6501838e Mon Sep 17 00:00:00 2001 From: EdblackGitHub Date: Wed, 4 Dec 2024 15:54:46 -0500 Subject: [PATCH 03/11] Update views.py With search and alias --- app/uid/views.py | 133 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/app/uid/views.py b/app/uid/views.py index 8e77965..7e56ae2 100644 --- a/app/uid/views.py +++ b/app/uid/views.py @@ -11,6 +11,65 @@ from rest_framework.response import Response from django.views.decorators.csrf import csrf_exempt +from django.contrib import messages +import os +from .forms import SearchForm +import requests +import urllib.parse +from .models import Alias +from .forms import AliasForm + +# Cypher Queries +SEARCH_BY_ALIAS = """ +WITH toLower($search_term) as search_term +MATCH (a:NeoAlias) +WHERE toLower(a.alias) CONTAINS search_term +MATCH (a)-[:POINTS_TO]->(term:NeoTerm) +OPTIONAL MATCH (term)-[:POINTS_TO]->(def:NeoDefinition) +OPTIONAL MATCH (ctx:NeoContext)-[:IS_A]->(term) +RETURN term.uid as LCVID, a.alias as Alias, def.definition as Definition, ctx.context as Context +LIMIT 100 +""" + +SEARCH_BY_DEFINITION = """ +WITH toLower($search_term) as search_term +MATCH (def:NeoDefinition) +WHERE toLower(def.definition) CONTAINS search_term +MATCH (term:NeoTerm)-[:POINTS_TO]->(def) +OPTIONAL MATCH (a:NeoAlias)-[:POINTS_TO]->(term) +OPTIONAL MATCH (ctx:NeoContext)-[:IS_A]->(term) +RETURN term.uid as LCVID, a.alias as Alias, def.definition as Definition, ctx.context as Context +LIMIT 100 +""" + +SEARCH_BY_CONTEXT = """ +WITH toLower($search_term) as search_term +MATCH (ctx:NeoContext) +WHERE toLower(ctx.context) CONTAINS search_term +MATCH (ctx)-[:IS_A]->(term:NeoTerm) +OPTIONAL MATCH (term)-[:POINTS_TO]->(def:NeoDefinition) +OPTIONAL MATCH (a:NeoAlias)-[:POINTS_TO]->(term) +RETURN term.uid as LCVID, a.alias as Alias, def.definition as Definition, ctx.context as Context +LIMIT 100 +""" + +GENERAL_GRAPH_SEARCH = """ +WITH toLower($search_term) as search_term +MATCH (n) +WHERE (n:NeoAlias OR n:NeoDefinition OR n:NeoContext) + AND ( + (n:NeoAlias AND toLower(n.alias) CONTAINS search_term) OR + (n:NeoDefinition AND toLower(n.definition) CONTAINS search_term) OR + (n:NeoContext AND toLower(n.context) CONTAINS search_term) + ) +WITH n +CALL { + WITH n + MATCH path = (n)-[*1..2]-(connected) + RETURN path +} +RETURN * LIMIT 100 +""" # Set up logging to capture errors and important information logger = logging.getLogger(__name__) @@ -82,6 +141,80 @@ def generate_uid_node(request: HttpRequest): #except Provider.DoesNotExist: # return JsonResponse({'error': 'Provider not found'}, status=404) +# Django view for search functionality +def search(request): + results = [] + if request.method == 'POST': + form = SearchForm(request.POST) + if form.is_valid(): + search_term = form.cleaned_data['search_term'] + search_type = form.cleaned_data['search_type'] + + # Log form data for debugging + logger.info(f"Search form data: search_term={search_term}, search_type={search_type}") + + # Determine which query to use based on search type + if search_type == 'alias': + query = SEARCH_BY_ALIAS + elif search_type == 'definition': + query = SEARCH_BY_DEFINITION + elif search_type == 'context': + query = SEARCH_BY_CONTEXT + else: + query = GENERAL_GRAPH_SEARCH # For 'general' search + + # Log the query and params being sent to Neo4j + logger.info(f"Executing query: {query} with params: {{'search_term': {search_term}}}") + + # Execute the query + results_data = execute_neo4j_query(query, {"search_term": search_term}) + + if results_data: + logger.info(f"Raw results data: {results_data}") + results = [ + { + "LCVID": record[0], # Assuming record[0] is 'LCVID' + "Alias": record[1], # Assuming record[1] is 'Alias' + "Definition": record[2], # Assuming record[2] is 'Definition' + "Context": record[3] # Assuming record[3] is 'Context' + } + for record in results_data # Iterating over each record in results_data + ] + else: + logger.info("No results found.") + results = [{'error': 'No results found or error querying Neo4j.'}] + + else: + form = SearchForm() + + return render(request, 'search.html', {'form': form, 'results': results}) + +def create_alias(request): + if request.method == 'POST': + form = AliasForm(request.POST) + if form.is_valid(): + alias = form.save() # This will create and save the Alias to Neo4j + + # Check if there was an error with the context linking + context_error = getattr(alias, 'context_error', None) + + if context_error: + # If there was a context error, show a message + messages.error(request, f"Error: {context_error}") + else: + # If the alias was created successfully, show a success message + messages.success(request, f"Warning: No Context provided but Alias '{alias.alias}' has been successfully created.") + + # Reset the form for another entry if needed + form = AliasForm() + + else: + messages.error(request, "There was an error with the form. Please try again.") + else: + form = AliasForm() + + return render(request, 'create_alias.html', {'form': form}) + # Provider and LCVTerm (Otherwise alternative Parent and child) Now with collision detection on both. def create_provider(request): if request.method == 'POST': From 10665423cda9e87fc8b5c40308e141cc4434bce9 Mon Sep 17 00:00:00 2001 From: EdblackGitHub Date: Wed, 4 Dec 2024 16:21:57 -0500 Subject: [PATCH 04/11] Update models.py --- app/uid/models.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/app/uid/models.py b/app/uid/models.py index b932025..401c80c 100644 --- a/app/uid/models.py +++ b/app/uid/models.py @@ -7,6 +7,9 @@ from typing import List from uuid import uuid4 +from collections import defaultdict +from core.models import NeoTerm + logger = logging.getLogger(__name__) GLOBAL_PROVIDER_OWNER_UID = "0xFFFFFFFF" @@ -23,6 +26,51 @@ def check_neo4j_connection(): time.sleep(1) # Wait before retrying return False +# Alias class incase you create and alias with no context +class Alias(StructuredNode): + alias = StringProperty(unique_index=True) # The alias name + context = StringProperty(required=False, default=None) # Optional context + points_to = RelationshipTo('NeoTerm', 'POINTS_TO') # The relationship to NeoTerm + context_error = StringProperty(required=False) # Optional field to store error message + + def __str__(self): + return self.alias + + def link_to_term(self, neo_term): + """Link this alias to a NeoTerm.""" + if isinstance(neo_term, NeoTerm): + self.points_to.connect(neo_term) + + def save(self, *args, **kwargs): + """Override the save method to automatically link the alias to a NeoTerm if context is provided.""" + context_error = None # Initialize an error variable + + # Call the parent class save method + super(Alias, self).save(*args, **kwargs) + + if self.context: + # Get or create the NeoTerm based on the context + term, created = NeoTerm.get_or_create(uid=self.context) + if term: + # Set relationships for the NeoTerm, including the alias + term.set_relationships(definition_node, context_node, self) + else: + context_error = f"No matching NeoTerm found for context: {self.context}" + else: + # If no context is provided, link to a default NeoTerm (first available NeoTerm) + term = NeoTerm.nodes.first() # You can change this to a specific fallback logic + if term: + self.link_to_term(term) + else: + context_error = "No NeoTerm available to link." + + # If an error was encountered, raise it so it can be caught in the view or returned to the form + if context_error: + self.context_error = context_error # Store the error message in the instance + self.save() + + return context_error # Return the error message, if any + # Generated Logs to track instance, time of generation, uid, provider and lcv terms class GeneratedUIDLog(models.Model): uid = models.CharField(max_length=255, default="UNKNOWN") @@ -512,4 +560,4 @@ def report_all_term_uids(): # # Find collisions (where length > 1) # collisions = {key: value for key, value in uid_dict.items() if len(value) > 1} -# return collisions \ No newline at end of file +# return collisions From 5eeb9ca8f918ba3ca31aea7d43106bb8aef14f89 Mon Sep 17 00:00:00 2001 From: EdblackGitHub Date: Wed, 4 Dec 2024 16:23:58 -0500 Subject: [PATCH 05/11] Update forms.py --- app/uid/forms.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/app/uid/forms.py b/app/uid/forms.py index 2077103..cfb8620 100644 --- a/app/uid/forms.py +++ b/app/uid/forms.py @@ -1,6 +1,7 @@ from django import forms from .models import Provider, LCVTerm, UIDRequestToken # Import Neo4j models directly from uuid import uuid4 +from .models import Alias # Import Neo4j models directly #from .models import LastGeneratedUID #class LastGeneratedUIDForm(forms.ModelForm): @@ -70,3 +71,32 @@ class Meta: model = LCVTerm #fields = ['uid', 'term', 'echelon_level'] fields = ['provider_name', 'term', 'echelon', 'structure'] # UID is self Generated + +# Search Forms +class SearchForm(forms.Form): + search_term = forms.CharField(max_length=255, required=True, label="Search Term") + search_type = forms.ChoiceField(choices=[ + ('general', 'General Search'), + ('alias', 'Search by Alias'), + ('definition', 'Search by Definition'), + ('context', 'Search by Context'), + ], required=True, label="Search Type" + ) + context = forms.CharField(label='Context', required=False, max_length=255) + +class AliasForm(forms.Form): + alias = forms.CharField(max_length=255, required=True) # The alias name + context = forms.CharField(max_length=255, required=False) # Context as a string (the term's name) + + def save(self): + # Create and save Alias + alias = Alias(alias=self.cleaned_data['alias'], context=self.cleaned_data.get('context')) + alias.save() + + # Optionally, if context is provided, link to the NeoTerm + if alias.context: + term = NeoTerm.nodes.get_or_none(name=alias.context) + if term: + alias.link_to_term(term) # Link this alias to the found NeoTerm + + return alias From 5536e79dd0647bba73b23cb48e9345a0b4462a14 Mon Sep 17 00:00:00 2001 From: EdblackGitHub Date: Tue, 10 Dec 2024 13:43:11 -0500 Subject: [PATCH 06/11] Create create_alias.html --- app/uid/templates/create_alias.html | 44 +++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 app/uid/templates/create_alias.html diff --git a/app/uid/templates/create_alias.html b/app/uid/templates/create_alias.html new file mode 100644 index 0000000..ca6eae3 --- /dev/null +++ b/app/uid/templates/create_alias.html @@ -0,0 +1,44 @@ + + + + Create Alias + + + +

Create Alias

+ + +
+ {% csrf_token %} + {{ form.as_p }} + +
+ + +
+ {% if messages %} + {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} + {% endif %} +
+ +

Back to Home

+ + From e3bfc0cd8986cea36f7482ec437c942c6bcbd66d Mon Sep 17 00:00:00 2001 From: EdblackGitHub Date: Tue, 10 Dec 2024 13:48:57 -0500 Subject: [PATCH 07/11] Update views.py Updated views.py for alias with/without context and using Neoalias. Commend old working code. --- app/uid/views.py | 58 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/app/uid/views.py b/app/uid/views.py index 7e56ae2..3eb00bd 100644 --- a/app/uid/views.py +++ b/app/uid/views.py @@ -189,21 +189,61 @@ def search(request): return render(request, 'search.html', {'form': form, 'results': results}) +#def create_alias(request): + # if request.method == 'POST': + # form = AliasForm(request.POST) + # if form.is_valid(): + # alias = form.save() # This will create and save the Alias to Neo4j + + # Check if there was an error with the context linking + # context_error = getattr(alias, 'context_error', None) + + # if context_error: + # If there was a context error, show a message + # messages.error(request, f"Error: {context_error}") + # else: + # If the alias was created successfully, show a success message + # messages.success(request, f"Warning: No Context provided but Alias '{alias.alias}' has been successfully created.") + + # Reset the form for another entry if needed + # form = AliasForm() + + #else: + # messages.error(request, "There was an error with the form. Please try again.") + #else: + # form = AliasForm() + + #return render(request, 'create_alias.html', {'form': form}) + def create_alias(request): if request.method == 'POST': form = AliasForm(request.POST) if form.is_valid(): - alias = form.save() # This will create and save the Alias to Neo4j - - # Check if there was an error with the context linking - context_error = getattr(alias, 'context_error', None) + alias = form.save() # This will create and save the Alias to Neo4j + + # Retrieve the context from the form (if provided) + context = form.cleaned_data.get('context') + + # If a context is provided, try to link it to the alias + if context: + try: + # Assuming 'context' is the context related to the alias + term = alias.term # Fetch the term associated with the alias + # Link the alias to the term and context in Neo4j + alias.context.connect(context) + + # Also connect the alias to the term if it's not already done + term.aliases.connect(alias) + + messages.success(request, f"Alias '{alias.alias}' successfully created and linked to the context '{context.context}'.") + + except Exception as e: + # If there is an error with linking the context, handle it + messages.error(request, f"Error linking alias to context: {str(e)}") - if context_error: - # If there was a context error, show a message - messages.error(request, f"Error: {context_error}") else: - # If the alias was created successfully, show a success message - messages.success(request, f"Warning: No Context provided but Alias '{alias.alias}' has been successfully created.") + # If no context is provided, still save the alias but show a warning + messages.warning(request, f"Alias '{alias.alias}' created without a context. You can add a context later.") # Reset the form for another entry if needed form = AliasForm() From 0e78368f904c918825a67bb88448f062636a2998 Mon Sep 17 00:00:00 2001 From: EdblackGitHub Date: Tue, 10 Dec 2024 13:54:13 -0500 Subject: [PATCH 08/11] Update forms.py Updated Forms.py to include new NeoAlias changes. --- app/uid/forms.py | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/app/uid/forms.py b/app/uid/forms.py index cfb8620..d6f2f16 100644 --- a/app/uid/forms.py +++ b/app/uid/forms.py @@ -3,6 +3,7 @@ from uuid import uuid4 from .models import Alias # Import Neo4j models directly #from .models import LastGeneratedUID +from .models import NeoAliasManager #class LastGeneratedUIDForm(forms.ModelForm): # class Meta: @@ -84,19 +85,41 @@ class SearchForm(forms.Form): ) context = forms.CharField(label='Context', required=False, max_length=255) +#class AliasForm(forms.Form): + # alias = forms.CharField(max_length=255, required=True) # The alias name + # context = forms.CharField(max_length=255, required=False) # Context as a string (the term's name) + + # def save(self): + # Create and save Alias + # alias = Alias(alias=self.cleaned_data['alias'], context=self.cleaned_data.get('context')) + # alias.save() + + # Optionally, if context is provided, link to the NeoTerm + # if alias.context: + # term = NeoTerm.nodes.get_or_none(name=alias.context) + # if term: + # alias.link_to_term(term) # Link this alias to the found NeoTerm + + #return alias + class AliasForm(forms.Form): alias = forms.CharField(max_length=255, required=True) # The alias name context = forms.CharField(max_length=255, required=False) # Context as a string (the term's name) def save(self): - # Create and save Alias - alias = Alias(alias=self.cleaned_data['alias'], context=self.cleaned_data.get('context')) - alias.save() + from core.models import NeoAlias + # Retrieve cleaned data + alias_name = self.cleaned_data['alias'] + context = self.cleaned_data.get('context') - # Optionally, if context is provided, link to the NeoTerm - if alias.context: - term = NeoTerm.nodes.get_or_none(name=alias.context) - if term: - alias.link_to_term(term) # Link this alias to the found NeoTerm + # Use NeoAliasManager to create or link the alias + context_error = NeoAliasManager.link_alias_to_term_and_context(alias_name, context) - return alias + # Check if there were any errors while linking + if context_error: + raise forms.ValidationError(f"Error: {context_error}") + + # Optionally, you can also return the created or updated NeoAlias + neo_alias, created = NeoAlias.get_or_create(alias=alias_name) + + return neo_alias # Return the created or linked NeoAlias instance From 0325f7a0caa3e16d78ba8f34885864c0b74c3364 Mon Sep 17 00:00:00 2001 From: EdblackGitHub Date: Tue, 10 Dec 2024 14:02:01 -0500 Subject: [PATCH 09/11] Update models.py Updated models.py to manage the linking NeoAlias, NeoTerm and NeoContext even if context is not provided default to first available NeoTerm. THis is now handled by the addition of the NeoAliasManager class. --- app/uid/models.py | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/app/uid/models.py b/app/uid/models.py index 401c80c..5f4d594 100644 --- a/app/uid/models.py +++ b/app/uid/models.py @@ -37,6 +37,7 @@ def __str__(self): return self.alias def link_to_term(self, neo_term): + from core.models import NeoTerm, NeoAlias, NeoContext """Link this alias to a NeoTerm.""" if isinstance(neo_term, NeoTerm): self.points_to.connect(neo_term) @@ -49,8 +50,8 @@ def save(self, *args, **kwargs): super(Alias, self).save(*args, **kwargs) if self.context: - # Get or create the NeoTerm based on the context - term, created = NeoTerm.get_or_create(uid=self.context) + from core.models import NeoTerm, NeoAlias, NeoContext + term, created = NeoTerm.get_or_create(uid=self.context) # Get or create the NeoTerm based on the context if term: # Set relationships for the NeoTerm, including the alias term.set_relationships(definition_node, context_node, self) @@ -71,6 +72,44 @@ def save(self, *args, **kwargs): return context_error # Return the error message, if any +# Addition of the NeoAliasManager class to use NeoAlias in core/models +class NeoAliasManager: + @staticmethod + def link_alias_to_term_and_context(alias: str, context: str = None): + from core.models import NeoTerm, NeoAlias, NeoContext + """Manage the linking of NeoAlias to NeoTerm and NeoContext.""" + context_error = None + + # Get or create the NeoAlias (same as get_or_create in your Alias class) + alias_node, created = NeoAlias.get_or_create(alias) + + if context: + # If context is provided, attempt to get or create NeoContext + context_node, context_created = NeoContext.get_or_create(context) + if context_node: + # Link the alias to the context + alias_node.context.connect(context_node) + else: + context_error = f"No matching NeoContext found for context: {context}" + + if not alias_node.term: + # If no term is linked, link the alias to the first available NeoTerm + term = NeoTerm.nodes.first() # Fallback logic to link to the first available NeoTerm + if term: + alias_node.term.connect(term) + else: + context_error = context_error or "No NeoTerm available to link." + + # Save the alias (optional) + alias_node.save() + + # If any errors occurred, return the error message + if context_error: + alias_node.context_error = context_error # Store error on the alias + alias_node.save() # Save the alias again with the error information + + return context_error # Return the error message, if any + # Generated Logs to track instance, time of generation, uid, provider and lcv terms class GeneratedUIDLog(models.Model): uid = models.CharField(max_length=255, default="UNKNOWN") From d71e01b981daf3bf17d8cdb451a4d7f822f58363 Mon Sep 17 00:00:00 2001 From: bay Date: Mon, 16 Dec 2024 20:05:12 -0800 Subject: [PATCH 10/11] fixed search, alias without context still broken --- app/core/admin.py | 10 +++- app/core/models.py | 93 +++++++++++++++-------------- app/core/urls.py | 1 - app/core/utils.py | 108 +++++++++++++++++++++------------- app/uid/forms.py | 34 +++++------ app/uid/models.py | 88 +++++++++++++-------------- app/uid/templates/search.html | 16 ++--- app/uid/urls.py | 2 +- app/uid/views.py | 89 +++++++++++++++------------- 9 files changed, 243 insertions(+), 198 deletions(-) diff --git a/app/core/admin.py b/app/core/admin.py index 8bbdb62..9667722 100644 --- a/app/core/admin.py +++ b/app/core/admin.py @@ -142,8 +142,8 @@ def get_form(self, request, obj=None, **kwargs): class NeoTermAdminForm(forms.ModelForm): alias = forms.CharField(required=False, help_text="Enter alias") # Custom field definition = forms.CharField(required=True, help_text="Enter definition") # Custom field - context = forms.CharField(required=True, help_text="Enter context") # Custom field - context_description = forms.CharField(required=True, help_text="Enter context description") # Custom field + context = forms.CharField(required=False, help_text="Enter context") # Custom field + context_description = forms.CharField(required=False, help_text="Enter context description") # Custom field class Meta: model = NeoTerm @@ -177,8 +177,12 @@ def save_model(self, request, obj, form, change): definition = form.cleaned_data['definition'] context = form.cleaned_data['context'] context_description = form.cleaned_data['context_description'] - logger.info(f"Creating NeoTerm with alias: {alias}, definition: {definition}, context: {context}, context_description: {context_description}") + + logger.info(f"Creating NeoTerm with alias: {alias}, definition: {definition}, context: {context}, context_description: {context_description}") + if context is None: + messages.warning(request, 'Adding an alias without a context is not recommended') + return run_node_creation(alias=alias, definition=definition, context=context, context_description=context_description) messages.success(request, 'NeoTerm saved successfully.') diff --git a/app/core/models.py b/app/core/models.py index d0c39a7..e5ffe08 100644 --- a/app/core/models.py +++ b/app/core/models.py @@ -472,25 +472,17 @@ def create_new_term(cls, lcvid: str = None) -> 'NeoTerm': return term_node - def set_relationships(self, definition_node, context_node, alias_node): + def set_relationships(self, definition_node=None, context_node=None, alias_node=None): try: if alias_node: self.alias.connect(alias_node) - self.context.connect(context_node) - self.definition.connect(definition_node) + if context_node: + self.context.connect(context_node) + if definition_node: + self.definition.connect(definition_node) except exceptions.NeomodelException as e: logger.error(f"NeoModel-related error while connecting relationships for term '{self.uid}': {e}") raise e - -# @abstract -# class NeoGeneric(DjangoNode): - -# def are_any_nodes_present(self, model, **kwargs): -# pass -# def connect(self, node, relationship): -# pass - - class NeoAlias(DjangoNode): django_id = UniqueIdProperty() alias = StringProperty(unique_index=True,required=True) @@ -520,16 +512,25 @@ def get_or_create(cls, alias: str) -> Tuple['NeoAlias', bool]: logger.error(f"Unexpected error in get_or_create for alias '{alias}': {e}") raise e - def set_relationships(self, term_node, context_node): + def set_relationships(self, term_node=None, context_node=None, collided_definition=None): try: - self.term.connect(term_node) - self.context.connect(context_node) + if term_node: + self.term.connect(term_node) + if context_node: + self.context.connect(context_node) + if collided_definition: + self.collided_definition.connect(collided_definition) except exceptions.NeomodelException as e: logger.error(f"NeoModel-related error while connecting relationships for alias '{self.alias}': {e}") raise e except Exception as e: logger.error(f"Unexpected error while connecting relationships for alias '{self.alias}': {e}") raise e + + def handle_collision(self, definition_node, context_node=None): + if context_node: + self.context.connect(context_node) + self.collided_definition.connect(definition_node) class NeoContext(DjangoNode): django_id = UniqueIdProperty() @@ -545,32 +546,30 @@ class Meta: @classmethod def get_or_create(cls, context: str) -> Tuple['NeoContext', bool]: try: - context_node = cls.nodes.get_or_none(context=context) - if context_node: return context_node, False - - context_node = NeoContext(context=context) - context_node.save() - return context_node, True - + if not context == '': + context_node = NeoContext(context=context) + context_node.save() + return context_node, True except exceptions.NeomodelException as e: logger.error(f"NeoModel-related error while getting or creating context '{context}': {e}") raise e except Exception as e: - logger.error(e) logger.error(f"Unexpected error in get_or_create for context '{context}': {e}") raise e - def set_relationships(self, term_node, alias_node, definition_node, context_description_node): + def set_relationships(self, term_node=None, alias_node=None, definition_node=None, context_description_node=None,): try: - self.term.connect(term_node) + if term_node: + self.term.connect(term_node) if alias_node: self.alias.connect(alias_node) - self.definition.connect(definition_node) - logger.info(f"Connecting context_description_node: {context_description_node}") - self.context_description.connect(context_description_node) + if definition_node: + self.definition.connect(definition_node) + if context_description_node: + self.context_description.connect(context_description_node) except exceptions.NeomodelException as e: logger.error(f"NeoModel-related error while connecting relationships for context '{self.context}': {e}") raise e @@ -588,16 +587,14 @@ class Meta: app_label = 'core' @classmethod - def get_or_create(cls, context_description: str, context_node: NeoContext): + def get_or_create(cls, context_description: str, context_node: 'NeoContext'): try: - existing_context_description = context_node.context_description.all() - if existing_context_description: - return existing_context_description[0], False - - context_description_node = NeoContextDescription(context_description=context_description) + existing = context_node.context_description.all() if context_node else [] + if existing: + return existing[0], False + context_description_node = cls(context_description=context_description) context_description_node.save() return context_description_node, True - except exceptions.NeomodelException as e: logger.error(f"NeoModel-related error while getting or creating context_description '{context_description}': {e}") raise e @@ -605,10 +602,12 @@ def get_or_create(cls, context_description: str, context_node: NeoContext): logger.error(f"Unexpected error in get_or_create for context_description '{context_description}': {e}") raise e - def set_relationships(self, definition_node, context_node): + def set_relationships(self, definition_node=None, context_node=None): try: - self.definition.connect(definition_node) - self.context.connect(context_node) + if definition_node: + self.definition.connect(definition_node) + if context_node: + self.context.connect(context_node) except exceptions.NeomodelException as e: logger.error(f"NeoModel-related error while connecting relationships for context_description '{self.context_description}': {e}") raise e @@ -642,12 +641,20 @@ def get_or_create(cls, definition:str, definition_embedding=None): except Exception as e: logger.error(f"Error in get for NeoDefinition '{definition}': {e}") + raise e - def set_relationships(self, term_node, context_node, context_description_node): + def set_relationships(self, term_node=None, context_node=None, context_description_node=None, collision=None, collision_alias=None): try: - self.term.connect(term_node) - self.context.connect(context_node) - self.context_description.connect(context_description_node) + if term_node: + self.term.connect(term_node) + if context_node: + self.context.connect(context_node) + if context_description_node: + self.context_description.connect(context_description_node) + if collision: + self.collision.connect(collision) + if collision_alias: + self.collision_alias.connect(collision_alias) except exceptions.NeomodelException as e: logger.error(f"NeoModel-related error while connecting relationships for definition '{self.definition}': {e}") raise e diff --git a/app/core/urls.py b/app/core/urls.py index b13d400..06a7f0b 100644 --- a/app/core/urls.py +++ b/app/core/urls.py @@ -4,5 +4,4 @@ urlpatterns = [ path('neo_term_list/', views.get_neo_terms, name='neo_term_list'), - ] diff --git a/app/core/utils.py b/app/core/utils.py index abef93f..50d341a 100644 --- a/app/core/utils.py +++ b/app/core/utils.py @@ -15,7 +15,7 @@ def run_node_creation(definition: str, context: str, context_description: str, a if deconfliction_status == 'unique': run_unique_definition_creation(definition=definition, context=context, context_description=context_description, definition_embedding=definition_vector_embedding, alias=alias) elif deconfliction_status == 'duplicate': - run_duplicate_definition_creation(alias, most_similar_text, context, context_description) + run_duplicate_definition_creation(alias=alias, definition=most_similar_text, context=context, context_description=context_description) elif deconfliction_status == 'collision': run_collision_definition_creation(alias, most_similar_text, definition, context, context_description, definition_vector_embedding, highest_score) @@ -27,66 +27,94 @@ def run_node_creation(definition: str, context: str, context_description: str, a def run_unique_definition_creation(definition, context, context_description, definition_embedding, alias): try: - # uid = uuid4() term_node = NeoTerm.create_new_term() - - alias_node = None - if alias: - alias_node, _ = NeoAlias.get_or_create(alias=alias) - + alias_node, _ = NeoAlias.get_or_create(alias=alias) if alias else (None, None) definition_node, _ = NeoDefinition.get_or_create(definition=definition, definition_embedding=definition_embedding) - context_node, _ = NeoContext.get_or_create(context=context) - context_description_node, _ = NeoContextDescription.get_or_create(context_description=context_description, context_node=context_node) - context_node.set_relationships(term_node=term_node, alias_node=alias_node, definition_node=definition_node, context_description_node=context_description_node) term_node.set_relationships(alias_node=alias_node, definition_node=definition_node, context_node=context_node) + context_node.set_relationships(term_node=term_node, alias_node=alias_node, definition_node=definition_node, context_description_node=context_description_node) + definition_node.set_relationships(term_node=term_node, context_node=context_node, context_description_node=context_description_node) + context_description_node.set_relationships(definition_node=definition_node, context_node=context_node) + if alias_node: alias_node.set_relationships(term_node=term_node, context_node=context_node) - definition_node.set_relationships(term_node, context_node, context_description_node) - context_description_node.set_relationships(definition_node, context_node) - - except Exception as e: + + except Exception as e: logger.error(f"Error in run_unique_definition_creation: {e}") - raise e + raise +# def run_duplicate_definition_creation(alias, definition, context, context_description): +# try: +# alias_node = None +# context_node = None + +# if alias: +# alias_node, _ = NeoAlias.get_or_create(alias=alias) +# logger.info(f"Alias Node: {alias_node}") + +# if context: +# context_node, _ = NeoContext.get_or_create(context=context) +# logger.info(f"Context Node: {context_node}") +# context_description_node, _ = NeoContextDescription.get_or_create(context_description=context_description, context_node=context_node) +# logger.info(f"Context Description Node: {context_description_node}") + +# definition_node, _ = NeoDefinition.get_or_create(definition=definition) +# term_node = definition_node.term.single() +# if not term_node: +# context_node.alias.connect(alias_node) +# context_node.context_description.connect(context_description_node) +# context_node.definition.connect(definition_node) +# alias_node.context.connect(context_node) +# alias_node.collided_definition.connect(definition_node) +# definition_node.context.connect(context_node) +# definition_node.context_description.connect(context_description_node) +# return +# context_node.set_relationships(term_node=term_node, alias_node=alias_node, definition_node=definition_node, context_description_node=context_description_node) +# logger.info(f"Context Node Relationships: {context_node}") +# logger.info(f"Alias Node Relationships: {alias_node}") +# term_node.set_relationships(alias_node=alias_node, definition_node=definition_node, context_node=context_node) +# if alias_node: +# alias_node.set_relationships(term_node, context_node) +# definition_node.set_relationships(term_node, context_node, context_description_node) + +# except Exception as e: +# logger.error(f"Error in run_duplicate_definition_creation: {e}") +# raise e + def run_duplicate_definition_creation(alias, definition, context, context_description): try: - - alias_node = None - alias__ = False - if alias: - alias_node, _ = NeoAlias.get_or_create(alias=alias) - logger.info(f"Alias Node: {alias_node}") - - context_node, _ = NeoContext.get_or_create(context=context) - logger.info(f"Context Node: {context_node}") + alias_node, _ = NeoAlias.get_or_create(alias=alias) if alias else (None, None) + context_node, _ = NeoContext.get_or_create(context=context) if context else (None, None) context_description_node, _ = NeoContextDescription.get_or_create(context_description=context_description, context_node=context_node) - logger.info(f"Context Description Node: {context_description_node}") - definition_node, _ = NeoDefinition.get_or_create(definition=definition) + term_node = definition_node.term.single() + if not term_node: - context_node.alias.connect(alias_node) - context_node.context_description.connect(context_description_node) - context_node.definition.connect(definition_node) - alias_node.context.connect(context_node) - alias_node.collided_definition.connect(definition_node) - definition_node.context.connect(context_node) - definition_node.context_description.connect(context_description_node) + # Collision scenario since no term node exists + # Use collision scenario methods + if alias_node: + alias_node.set_relationships(collided_definition=definition_node, context_node=context_node) + if context_node: + context_node.set_relationships(alias_node=alias_node, definition_node=definition_node, context_description_node=context_description_node) + definition_node.set_relationships(alias_node=alias_node, context_node=context_node, context_description_node=context_description_node) + context_description_node.set_relationships(definition_node=definition_node, context_node=context_node) return - context_node.set_relationships(term_node=term_node, alias_node=alias_node, definition_node=definition_node, context_description_node=context_description_node) - logger.info(f"Context Node Relationships: {context_node}") - logger.info(f"Alias Node Relationships: {alias_node}") + + # Duplicate scenario with a term node (acts like unique scenario) term_node.set_relationships(alias_node=alias_node, definition_node=definition_node, context_node=context_node) + context_node.set_relationships(term_node=term_node, alias_node=alias_node, definition_node=definition_node, context_description_node=context_description_node) + definition_node.set_relationships(term_node=term_node, context_node=context_node, context_description_node=context_description_node) + context_description_node.set_relationships(definition_node=definition_node, context_node=context_node) + if alias_node: - alias_node.set_relationships(term_node, context_node) - definition_node.set_relationships(term_node, context_node, context_description_node) + alias_node.set_relationships(term_node=term_node, context_node=context_node) - except Exception as e: + except Exception as e: logger.error(f"Error in run_duplicate_definition_creation: {e}") - raise e + raise def run_collision_definition_creation(alias, most_similar_definition, definition, context, context_description, definition_vector_embedding, highest_score): try: diff --git a/app/uid/forms.py b/app/uid/forms.py index d6f2f16..e178d22 100644 --- a/app/uid/forms.py +++ b/app/uid/forms.py @@ -1,7 +1,7 @@ from django import forms from .models import Provider, LCVTerm, UIDRequestToken # Import Neo4j models directly from uuid import uuid4 -from .models import Alias # Import Neo4j models directly +# from .models import Alias # Import Neo4j models directly #from .models import LastGeneratedUID from .models import NeoAliasManager @@ -102,24 +102,24 @@ class SearchForm(forms.Form): #return alias -class AliasForm(forms.Form): - alias = forms.CharField(max_length=255, required=True) # The alias name - context = forms.CharField(max_length=255, required=False) # Context as a string (the term's name) +# class AliasForm(forms.Form): +# alias = forms.CharField(max_length=255, required=True) # The alias name +# context = forms.CharField(max_length=255, required=False) # Context as a string (the term's name) - def save(self): - from core.models import NeoAlias - # Retrieve cleaned data - alias_name = self.cleaned_data['alias'] - context = self.cleaned_data.get('context') +# def save(self): +# from core.models import NeoAlias +# # Retrieve cleaned data +# alias_name = self.cleaned_data['alias'] +# context = self.cleaned_data.get('context') - # Use NeoAliasManager to create or link the alias - context_error = NeoAliasManager.link_alias_to_term_and_context(alias_name, context) +# # Use NeoAliasManager to create or link the alias +# context_error = NeoAliasManager.link_alias_to_term_and_context(alias_name, context) - # Check if there were any errors while linking - if context_error: - raise forms.ValidationError(f"Error: {context_error}") +# # Check if there were any errors while linking +# if context_error: +# raise forms.ValidationError(f"Error: {context_error}") - # Optionally, you can also return the created or updated NeoAlias - neo_alias, created = NeoAlias.get_or_create(alias=alias_name) +# # Optionally, you can also return the created or updated NeoAlias +# neo_alias, created = NeoAlias.get_or_create(alias=alias_name) - return neo_alias # Return the created or linked NeoAlias instance +# return neo_alias # Return the created or linked NeoAlias instance diff --git a/app/uid/models.py b/app/uid/models.py index 5f4d594..0d2466b 100644 --- a/app/uid/models.py +++ b/app/uid/models.py @@ -8,7 +8,7 @@ from uuid import uuid4 from collections import defaultdict -from core.models import NeoTerm +# from core.models import NeoTerm logger = logging.getLogger(__name__) @@ -27,50 +27,50 @@ def check_neo4j_connection(): return False # Alias class incase you create and alias with no context -class Alias(StructuredNode): - alias = StringProperty(unique_index=True) # The alias name - context = StringProperty(required=False, default=None) # Optional context - points_to = RelationshipTo('NeoTerm', 'POINTS_TO') # The relationship to NeoTerm - context_error = StringProperty(required=False) # Optional field to store error message - - def __str__(self): - return self.alias - - def link_to_term(self, neo_term): - from core.models import NeoTerm, NeoAlias, NeoContext - """Link this alias to a NeoTerm.""" - if isinstance(neo_term, NeoTerm): - self.points_to.connect(neo_term) - - def save(self, *args, **kwargs): - """Override the save method to automatically link the alias to a NeoTerm if context is provided.""" - context_error = None # Initialize an error variable - - # Call the parent class save method - super(Alias, self).save(*args, **kwargs) - - if self.context: - from core.models import NeoTerm, NeoAlias, NeoContext - term, created = NeoTerm.get_or_create(uid=self.context) # Get or create the NeoTerm based on the context - if term: - # Set relationships for the NeoTerm, including the alias - term.set_relationships(definition_node, context_node, self) - else: - context_error = f"No matching NeoTerm found for context: {self.context}" - else: - # If no context is provided, link to a default NeoTerm (first available NeoTerm) - term = NeoTerm.nodes.first() # You can change this to a specific fallback logic - if term: - self.link_to_term(term) - else: - context_error = "No NeoTerm available to link." - - # If an error was encountered, raise it so it can be caught in the view or returned to the form - if context_error: - self.context_error = context_error # Store the error message in the instance - self.save() +# class Alias(StructuredNode): +# alias = StringProperty(unique_index=True) # The alias name +# context = StringProperty(required=False, default=None) # Optional context +# points_to = RelationshipTo('NeoTerm', 'POINTS_TO') # The relationship to NeoTerm +# context_error = StringProperty(required=False) # Optional field to store error message + +# def __str__(self): +# return self.alias + +# def link_to_term(self, neo_term): +# from core.models import NeoTerm, NeoAlias, NeoContext +# """Link this alias to a NeoTerm.""" +# if isinstance(neo_term, NeoTerm): +# self.points_to.connect(neo_term) + +# def save(self, *args, **kwargs): +# """Override the save method to automatically link the alias to a NeoTerm if context is provided.""" +# context_error = None # Initialize an error variable + +# # Call the parent class save method +# super(Alias, self).save(*args, **kwargs) + +# if self.context: +# from core.models import NeoTerm, NeoAlias, NeoContext +# term, created = NeoTerm.get_or_create(uid=self.context) # Get or create the NeoTerm based on the context +# if term: +# # Set relationships for the NeoTerm, including the alias +# term.set_relationships(definition_node, context_node, self) +# else: +# context_error = f"No matching NeoTerm found for context: {self.context}" +# else: +# # If no context is provided, link to a default NeoTerm (first available NeoTerm) +# term = NeoTerm.nodes.first() # You can change this to a specific fallback logic +# if term: +# self.link_to_term(term) +# else: +# context_error = "No NeoTerm available to link." + +# # If an error was encountered, raise it so it can be caught in the view or returned to the form +# if context_error: +# self.context_error = context_error # Store the error message in the instance +# self.save() - return context_error # Return the error message, if any +# return context_error # Return the error message, if any # Addition of the NeoAliasManager class to use NeoAlias in core/models class NeoAliasManager: diff --git a/app/uid/templates/search.html b/app/uid/templates/search.html index fee79fe..7cdc87b 100644 --- a/app/uid/templates/search.html +++ b/app/uid/templates/search.html @@ -88,7 +88,7 @@

Search CCV/LCV Definitions

- {% if results_data %} + {% if results.data %}

Search Results

@@ -101,12 +101,12 @@

Search Results

- {% for record in results_data %} + {% for record in results.data %} - - - - + + + + {% endfor %} @@ -114,8 +114,8 @@

Search Results

{% endif %} - {% if error %} -

{{ error }}

+ {% if results.error %} +

{{ results.error }}

{% endif %} diff --git a/app/uid/urls.py b/app/uid/urls.py index 2b3b688..4f60d3b 100644 --- a/app/uid/urls.py +++ b/app/uid/urls.py @@ -18,7 +18,7 @@ path('api/generate', api_generate_uid, name='uid-generated'), path('search/', views.search, name='search'), # For the search endpoint - path('create_alias/', views.create_alias, name='create_alias'), + # path('create_alias/', views.create_alias, name='create_alias'), # path('api/uid-repo/', UIDRepoViewSet.as_view({'get': 'list'}), name='uid-repo'), # path('api/uid/all', UIDTermViewSet.as_view({'get': 'list'}), name='uid-all'), diff --git a/app/uid/views.py b/app/uid/views.py index 3eb00bd..a367768 100644 --- a/app/uid/views.py +++ b/app/uid/views.py @@ -16,8 +16,8 @@ from .forms import SearchForm import requests import urllib.parse -from .models import Alias -from .forms import AliasForm +# from .models import Alias +# from .forms import AliasForm # Cypher Queries SEARCH_BY_ALIAS = """ @@ -28,7 +28,6 @@ OPTIONAL MATCH (term)-[:POINTS_TO]->(def:NeoDefinition) OPTIONAL MATCH (ctx:NeoContext)-[:IS_A]->(term) RETURN term.uid as LCVID, a.alias as Alias, def.definition as Definition, ctx.context as Context -LIMIT 100 """ SEARCH_BY_DEFINITION = """ @@ -39,7 +38,6 @@ OPTIONAL MATCH (a:NeoAlias)-[:POINTS_TO]->(term) OPTIONAL MATCH (ctx:NeoContext)-[:IS_A]->(term) RETURN term.uid as LCVID, a.alias as Alias, def.definition as Definition, ctx.context as Context -LIMIT 100 """ SEARCH_BY_CONTEXT = """ @@ -50,7 +48,6 @@ OPTIONAL MATCH (term)-[:POINTS_TO]->(def:NeoDefinition) OPTIONAL MATCH (a:NeoAlias)-[:POINTS_TO]->(term) RETURN term.uid as LCVID, a.alias as Alias, def.definition as Definition, ctx.context as Context -LIMIT 100 """ GENERAL_GRAPH_SEARCH = """ @@ -68,11 +65,11 @@ MATCH path = (n)-[*1..2]-(connected) RETURN path } -RETURN * LIMIT 100 +RETURN n.LCVID as LCVID, n.alias AS Alias, n.definition AS Definition, n.context AS Context """ # Set up logging to capture errors and important information -logger = logging.getLogger(__name__) +logger = logging.getLogger('dict_config_logger') # # Attempt to initialize the UID generator # try: @@ -141,6 +138,17 @@ def generate_uid_node(request: HttpRequest): #except Provider.DoesNotExist: # return JsonResponse({'error': 'Provider not found'}, status=404) +def execute_neo4j_query(query, params): + query_str = query + try: + logger.info(f"Executing query: {query} with params: {params}") + results, meta = db.cypher_query(query_str, params) + logger.info(results) + return results + except Exception as e: + logger.error(f"Error executing Neo4j query: {e}") + return None + # Django view for search functionality def search(request): results = [] @@ -171,7 +179,7 @@ def search(request): if results_data: logger.info(f"Raw results data: {results_data}") - results = [ + results = {"data":[ { "LCVID": record[0], # Assuming record[0] is 'LCVID' "Alias": record[1], # Assuming record[1] is 'Alias' @@ -179,14 +187,13 @@ def search(request): "Context": record[3] # Assuming record[3] is 'Context' } for record in results_data # Iterating over each record in results_data - ] + ]} else: logger.info("No results found.") - results = [{'error': 'No results found or error querying Neo4j.'}] + results = {'error': 'No results found or error querying Neo4j.'} else: form = SearchForm() - return render(request, 'search.html', {'form': form, 'results': results}) #def create_alias(request): @@ -215,45 +222,45 @@ def search(request): #return render(request, 'create_alias.html', {'form': form}) -def create_alias(request): - if request.method == 'POST': - form = AliasForm(request.POST) - if form.is_valid(): - alias = form.save() # This will create and save the Alias to Neo4j +# def create_alias(request): +# if request.method == 'POST': +# form = AliasForm(request.POST) +# if form.is_valid(): +# alias = form.save() # This will create and save the Alias to Neo4j - # Retrieve the context from the form (if provided) - context = form.cleaned_data.get('context') +# # Retrieve the context from the form (if provided) +# context = form.cleaned_data.get('context') - # If a context is provided, try to link it to the alias - if context: - try: - # Assuming 'context' is the context related to the alias - term = alias.term # Fetch the term associated with the alias - # Link the alias to the term and context in Neo4j - alias.context.connect(context) +# # If a context is provided, try to link it to the alias +# if context: +# try: +# # Assuming 'context' is the context related to the alias +# term = alias.term # Fetch the term associated with the alias +# # Link the alias to the term and context in Neo4j +# alias.context.connect(context) - # Also connect the alias to the term if it's not already done - term.aliases.connect(alias) +# # Also connect the alias to the term if it's not already done +# term.aliases.connect(alias) - messages.success(request, f"Alias '{alias.alias}' successfully created and linked to the context '{context.context}'.") +# messages.success(request, f"Alias '{alias.alias}' successfully created and linked to the context '{context.context}'.") - except Exception as e: - # If there is an error with linking the context, handle it - messages.error(request, f"Error linking alias to context: {str(e)}") +# except Exception as e: +# # If there is an error with linking the context, handle it +# messages.error(request, f"Error linking alias to context: {str(e)}") - else: - # If no context is provided, still save the alias but show a warning - messages.warning(request, f"Alias '{alias.alias}' created without a context. You can add a context later.") +# else: +# # If no context is provided, still save the alias but show a warning +# messages.warning(request, f"Alias '{alias.alias}' created without a context. You can add a context later.") - # Reset the form for another entry if needed - form = AliasForm() +# # Reset the form for another entry if needed +# form = AliasForm() - else: - messages.error(request, "There was an error with the form. Please try again.") - else: - form = AliasForm() +# else: +# messages.error(request, "There was an error with the form. Please try again.") +# else: +# form = AliasForm() - return render(request, 'create_alias.html', {'form': form}) +# return render(request, 'create_alias.html', {'form': form}) # Provider and LCVTerm (Otherwise alternative Parent and child) Now with collision detection on both. def create_provider(request): From f67e319255b34dd869c94fa4feba5e3a13f77cf7 Mon Sep 17 00:00:00 2001 From: bay Date: Tue, 17 Dec 2024 11:45:43 -0800 Subject: [PATCH 11/11] updated term creation form to allow for NeoAlias without context, hid general search feature --- app/core/admin.py | 20 +++----- app/core/models.py | 8 +++- app/core/utils.py | 89 +++++++++++------------------------ app/uid/forms.py | 40 ---------------- app/uid/templates/search.html | 1 - app/uid/views.py | 65 ------------------------- 6 files changed, 41 insertions(+), 182 deletions(-) diff --git a/app/core/admin.py b/app/core/admin.py index 9667722..0e95d59 100644 --- a/app/core/admin.py +++ b/app/core/admin.py @@ -149,16 +149,6 @@ class Meta: model = NeoTerm fields = ['lcvid', 'alias', 'definition', 'context', 'context_description'] - # def clean_definition(self): - # definition = self.cleaned_data.get('definition') - - # get_terms_with_multiple_definitions() - # # Check if the definition already exists in the NeoDefinition model - # if is_any_node_present(NeoDefinition, definition=definition): - # raise forms.ValidationError(f"A definition of '{definition}' already exists.") - - # return definition # Return the cleaned value - class NeoTermAdmin(admin.ModelAdmin): form = NeoTermAdminForm list_display = ('lcvid', 'uid') @@ -180,8 +170,13 @@ def save_model(self, request, obj, form, change): logger.info(f"Creating NeoTerm with alias: {alias}, definition: {definition}, context: {context}, context_description: {context_description}") - if context is None: - messages.warning(request, 'Adding an alias without a context is not recommended') + if context == '' and context_description == '' and alias != '': + definition_node = NeoDefinition.nodes.get_or_none(definition=definition) + if definition_node: + messages.warning(request, 'Adding an alias without a context is not recommended.') + run_node_creation(alias=alias, definition=definition, context=context, context_description=context_description) + return + messages.error(request, 'Adding a definition without a context is not allowed.') return run_node_creation(alias=alias, definition=definition, context=context, context_description=context_description) @@ -191,7 +186,6 @@ def save_model(self, request, obj, form, change): logger.error('Error saving NeoTerm: {}'.format(e)) messages.error(request, 'Error saving NeoTerm: {}'.format(e)) return - def delete_model(self, request, obj) -> None: messages.error(request, 'Deleting terms is not allowed') diff --git a/app/core/models.py b/app/core/models.py index e5ffe08..148205f 100644 --- a/app/core/models.py +++ b/app/core/models.py @@ -622,7 +622,7 @@ class NeoDefinition(DjangoNode): rejected = BooleanProperty(default=False) context = RelationshipTo('NeoContext', 'VALID_IN') context_description = RelationshipFrom('NeoContextDescription', 'BASED_ON') - term = RelationshipFrom('NeoTerm', 'POINTS_TO') + term = Relationship('NeoTerm', 'POINTS_TO') collision = Relationship('NeoDefinition', 'IS_COLLIDING_WITH') collision_alias = Relationship('NeoAlias', 'WAS_ADDED_WITH') @@ -643,6 +643,12 @@ def get_or_create(cls, definition:str, definition_embedding=None): logger.error(f"Error in get for NeoDefinition '{definition}': {e}") raise e + def get_term_node(self)-> 'NeoTerm': + if self.term: + logger.info(f'The data is: {self.term.single()}') + return self.term.single() + return None + def set_relationships(self, term_node=None, context_node=None, context_description_node=None, collision=None, collision_alias=None): try: if term_node: diff --git a/app/core/utils.py b/app/core/utils.py index 50d341a..3468f0b 100644 --- a/app/core/utils.py +++ b/app/core/utils.py @@ -44,99 +44,64 @@ def run_unique_definition_creation(definition, context, context_description, def except Exception as e: logger.error(f"Error in run_unique_definition_creation: {e}") raise - -# def run_duplicate_definition_creation(alias, definition, context, context_description): -# try: -# alias_node = None -# context_node = None - -# if alias: -# alias_node, _ = NeoAlias.get_or_create(alias=alias) -# logger.info(f"Alias Node: {alias_node}") - -# if context: -# context_node, _ = NeoContext.get_or_create(context=context) -# logger.info(f"Context Node: {context_node}") -# context_description_node, _ = NeoContextDescription.get_or_create(context_description=context_description, context_node=context_node) -# logger.info(f"Context Description Node: {context_description_node}") - -# definition_node, _ = NeoDefinition.get_or_create(definition=definition) -# term_node = definition_node.term.single() -# if not term_node: -# context_node.alias.connect(alias_node) -# context_node.context_description.connect(context_description_node) -# context_node.definition.connect(definition_node) -# alias_node.context.connect(context_node) -# alias_node.collided_definition.connect(definition_node) -# definition_node.context.connect(context_node) -# definition_node.context_description.connect(context_description_node) -# return -# context_node.set_relationships(term_node=term_node, alias_node=alias_node, definition_node=definition_node, context_description_node=context_description_node) -# logger.info(f"Context Node Relationships: {context_node}") -# logger.info(f"Alias Node Relationships: {alias_node}") -# term_node.set_relationships(alias_node=alias_node, definition_node=definition_node, context_node=context_node) -# if alias_node: -# alias_node.set_relationships(term_node, context_node) -# definition_node.set_relationships(term_node, context_node, context_description_node) - -# except Exception as e: -# logger.error(f"Error in run_duplicate_definition_creation: {e}") -# raise e def run_duplicate_definition_creation(alias, definition, context, context_description): try: alias_node, _ = NeoAlias.get_or_create(alias=alias) if alias else (None, None) context_node, _ = NeoContext.get_or_create(context=context) if context else (None, None) - context_description_node, _ = NeoContextDescription.get_or_create(context_description=context_description, context_node=context_node) + context_description_node, _ = NeoContextDescription.get_or_create(context_description=context_description, context_node=context_node) if context_description else (None, None) definition_node, _ = NeoDefinition.get_or_create(definition=definition) - term_node = definition_node.term.single() - - if not term_node: - # Collision scenario since no term node exists - # Use collision scenario methods + term_node = definition_node.get_term_node() + logger.info(term_node) + if not term_node: # Duplicate collision scenario if alias_node: alias_node.set_relationships(collided_definition=definition_node, context_node=context_node) if context_node: context_node.set_relationships(alias_node=alias_node, definition_node=definition_node, context_description_node=context_description_node) - definition_node.set_relationships(alias_node=alias_node, context_node=context_node, context_description_node=context_description_node) + definition_node.set_relationships(context_node=context_node, context_description_node=context_description_node) context_description_node.set_relationships(definition_node=definition_node, context_node=context_node) return # Duplicate scenario with a term node (acts like unique scenario) - term_node.set_relationships(alias_node=alias_node, definition_node=definition_node, context_node=context_node) - context_node.set_relationships(term_node=term_node, alias_node=alias_node, definition_node=definition_node, context_description_node=context_description_node) + term_node.set_relationships(alias_node=alias_node, definition_node=definition_node) + if context_node: + context_node.set_relationships(term_node=term_node, alias_node=alias_node, definition_node=definition_node, context_description_node=context_description_node) + definition_node.set_relationships(term_node=term_node, context_node=context_node, context_description_node=context_description_node) - context_description_node.set_relationships(definition_node=definition_node, context_node=context_node) + if context_description_node: + context_description_node.set_relationships(definition_node=definition_node, context_node=context_node) if alias_node: + if not context_node: + alias_node.set_relationships(term_node=term_node) alias_node.set_relationships(term_node=term_node, context_node=context_node) except Exception as e: logger.error(f"Error in run_duplicate_definition_creation: {e}") - raise + raise e def run_collision_definition_creation(alias, most_similar_definition, definition, context, context_description, definition_vector_embedding, highest_score): try: alias_node = None if alias: alias_node, _ = NeoAlias.get_or_create(alias=alias) - existing_definition_node = NeoDefinition.nodes.get(definition=most_similar_definition) - colliding_definition_node = NeoDefinition(definition=definition, embedding=definition_vector_embedding) - colliding_definition_node.save() + existing_definition_node, _ = NeoDefinition.get_or_create(definition=most_similar_definition) + if not existing_definition_node: + logger.error('Existing definition node not found') + raise Exception('Existing definition node not found') + colliding_definition_node, _ = NeoDefinition.get_or_create(definition=definition, definition_embedding=definition_vector_embedding) + logger.info(f"Colliding Definition Node: {colliding_definition_node}") + if not colliding_definition_node: + logger.error('Colliding definition node not found') + raise Exception('Colliding definition node not found') context_node, _ = NeoContext.get_or_create(context=context) - context_description_node, _ = NeoContextDescription.get_or_create(context_description=context_description, context_node=context_node) - alias_node.context.connect(context_node) - context_node.context_description.connect(context_description_node) - context_node.definition.connect(colliding_definition_node) - context_description_node.definition.connect(colliding_definition_node) - colliding_definition_node.context.connect(context_node) - colliding_definition_node.context_description.connect(context_description_node) - alias_node.collided_definition.connect(colliding_definition_node) - colliding_definition_node.collision_alias.connect(alias_node) - colliding_definition_node.collision.connect(existing_definition_node) + alias_node.set_relationships(context_node=context_node, collided_definition=colliding_definition_node) + context_node.set_relationships(context_description_node=context_description_node, definition_node=colliding_definition_node) + context_description_node.set_relationships(definition_node=colliding_definition_node) + colliding_definition_node.set_relationships(context_node=context_node, context_description_node=context_description_node, collision_alias=alias_node, collision=existing_definition_node) except Exception as e: logger.error(f"Error in run_collision_definition_creation: {e}") diff --git a/app/uid/forms.py b/app/uid/forms.py index e178d22..fdf647e 100644 --- a/app/uid/forms.py +++ b/app/uid/forms.py @@ -77,49 +77,9 @@ class Meta: class SearchForm(forms.Form): search_term = forms.CharField(max_length=255, required=True, label="Search Term") search_type = forms.ChoiceField(choices=[ - ('general', 'General Search'), ('alias', 'Search by Alias'), ('definition', 'Search by Definition'), ('context', 'Search by Context'), ], required=True, label="Search Type" ) context = forms.CharField(label='Context', required=False, max_length=255) - -#class AliasForm(forms.Form): - # alias = forms.CharField(max_length=255, required=True) # The alias name - # context = forms.CharField(max_length=255, required=False) # Context as a string (the term's name) - - # def save(self): - # Create and save Alias - # alias = Alias(alias=self.cleaned_data['alias'], context=self.cleaned_data.get('context')) - # alias.save() - - # Optionally, if context is provided, link to the NeoTerm - # if alias.context: - # term = NeoTerm.nodes.get_or_none(name=alias.context) - # if term: - # alias.link_to_term(term) # Link this alias to the found NeoTerm - - #return alias - -# class AliasForm(forms.Form): -# alias = forms.CharField(max_length=255, required=True) # The alias name -# context = forms.CharField(max_length=255, required=False) # Context as a string (the term's name) - -# def save(self): -# from core.models import NeoAlias -# # Retrieve cleaned data -# alias_name = self.cleaned_data['alias'] -# context = self.cleaned_data.get('context') - -# # Use NeoAliasManager to create or link the alias -# context_error = NeoAliasManager.link_alias_to_term_and_context(alias_name, context) - -# # Check if there were any errors while linking -# if context_error: -# raise forms.ValidationError(f"Error: {context_error}") - -# # Optionally, you can also return the created or updated NeoAlias -# neo_alias, created = NeoAlias.get_or_create(alias=alias_name) - -# return neo_alias # Return the created or linked NeoAlias instance diff --git a/app/uid/templates/search.html b/app/uid/templates/search.html index 7cdc87b..1ba9e95 100644 --- a/app/uid/templates/search.html +++ b/app/uid/templates/search.html @@ -78,7 +78,6 @@

Search CCV/LCV Definitions

- diff --git a/app/uid/views.py b/app/uid/views.py index a367768..983ceda 100644 --- a/app/uid/views.py +++ b/app/uid/views.py @@ -196,71 +196,6 @@ def search(request): form = SearchForm() return render(request, 'search.html', {'form': form, 'results': results}) -#def create_alias(request): - # if request.method == 'POST': - # form = AliasForm(request.POST) - # if form.is_valid(): - # alias = form.save() # This will create and save the Alias to Neo4j - - # Check if there was an error with the context linking - # context_error = getattr(alias, 'context_error', None) - - # if context_error: - # If there was a context error, show a message - # messages.error(request, f"Error: {context_error}") - # else: - # If the alias was created successfully, show a success message - # messages.success(request, f"Warning: No Context provided but Alias '{alias.alias}' has been successfully created.") - - # Reset the form for another entry if needed - # form = AliasForm() - - #else: - # messages.error(request, "There was an error with the form. Please try again.") - #else: - # form = AliasForm() - - #return render(request, 'create_alias.html', {'form': form}) - -# def create_alias(request): -# if request.method == 'POST': -# form = AliasForm(request.POST) -# if form.is_valid(): -# alias = form.save() # This will create and save the Alias to Neo4j - -# # Retrieve the context from the form (if provided) -# context = form.cleaned_data.get('context') - -# # If a context is provided, try to link it to the alias -# if context: -# try: -# # Assuming 'context' is the context related to the alias -# term = alias.term # Fetch the term associated with the alias -# # Link the alias to the term and context in Neo4j -# alias.context.connect(context) - -# # Also connect the alias to the term if it's not already done -# term.aliases.connect(alias) - -# messages.success(request, f"Alias '{alias.alias}' successfully created and linked to the context '{context.context}'.") - -# except Exception as e: -# # If there is an error with linking the context, handle it -# messages.error(request, f"Error linking alias to context: {str(e)}") - -# else: -# # If no context is provided, still save the alias but show a warning -# messages.warning(request, f"Alias '{alias.alias}' created without a context. You can add a context later.") - -# # Reset the form for another entry if needed -# form = AliasForm() - -# else: -# messages.error(request, "There was an error with the form. Please try again.") -# else: -# form = AliasForm() - -# return render(request, 'create_alias.html', {'form': form}) # Provider and LCVTerm (Otherwise alternative Parent and child) Now with collision detection on both. def create_provider(request):
{{ record.0 }} {{ record.1 }} {{ record.2 }} {{ record.3|default:"No context" }} {{ record.LCVID }} {{ record.Alias }} {{ record.Definition }} {{ record.Context|default:"No context" }}