Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add query to delete statement #93

Merged
merged 8 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions pkg_api/pkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,3 +347,19 @@ def _retrieve_and_parse_concept(
return None

return Concept(**concept_dict)

def remove_statement(self, pkg_data: PKGData) -> None:
"""Removes a statement from the PKG.

Args:
pkg_data: PKG data associated to the statement.
"""
# Remove preference derived from the statement, if any
query = utils.get_query_for_remove_preference(pkg_data)
self._connector.execute_sparql_update(query)
# Remove statement
query = utils.get_query_for_remove_statement(pkg_data)
self._connector.execute_sparql_update(query)
# Remove dangling concepts and scales
for query in utils.get_queries_for_remove_cleanup():
self._connector.execute_sparql_update(query)
113 changes: 107 additions & 6 deletions pkg_api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from pkg_api.core.namespaces import PKGPrefixes
from pkg_api.core.pkg_types import URI, SPARQLQuery

_SPARQL_STATEMENT_VARIABLE = "?statement"


def _clean_sparql_representation(sparql: str) -> str:
"""Cleans a SPARQL representation.
Expand Down Expand Up @@ -222,7 +224,7 @@ def get_query_for_add_preference(pkg_data: PKGData) -> SPARQLQuery:
query = f"""
INSERT {{
?subject wi:preference [
pav:derivedFrom ?statement ;
pav:derivedFrom {_SPARQL_STATEMENT_VARIABLE} ;
wi:topic ?object ; wo:weight [
wo:weight_value "{pkg_data.preference.weight}"^^xsd:decimal;
wo:scale pkg:StandardScale
Expand All @@ -232,7 +234,7 @@ def get_query_for_add_preference(pkg_data: PKGData) -> SPARQLQuery:
WHERE {{
{statement_node_id} a rdf:Statement ;
rdf:subject ?subject; rdf:object ?object .
BIND({statement_node_id} AS ?statement)
BIND({statement_node_id} AS {_SPARQL_STATEMENT_VARIABLE})
}}
"""

Expand All @@ -258,10 +260,10 @@ def get_query_for_get_statements(pkg_data: PKGData) -> SPARQLQuery:
SPARQL query.
"""
statement_representation = _get_statement_representation(
pkg_data, "?statement"
pkg_data, _SPARQL_STATEMENT_VARIABLE
)
query = f"""
SELECT ?statement
SELECT {_SPARQL_STATEMENT_VARIABLE}
WHERE {{
{statement_representation}
}}
Expand Down Expand Up @@ -295,14 +297,113 @@ def get_query_for_conditional_get_statements(triple: Triple) -> SPARQLQuery:
if not annotation:
continue
value = _get_property_representation(annotation)
conditions.append(f"?statement {property} {value} .")
conditions.append(f"{_SPARQL_STATEMENT_VARIABLE} {property} {value} .")

query = f"""
SELECT ?statement
SELECT {_SPARQL_STATEMENT_VARIABLE}
WHERE {{
{" ".join(conditions)}
}}
"""

# Cleaning up the query
return _clean_sparql_representation(query)


def get_query_for_remove_preference(pkg_data: PKGData) -> SPARQLQuery:
"""Gets SPARQL query to remove a preference.

Args:
pkg_data: PKG data associated to a statement.

Returns:
SPARQL query.
"""
statement = _get_statement_representation(
pkg_data, _SPARQL_STATEMENT_VARIABLE
)
# Remove statement description from conditions
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to do that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description is removed because it not possible that it is the same when asking to remove a statement, especially when using natural language. For example, a user might input "Delete my preference towards Tom Cruise" or " Remove the statements related to Tom Cruise" to remove the statement "I like Tom Cruise."

statement = re.sub(r'dc:description "[^"]+" ;', "", statement)

query = f"""
DELETE {{
?preference ?p ?o .
?subject wi:preference ?preference .
}}
WHERE {{
{statement}
?subject wi:preference ?preference .
?preference pav:derivedFrom {_SPARQL_STATEMENT_VARIABLE} .
?preference ?p ?o .
}}
"""

# Cleaning up the query
return _clean_sparql_representation(query)


def get_query_for_remove_statement(pkg_data: PKGData) -> SPARQLQuery:
"""Gets SPARQL query to remove a statement.

Note that if a preference is derived from the statement, it is also removed.

Args:
pkg_data: PKG data associated to a statement.

Returns:
SPARQL query.
"""
statement_representation = _get_statement_representation(
pkg_data, _SPARQL_STATEMENT_VARIABLE
)
# Remove statement description from conditions
statement_representation = re.sub(
r'dc:description "[^"]+" ;', "", statement_representation
)

query = f"""
DELETE {{
{_SPARQL_STATEMENT_VARIABLE} ?p ?o .
?preference ?pp ?op .
}}
WHERE {{
{statement_representation}
{_SPARQL_STATEMENT_VARIABLE} ?p ?o .
OPTIONAL {{
?preference pav:derivedFrom {_SPARQL_STATEMENT_VARIABLE} .
?preference ?pp ?op .
}}
}}
"""

# Cleaning up the query
return _clean_sparql_representation(query)


def get_queries_for_remove_cleanup() -> List[SPARQLQuery]:
"""Gets SPARQL queries to delete dangling concepts and weight scales.

Returns:
List of SPARQL queries.
"""
query_delete_concepts = """
DELETE {
?concept ?p ?o .
} WHERE {
?concept a skos:Concept .
?concept ?p ?o .
FILTER NOT EXISTS { ?_1 ?_2 ?concept . }
}
"""
NoB0 marked this conversation as resolved.
Show resolved Hide resolved
query_delete_concepts = _clean_sparql_representation(query_delete_concepts)
query_delete_scales = """
DELETE {
?x ?p ?o .
} WHERE {
?x wo:scale ?_3 .
?x ?p ?o .
FILTER NOT EXISTS { ?_1 ?_2 ?x . }
}
"""
NoB0 marked this conversation as resolved.
Show resolved Hide resolved
query_delete_scales = _clean_sparql_representation(query_delete_scales)
return [query_delete_concepts, query_delete_scales]
11 changes: 11 additions & 0 deletions tests/pkg_api/test_pkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,14 @@ def test_get_statements_with_triple_conditions(
)
assert len(statements) == 2
assert retrieved_statement_with_concept in statements


def test_remove_statement(user_pkg: PKG, statement: PKGData) -> None:
"""Tests removing a statement."""
user_pkg.add_statement(statement)
statements = user_pkg.get_statements(statement)
assert len(statements) == 1

user_pkg.remove_statement(statement)
statements = user_pkg.get_statements(statement)
assert len(statements) == 0
66 changes: 65 additions & 1 deletion tests/pkg_api/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Tests for utility methods."""


import re
import uuid
from typing import Optional, Union
Expand Down Expand Up @@ -302,3 +301,68 @@ def test_get_query_for_conditional_get_statements(
assert utils.get_query_for_conditional_get_statements(
pkg_data_example.triple
) == strip_string(expected_query)


def test_get_query_for_remove_statement(
pkg_data_example: PKGData, statement_representation: str
) -> None:
"""Tests get_query_for_remove_statement method.

Args:
pkg_data_example: PKG data example.
statement_representation: Statement representation.
"""
statement_node_id = utils.get_statement_node_id(pkg_data_example)
statement_representation = statement_representation.replace(
statement_node_id, "?statement"
)
statement_representation = re.sub(
r'dc:description "[^"]+" ;', "", statement_representation
)
sparql_query = f"""
DELETE {{
?statement ?p ?o .
?preference ?pp ?op .
}}
WHERE {{
{statement_representation}
?statement ?p ?o .
OPTIONAL {{
?preference pav:derivedFrom ?statement .
?preference ?pp ?op .
}}
}}
"""

assert utils.get_query_for_remove_statement(
pkg_data_example
) == strip_string(sparql_query)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests for get_queries_for_remove_cleanup and for get_query_for_remove_preference are missing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a test for get_query_for_remove_preference. I am not sure a test is needed for get_queries_for_remove_cleanup because it appears to be already covered and it returns a string without alteration by a variable.



def test_get_query_for_remove_preference(
pkg_data_example: PKGData, statement_representation: str
) -> None:
"""Tests get_query_for_remove_preference method."""
statement_node_id = utils.get_statement_node_id(pkg_data_example)
statement_representation = statement_representation.replace(
statement_node_id, "?statement"
)
statement_representation = re.sub(
r'dc:description "[^"]+" ;', "", statement_representation
)

sparql_query = f"""
DELETE {{
?preference ?p ?o .
?subject wi:preference ?preference .
}}
WHERE {{
{statement_representation}
?subject wi:preference ?preference .
?preference pav:derivedFrom ?statement .
?preference ?p ?o .
}}
"""
assert utils.get_query_for_remove_preference(
pkg_data_example
) == strip_string(sparql_query)
Loading