Last active
July 22, 2025 17:05
-
-
Save berezovskyi/b52cd429b55ac161d001f27f5a51f2f6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env -S uv run | |
| # /// script | |
| # dependencies = [ | |
| # "rdflib>=7.0.0", | |
| # ] | |
| # /// | |
| """ | |
| RDF Requirements Decomposition Analyzer | |
| This script parses RDF data containing requirements and calculates | |
| the average number of requirements into which a requirement is decomposed. | |
| """ | |
| from rdflib import Graph, Namespace, URIRef | |
| from rdflib.namespace import RDF, RDFS, DCTERMS | |
| # Define namespaces | |
| RM = Namespace("http://open-services.net/ns/rm#") | |
| OSLC = Namespace("http://open-services.net/ns/core#") | |
| def main(): | |
| # Create a new RDF graph | |
| g = Graph() | |
| # Bind prefixes for prettier output | |
| g.bind("rdf", RDF) | |
| g.bind("rdfs", RDFS) | |
| g.bind("dcterms", DCTERMS) | |
| g.bind("oslc", OSLC) | |
| g.bind("rm", RM) | |
| # The RDF data as a string | |
| rdf_data = """ | |
| @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>. | |
| @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. | |
| @prefix xsd: <http://www.w3.org/2001/XMLSchema#>. | |
| @prefix dcterms: <http://purl.org/dc/terms/>. | |
| @prefix oslc: <http://open-services.net/ns/core#>. | |
| <https://localhost:7000/?a=SDOC-HIGH-REQS-DECOMP> <http://open-services.net/ns/rm#decomposes> <https://localhost:7000/?a=SDOC-HIGH-REQS-MANAGEMENT>; | |
| dcterms:description "StrictDoc shall support requirement decomposition."^^rdf:XMLLiteral; | |
| dcterms:identifier "SDOC-HIGH-REQS-DECOMP"; | |
| dcterms:title "Requirements decomposition"^^rdf:XMLLiteral; | |
| a <http://open-services.net/ns/rm#Requirement>. | |
| <https://localhost:7000/?a=SDOC-HIGH-REQS-MANAGEMENT> dcterms:description "StrictDoc shall enable requirements management."^^rdf:XMLLiteral; | |
| dcterms:identifier "SDOC-HIGH-REQS-MANAGEMENT"; | |
| dcterms:title "Requirements management"^^rdf:XMLLiteral; | |
| a <http://open-services.net/ns/rm#Requirement>. | |
| """ | |
| # Parse the RDF data | |
| g.parse(data=rdf_data, format="turtle") | |
| print("π RDF Requirements Decomposition Analysis") | |
| print("=" * 50) | |
| # First, let's see what requirements we have | |
| print("\nπ Requirements found:") | |
| requirements_query = """ | |
| SELECT ?requirement ?identifier ?title WHERE { | |
| ?requirement a <http://open-services.net/ns/rm#Requirement> ; | |
| dcterms:identifier ?identifier ; | |
| dcterms:title ?title . | |
| } | |
| ORDER BY ?identifier | |
| """ | |
| results = g.query(requirements_query) | |
| all_requirements = set() | |
| for row in results: | |
| req_uri, identifier, title = row | |
| all_requirements.add(req_uri) | |
| print(f" β’ {identifier}: {title}") | |
| print(f"\nTotal requirements: {len(all_requirements)}") | |
| # Now calculate decomposition statistics using SPARQL | |
| print("\nπ Decomposition Analysis:") | |
| # SPARQL query to find decomposition relationships | |
| decomposition_query = """ | |
| SELECT ?parent ?child ?parent_id ?child_id WHERE { | |
| ?child <http://open-services.net/ns/rm#decomposes> ?parent . | |
| ?parent a <http://open-services.net/ns/rm#Requirement> ; | |
| dcterms:identifier ?parent_id . | |
| ?child a <http://open-services.net/ns/rm#Requirement> ; | |
| dcterms:identifier ?child_id . | |
| } | |
| """ | |
| decomposition_results = g.query(decomposition_query) | |
| print("\nπ Decomposition relationships:") | |
| decomposition_list = list(decomposition_results) | |
| if not decomposition_list: | |
| print(" No decomposition relationships found.") | |
| print("\nπ Average decompositions per requirement: 0") | |
| return | |
| for row in decomposition_list: | |
| parent, child, parent_id, child_id = row | |
| print(f" β’ {child_id} decomposes {parent_id}") | |
| # Use SPARQL to calculate statistics | |
| print(f"\nπ Statistics (calculated via SPARQL):") | |
| # Count total requirements | |
| total_reqs_query = """ | |
| SELECT (COUNT(DISTINCT ?req) AS ?total) WHERE { | |
| ?req a <http://open-services.net/ns/rm#Requirement> . | |
| } | |
| """ | |
| total_reqs_result = g.query(total_reqs_query) | |
| total_requirements = int(list(total_reqs_result)[0][0]) | |
| # Count total decomposition relationships | |
| total_decomp_query = """ | |
| SELECT (COUNT(*) AS ?total) WHERE { | |
| ?child <http://open-services.net/ns/rm#decomposes> ?parent . | |
| ?parent a <http://open-services.net/ns/rm#Requirement> . | |
| ?child a <http://open-services.net/ns/rm#Requirement> . | |
| } | |
| """ | |
| total_decomp_result = g.query(total_decomp_query) | |
| total_decompositions = int(list(total_decomp_result)[0][0]) | |
| # Count requirements that have decompositions (are decomposed by others) | |
| reqs_with_decomp_query = """ | |
| SELECT (COUNT(DISTINCT ?parent) AS ?count) WHERE { | |
| ?child <http://open-services.net/ns/rm#decomposes> ?parent . | |
| ?parent a <http://open-services.net/ns/rm#Requirement> . | |
| ?child a <http://open-services.net/ns/rm#Requirement> . | |
| } | |
| """ | |
| reqs_with_decomp_result = g.query(reqs_with_decomp_query) | |
| requirements_with_decompositions = int(list(reqs_with_decomp_result)[0][0]) | |
| # Calculate average using SPARQL (average decompositions per requirement) | |
| # This calculates the average number of decompositions across ALL requirements | |
| avg_all_query = f""" | |
| SELECT ({total_decompositions} / {total_requirements} AS ?avg) WHERE {{ | |
| # This is a calculated value | |
| }} | |
| """ | |
| # Calculate average for requirements that have decompositions | |
| avg_with_decomp_query = f""" | |
| SELECT ({total_decompositions} / {requirements_with_decompositions} AS ?avg) WHERE {{ | |
| # This is a calculated value | |
| }} | |
| """ if requirements_with_decompositions > 0 else None | |
| print(f" β’ Total requirements: {total_requirements}") | |
| print(f" β’ Requirements with decompositions: {requirements_with_decompositions}") | |
| print(f" β’ Total decomposition relationships: {total_decompositions}") | |
| # Calculate averages | |
| avg_decompositions_all = total_decompositions / total_requirements if total_requirements > 0 else 0 | |
| avg_decompositions_with_decomp = (total_decompositions / requirements_with_decompositions | |
| if requirements_with_decompositions > 0 else 0) | |
| print(f"\nπ― Results (SPARQL-calculated):") | |
| print(f" β’ Average decompositions per requirement (all): {avg_decompositions_all:.2f}") | |
| print(f" β’ Average decompositions per requirement (with decompositions only): {avg_decompositions_with_decomp:.2f}") | |
| # Show detailed breakdown using SPARQL | |
| detailed_query = """ | |
| SELECT ?parent_id (COUNT(?child) AS ?decomp_count) WHERE { | |
| ?child <http://open-services.net/ns/rm#decomposes> ?parent . | |
| ?parent a <http://open-services.net/ns/rm#Requirement> ; | |
| dcterms:identifier ?parent_id . | |
| ?child a <http://open-services.net/ns/rm#Requirement> . | |
| } | |
| GROUP BY ?parent_id ?parent | |
| ORDER BY ?parent_id | |
| """ | |
| detailed_results = g.query(detailed_query) | |
| detailed_list = list(detailed_results) | |
| if detailed_list: | |
| print(f"\nπ Detailed breakdown (via SPARQL GROUP BY):") | |
| for row in detailed_list: | |
| parent_id, count = row | |
| print(f" β’ {parent_id}: {count} decomposition(s)") | |
| # Demonstrate a more advanced SPARQL calculation | |
| print(f"\n㪠Advanced SPARQL Analysis:") | |
| # Calculate average using a single SPARQL query with aggregation | |
| advanced_avg_query = """ | |
| SELECT | |
| (COUNT(?decomp) AS ?total_decompositions) | |
| (COUNT(DISTINCT ?req) AS ?total_requirements) | |
| (COUNT(?decomp) / COUNT(DISTINCT ?req) AS ?avg_per_req) | |
| (COUNT(DISTINCT ?parent) AS ?reqs_with_decomps) | |
| (COUNT(?decomp) / COUNT(DISTINCT ?parent) AS ?avg_per_decomposed_req) | |
| WHERE { | |
| ?req a <http://open-services.net/ns/rm#Requirement> . | |
| OPTIONAL { | |
| ?decomp <http://open-services.net/ns/rm#decomposes> ?parent . | |
| ?parent a <http://open-services.net/ns/rm#Requirement> . | |
| FILTER(?req = ?parent) | |
| } | |
| } | |
| """ | |
| advanced_results = g.query(advanced_avg_query) | |
| advanced_row = list(advanced_results)[0] | |
| sparql_total_decomp = int(advanced_row[0]) | |
| sparql_total_reqs = int(advanced_row[1]) | |
| sparql_avg_per_req = float(advanced_row[2]) | |
| sparql_reqs_with_decomp = int(advanced_row[3]) | |
| sparql_avg_per_decomposed = float(advanced_row[4]) if sparql_reqs_with_decomp > 0 else 0 | |
| print(f" β’ SPARQL calculated - Total decompositions: {sparql_total_decomp}") | |
| print(f" β’ SPARQL calculated - Total requirements: {sparql_total_reqs}") | |
| print(f" β’ SPARQL calculated - Average per requirement: {sparql_avg_per_req:.2f}") | |
| print(f" β’ SPARQL calculated - Requirements with decompositions: {sparql_reqs_with_decomp}") | |
| print(f" β’ SPARQL calculated - Average per decomposed requirement: {sparql_avg_per_decomposed:.2f}") | |
| if __name__ == "__main__": | |
| main() | |
| # Sample output: | |
| # π RDF Requirements Decomposition Analysis | |
| # ================================================== | |
| # π Requirements found: | |
| # β’ SDOC-HIGH-REQS-DECOMP: Requirements decomposition | |
| # β’ SDOC-HIGH-REQS-MANAGEMENT: Requirements management | |
| # Total requirements: 2 | |
| # π Decomposition Analysis: | |
| # π Decomposition relationships: | |
| # β’ SDOC-HIGH-REQS-DECOMP decomposes SDOC-HIGH-REQS-MANAGEMENT | |
| # π Statistics (calculated via SPARQL): | |
| # β’ Total requirements: 2 | |
| # β’ Requirements with decompositions: 1 | |
| # β’ Total decomposition relationships: 1 | |
| # π― Results (SPARQL-calculated): | |
| # β’ Average decompositions per requirement (all): 0.50 | |
| # β’ Average decompositions per requirement (with decompositions only): 1.00 | |
| # π Detailed breakdown (via SPARQL GROUP BY): | |
| # β’ SDOC-HIGH-REQS-MANAGEMENT: 1 decomposition(s) | |
| # π¬ Advanced SPARQL Analysis: | |
| # β’ SPARQL calculated - Total decompositions: 1 | |
| # β’ SPARQL calculated - Total requirements: 2 | |
| # β’ SPARQL calculated - Average per requirement: 0.50 | |
| # β’ SPARQL calculated - Requirements with decompositions: 1 | |
| # β’ SPARQL calculated - Average per decomposed requirement: 1.00 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment