vfbquery 0.4.1__py3-none-any.whl → 0.5.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. test/readme_parser.py +29 -27
  2. test/term_info_queries_test.py +46 -34
  3. test/test_dataset_template_queries.py +138 -0
  4. test/test_default_caching.py +89 -84
  5. test/test_examples_code.py +7 -0
  6. test/test_examples_diff.py +95 -172
  7. test/test_expression_overlaps.py +183 -0
  8. test/test_expression_pattern_fragments.py +123 -0
  9. test/test_images_neurons.py +152 -0
  10. test/test_images_that_develop_from.py +112 -0
  11. test/test_lineage_clones_in.py +190 -0
  12. test/test_nblast_queries.py +124 -0
  13. test/test_neuron_classes_fasciculating.py +187 -0
  14. test/test_neuron_inputs.py +193 -0
  15. test/test_neuron_neuron_connectivity.py +89 -0
  16. test/test_neuron_region_connectivity.py +117 -0
  17. test/test_neurons_part_here.py +203 -0
  18. test/test_new_owlery_queries.py +282 -0
  19. test/test_publication_transgene_queries.py +101 -0
  20. test/test_query_performance.py +739 -0
  21. test/test_similar_morphology.py +177 -0
  22. test/test_tracts_nerves_innervating.py +188 -0
  23. test/test_transcriptomics.py +223 -0
  24. vfbquery/__init__.py +47 -35
  25. vfbquery/cached_functions.py +772 -131
  26. vfbquery/neo4j_client.py +120 -0
  27. vfbquery/owlery_client.py +463 -0
  28. vfbquery/solr_cache_integration.py +34 -30
  29. vfbquery/solr_fetcher.py +1 -1
  30. vfbquery/solr_result_cache.py +338 -36
  31. vfbquery/term_info_queries.py +1 -1
  32. vfbquery/vfb_queries.py +2969 -627
  33. vfbquery-0.5.1.dist-info/METADATA +2806 -0
  34. vfbquery-0.5.1.dist-info/RECORD +40 -0
  35. vfbquery-0.4.1.dist-info/METADATA +0 -1315
  36. vfbquery-0.4.1.dist-info/RECORD +0 -19
  37. {vfbquery-0.4.1.dist-info → vfbquery-0.5.1.dist-info}/LICENSE +0 -0
  38. {vfbquery-0.4.1.dist-info → vfbquery-0.5.1.dist-info}/WHEEL +0 -0
  39. {vfbquery-0.4.1.dist-info → vfbquery-0.5.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test suite for LineageClonesIn query.
4
+
5
+ Tests the query that finds lineage clones that overlap with a synaptic neuropil.
6
+ This implements the LineageClonesIn query from the VFB XMI specification.
7
+
8
+ Test cases:
9
+ 1. Query execution with known neuropil
10
+ 2. Schema generation and validation
11
+ 3. Term info integration
12
+ 4. Preview results validation
13
+ 5. Cache functionality
14
+ """
15
+
16
+ import unittest
17
+ import sys
18
+ import os
19
+
20
+ # Add the src directory to the path
21
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
22
+
23
+ from vfbquery.vfb_queries import (
24
+ get_lineage_clones_in,
25
+ LineageClonesIn_to_schema,
26
+ get_term_info
27
+ )
28
+
29
+
30
+ class LineageClonesInTest(unittest.TestCase):
31
+ """Test suite for LineageClonesIn query"""
32
+
33
+ def setUp(self):
34
+ """Set up test fixtures"""
35
+ # Example synaptic neuropil: adult antennal lobe (FBbt_00007401)
36
+ self.test_neuropil = "FBbt_00007401" # antennal lobe
37
+
38
+ def test_query_execution(self):
39
+ """Test that the query executes successfully"""
40
+ print(f"\n=== Testing LineageClonesIn query execution ===")
41
+
42
+ # Execute the query
43
+ result = get_lineage_clones_in(self.test_neuropil, return_dataframe=False, limit=5)
44
+
45
+ # Validate result structure
46
+ self.assertIsNotNone(result, "Query should return a result")
47
+ self.assertIsInstance(result, dict, "Result should be a dictionary")
48
+
49
+ # Check for expected keys
50
+ if result:
51
+ print(f"Query returned {len(result.get('data', []))} results")
52
+
53
+ # Validate data structure
54
+ if 'data' in result and len(result['data']) > 0:
55
+ first_result = result['data'][0]
56
+ self.assertIn('id', first_result, "Result should contain 'id' field")
57
+ self.assertIn('label', first_result, "Result should contain 'label' field")
58
+ print(f"First result: {first_result.get('label', 'N/A')} ({first_result.get('id', 'N/A')})")
59
+ else:
60
+ print("No results found (this is OK if no clones overlap this neuropil)")
61
+
62
+ def test_schema_generation(self):
63
+ """Test schema function generates correct structure"""
64
+ print(f"\n=== Testing LineageClonesIn schema generation ===")
65
+
66
+ test_name = "Test Neuropil"
67
+ test_takes = {"short_form": self.test_neuropil}
68
+
69
+ schema = LineageClonesIn_to_schema(test_name, test_takes)
70
+
71
+ # Validate schema structure
72
+ self.assertIsNotNone(schema, "Schema should not be None")
73
+ self.assertEqual(schema.query, "LineageClonesIn", "Query name should match")
74
+ self.assertEqual(schema.label, f"Lineage clones found in {test_name}", "Label should be formatted correctly")
75
+ self.assertEqual(schema.function, "get_lineage_clones_in", "Function name should match")
76
+ self.assertEqual(schema.preview, 5, "Preview should be 10")
77
+
78
+ # Check preview columns
79
+ expected_columns = ["id", "label", "tags", "thumbnail"]
80
+ self.assertEqual(schema.preview_columns, expected_columns, f"Preview columns should be {expected_columns}")
81
+
82
+ print(f"Schema generated successfully: {schema.label}")
83
+
84
+ def test_term_info_integration(self):
85
+ """Test that query appears in term info for appropriate terms"""
86
+ print(f"\n=== Testing term info integration ===")
87
+
88
+ # Get term info for a synaptic neuropil
89
+ term_info = get_term_info(self.test_neuropil, preview=False)
90
+
91
+ self.assertIsNotNone(term_info, "Term info should not be None")
92
+ self.assertIn("Queries", term_info, "Term info should contain Queries")
93
+
94
+ # Check if our query is present
95
+ queries = term_info.get("Queries", [])
96
+ query_names = [q.get('query') for q in queries]
97
+
98
+ print(f"Available queries for {self.test_neuropil}: {query_names}")
99
+
100
+ # For synaptic neuropils, this query should be available
101
+ if "Synaptic_neuropil" in term_info.get("SuperTypes", []) or \
102
+ "Synaptic_neuropil_domain" in term_info.get("SuperTypes", []):
103
+ self.assertIn("LineageClonesIn", query_names,
104
+ "LineageClonesIn should be available for Synaptic_neuropil")
105
+ print("✓ Query correctly appears for Synaptic_neuropil type")
106
+ else:
107
+ print(f"Warning: {self.test_neuropil} does not have Synaptic_neuropil type")
108
+ print(f"SuperTypes: {term_info.get('SuperTypes', [])}")
109
+
110
+ def test_preview_results(self):
111
+ """Test that preview results are properly formatted"""
112
+ print(f"\n=== Testing preview results ===")
113
+
114
+ # Get term info with preview enabled
115
+ term_info = get_term_info(self.test_neuropil, preview=True)
116
+
117
+ self.assertIsNotNone(term_info, "Term info should not be None")
118
+
119
+ # Find our query in the results
120
+ queries = term_info.get("Queries", [])
121
+ clones_query = None
122
+ for q in queries:
123
+ if q.get('query') == "LineageClonesIn":
124
+ clones_query = q
125
+ break
126
+
127
+ if clones_query:
128
+ print(f"Found LineageClonesIn query")
129
+
130
+ # Check if preview_results exist
131
+ if clones_query.get('preview_results'):
132
+ preview = clones_query['preview_results']
133
+ data_key = 'data' if 'data' in preview else 'rows'
134
+ print(f"Preview contains {len(preview.get(data_key, []))} results")
135
+
136
+ # Validate preview structure
137
+ self.assertIn(data_key, preview, f"Preview should contain '{data_key}' key")
138
+ self.assertIn('headers', preview, "Preview should contain 'headers' key")
139
+
140
+ # Check first result if available
141
+ if preview.get(data_key) and len(preview[data_key]) > 0:
142
+ first_result = preview[data_key][0]
143
+ print(f"First preview result: {first_result.get('label', 'N/A')}")
144
+
145
+ # Validate required fields
146
+ self.assertIn('id', first_result, "Preview result should have 'id'")
147
+ self.assertIn('label', first_result, "Preview result should have 'label'")
148
+ else:
149
+ print("No preview results available (this is OK if no clones overlap this neuropil)")
150
+ else:
151
+ print("LineageClonesIn query not found in term info")
152
+
153
+ def test_with_different_neuropils(self):
154
+ """Test with multiple synaptic neuropil types"""
155
+ print(f"\n=== Testing with different neuropils ===")
156
+
157
+ test_neuropils = [
158
+ ("FBbt_00007401", "antennal lobe"),
159
+ ("FBbt_00003982", "medulla"),
160
+ ("FBbt_00003679", "mushroom body"),
161
+ ]
162
+
163
+ for neuropil_id, neuropil_name in test_neuropils:
164
+ print(f"\nTesting {neuropil_name} ({neuropil_id})...")
165
+
166
+ try:
167
+ result = get_lineage_clones_in(neuropil_id, return_dataframe=False, limit=3)
168
+
169
+ if result and 'data' in result:
170
+ print(f" ✓ Query successful, found {len(result['data'])} results")
171
+ else:
172
+ print(f" ✓ Query successful, no results found")
173
+
174
+ except Exception as e:
175
+ print(f" ✗ Query failed: {str(e)}")
176
+ # Don't fail the test, just log the error
177
+ # raise
178
+
179
+
180
+ def run_tests():
181
+ """Run the test suite"""
182
+ suite = unittest.TestLoader().loadTestsFromTestCase(LineageClonesInTest)
183
+ runner = unittest.TextTestRunner(verbosity=2)
184
+ result = runner.run(suite)
185
+ return result.wasSuccessful()
186
+
187
+
188
+ if __name__ == '__main__':
189
+ success = run_tests()
190
+ sys.exit(0 if success else 1)
@@ -0,0 +1,124 @@
1
+ """
2
+ Unit tests for NBLAST similarity queries.
3
+
4
+ This tests all 6 NBLAST-related queries:
5
+ 1. get_similar_morphology - NBLAST matches
6
+ 2. get_similar_morphology_part_of - NBLASTexp to expression patterns
7
+ 3. get_similar_morphology_part_of_exp - Reverse NBLASTexp
8
+ 4. get_similar_morphology_nb - NeuronBridge matches
9
+ 5. get_similar_morphology_nb_exp - NeuronBridge for expression patterns
10
+ 6. get_similar_morphology_userdata - User upload NBLAST from SOLR
11
+
12
+ Test terms:
13
+ - VFB_00101567 - has NBLAST matches
14
+ - VFB_00050000 - example neuron with NBLASTexp
15
+ """
16
+
17
+ import unittest
18
+ import sys
19
+ import os
20
+
21
+ # Add src directory to path
22
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
23
+
24
+ from vfbquery.vfb_queries import (
25
+ get_similar_morphology,
26
+ get_similar_morphology_part_of,
27
+ get_similar_morphology_part_of_exp,
28
+ get_similar_morphology_nb,
29
+ get_similar_morphology_nb_exp,
30
+ get_similar_morphology_userdata,
31
+ SimilarMorphologyTo_to_schema,
32
+ SimilarMorphologyToPartOf_to_schema,
33
+ SimilarMorphologyToPartOfexp_to_schema,
34
+ SimilarMorphologyToNB_to_schema,
35
+ SimilarMorphologyToNBexp_to_schema,
36
+ SimilarMorphologyToUserData_to_schema
37
+ )
38
+
39
+
40
+ class NBLASTQueriesTest(unittest.TestCase):
41
+ """Test cases for NBLAST similarity queries"""
42
+
43
+ def setUp(self):
44
+ """Set up test fixtures"""
45
+ self.nblast_term = 'VFB_00101567' # Has NBLAST matches
46
+ self.neuron_term = 'VFB_00050000' # Example neuron
47
+
48
+ def test_get_similar_morphology(self):
49
+ """Test get_similar_morphology query"""
50
+ result = get_similar_morphology(self.nblast_term, return_dataframe=True, limit=5)
51
+ self.assertIsNotNone(result, "Result should not be None")
52
+
53
+ import pandas as pd
54
+ if isinstance(result, pd.DataFrame) and len(result) > 0:
55
+ print(f"\n✓ Found {len(result)} NBLAST matches for {self.nblast_term}")
56
+ self.assertIn('id', result.columns)
57
+ self.assertIn('label', result.columns)
58
+ self.assertIn('score', result.columns)
59
+
60
+ def test_get_similar_morphology_formatted(self):
61
+ """Test get_similar_morphology with formatted output"""
62
+ result = get_similar_morphology(self.nblast_term, return_dataframe=False, limit=3)
63
+ self.assertIsNotNone(result)
64
+
65
+ if isinstance(result, dict):
66
+ self.assertIn('headers', result)
67
+ self.assertIn('rows', result)
68
+
69
+ def test_get_similar_morphology_part_of(self):
70
+ """Test get_similar_morphology_part_of (NBLASTexp)"""
71
+ result = get_similar_morphology_part_of(self.neuron_term, return_dataframe=True, limit=5)
72
+ self.assertIsNotNone(result)
73
+
74
+ import pandas as pd
75
+ if isinstance(result, pd.DataFrame) and len(result) > 0:
76
+ print(f"\n✓ Found {len(result)} NBLASTexp matches for {self.neuron_term}")
77
+
78
+ def test_get_similar_morphology_part_of_exp(self):
79
+ """Test get_similar_morphology_part_of_exp (reverse NBLASTexp)"""
80
+ result = get_similar_morphology_part_of_exp(self.neuron_term, return_dataframe=True, limit=5)
81
+ self.assertIsNotNone(result)
82
+
83
+ import pandas as pd
84
+ if isinstance(result, pd.DataFrame) and len(result) > 0:
85
+ print(f"\n✓ Found {len(result)} reverse NBLASTexp matches")
86
+
87
+ def test_get_similar_morphology_nb(self):
88
+ """Test get_similar_morphology_nb (NeuronBridge)"""
89
+ result = get_similar_morphology_nb(self.neuron_term, return_dataframe=True, limit=5)
90
+ self.assertIsNotNone(result)
91
+
92
+ import pandas as pd
93
+ if isinstance(result, pd.DataFrame) and len(result) > 0:
94
+ print(f"\n✓ Found {len(result)} NeuronBridge matches")
95
+ self.assertIn('score', result.columns)
96
+
97
+ def test_get_similar_morphology_nb_exp(self):
98
+ """Test get_similar_morphology_nb_exp (NeuronBridge for expression)"""
99
+ result = get_similar_morphology_nb_exp(self.neuron_term, return_dataframe=True, limit=5)
100
+ self.assertIsNotNone(result)
101
+
102
+ def test_schema_functions_exist(self):
103
+ """Test that all NBLAST schema functions exist and are callable"""
104
+ schema_functions = [
105
+ SimilarMorphologyTo_to_schema,
106
+ SimilarMorphologyToPartOf_to_schema,
107
+ SimilarMorphologyToPartOfexp_to_schema,
108
+ SimilarMorphologyToNB_to_schema,
109
+ SimilarMorphologyToNBexp_to_schema,
110
+ SimilarMorphologyToUserData_to_schema
111
+ ]
112
+
113
+ for func in schema_functions:
114
+ self.assertTrue(callable(func), f"{func.__name__} should be callable")
115
+
116
+ def test_empty_results_handling(self):
117
+ """Test that queries handle empty results gracefully"""
118
+ # Use a term unlikely to have NBLAST matches
119
+ result = get_similar_morphology('FBbt_00000001', return_dataframe=True, limit=5)
120
+ self.assertIsNotNone(result)
121
+
122
+
123
+ if __name__ == '__main__':
124
+ unittest.main()
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test suite for NeuronClassesFasciculatingHere query.
4
+
5
+ Tests the query that finds neuron classes that fasciculate with (run along) tracts or nerves.
6
+ This implements the NeuronClassesFasciculatingHere query from the VFB XMI specification.
7
+
8
+ Test cases:
9
+ 1. Query execution with known tract
10
+ 2. Schema generation and validation
11
+ 3. Term info integration
12
+ 4. Preview results validation
13
+ 5. Cache functionality
14
+ """
15
+
16
+ import unittest
17
+ import sys
18
+ import os
19
+
20
+ # Add the src directory to the path
21
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
22
+
23
+ from vfbquery.vfb_queries import (
24
+ get_neuron_classes_fasciculating_here,
25
+ NeuronClassesFasciculatingHere_to_schema,
26
+ get_term_info
27
+ )
28
+
29
+
30
+ class NeuronClassesFasciculatingTest(unittest.TestCase):
31
+ """Test suite for NeuronClassesFasciculatingHere query"""
32
+
33
+ def setUp(self):
34
+ """Set up test fixtures"""
35
+ # Example tract/nerve: broad root (FBbt_00003987) - a neuron projection bundle
36
+ self.test_tract = "FBbt_00003987" # broad root
37
+
38
+ def test_query_execution(self):
39
+ """Test that the query executes successfully"""
40
+ print(f"\n=== Testing NeuronClassesFasciculatingHere query execution ===")
41
+
42
+ # Execute the query
43
+ result = get_neuron_classes_fasciculating_here(self.test_tract, return_dataframe=False, limit=5)
44
+
45
+ # Validate result structure
46
+ self.assertIsNotNone(result, "Query should return a result")
47
+ self.assertIsInstance(result, dict, "Result should be a dictionary")
48
+
49
+ # Check for expected keys
50
+ if result:
51
+ print(f"Query returned {len(result.get('data', []))} results")
52
+
53
+ # Validate data structure
54
+ if 'data' in result and len(result['data']) > 0:
55
+ first_result = result['data'][0]
56
+ self.assertIn('id', first_result, "Result should contain 'id' field")
57
+ self.assertIn('label', first_result, "Result should contain 'label' field")
58
+ print(f"First result: {first_result.get('label', 'N/A')} ({first_result.get('id', 'N/A')})")
59
+
60
+ def test_schema_generation(self):
61
+ """Test schema function generates correct structure"""
62
+ print(f"\n=== Testing NeuronClassesFasciculatingHere schema generation ===")
63
+
64
+ test_name = "Test Tract"
65
+ test_takes = {"short_form": self.test_tract}
66
+
67
+ schema = NeuronClassesFasciculatingHere_to_schema(test_name, test_takes)
68
+
69
+ # Validate schema structure
70
+ self.assertIsNotNone(schema, "Schema should not be None")
71
+ self.assertEqual(schema.query, "NeuronClassesFasciculatingHere", "Query name should match")
72
+ self.assertEqual(schema.label, f"Neurons fasciculating in {test_name}", "Label should be formatted correctly")
73
+ self.assertEqual(schema.function, "get_neuron_classes_fasciculating_here", "Function name should match")
74
+ self.assertEqual(schema.preview, 5, "Preview should be 5")
75
+
76
+ # Check preview columns
77
+ expected_columns = ["id", "label", "tags", "thumbnail"]
78
+ self.assertEqual(schema.preview_columns, expected_columns, f"Preview columns should be {expected_columns}")
79
+
80
+ print(f"Schema generated successfully: {schema.label}")
81
+
82
+ def test_term_info_integration(self):
83
+ """Test that query appears in term info for appropriate terms"""
84
+ print(f"\n=== Testing term info integration ===")
85
+
86
+ # Get term info for a tract/nerve
87
+ term_info = get_term_info(self.test_tract, preview=False)
88
+
89
+ self.assertIsNotNone(term_info, "Term info should not be None")
90
+ self.assertIn("Queries", term_info, "Term info should contain Queries")
91
+
92
+ # Check if our query is present
93
+ queries = term_info.get("Queries", [])
94
+ query_names = [q.get('query') for q in queries]
95
+
96
+ print(f"Available queries for {self.test_tract}: {query_names}")
97
+
98
+ # For tracts/nerves (Neuron_projection_bundle), this query should be available
99
+ if "Neuron_projection_bundle" in term_info.get("SuperTypes", []):
100
+ self.assertIn("NeuronClassesFasciculatingHere", query_names,
101
+ "NeuronClassesFasciculatingHere should be available for Neuron_projection_bundle")
102
+ print("✓ Query correctly appears for Neuron_projection_bundle type")
103
+ else:
104
+ print(f"Warning: {self.test_tract} does not have Neuron_projection_bundle type")
105
+ print(f"SuperTypes: {term_info.get('SuperTypes', [])}")
106
+
107
+ def test_preview_results(self):
108
+ """Test that preview results are properly formatted"""
109
+ print(f"\n=== Testing preview results ===")
110
+
111
+ # Get term info with preview enabled
112
+ term_info = get_term_info(self.test_tract, preview=True)
113
+
114
+ self.assertIsNotNone(term_info, "Term info should not be None")
115
+
116
+ # Find our query in the results
117
+ queries = term_info.get("Queries", [])
118
+ fasciculating_query = None
119
+ for q in queries:
120
+ if q.get('query') == "NeuronClassesFasciculatingHere":
121
+ fasciculating_query = q
122
+ break
123
+
124
+ if fasciculating_query:
125
+ print(f"Found NeuronClassesFasciculatingHere query")
126
+
127
+ # Check if preview_results exist
128
+ if fasciculating_query.get('preview_results'):
129
+ preview = fasciculating_query['preview_results']
130
+ data_key = 'data' if 'data' in preview else 'rows'
131
+ print(f"Preview contains {len(preview.get(data_key, []))} results")
132
+
133
+ # Validate preview structure
134
+ self.assertIn(data_key, preview, f"Preview should contain '{data_key}' key")
135
+ self.assertIn('headers', preview, "Preview should contain 'headers' key")
136
+
137
+ # Check first result if available
138
+ if preview.get(data_key) and len(preview[data_key]) > 0:
139
+ first_result = preview[data_key][0]
140
+ print(f"First preview result: {first_result.get('label', 'N/A')}")
141
+
142
+ # Validate required fields
143
+ self.assertIn('id', first_result, "Preview result should have 'id'")
144
+ self.assertIn('label', first_result, "Preview result should have 'label'")
145
+ else:
146
+ print("No preview results available (this is OK if no matching neurons exist)")
147
+ else:
148
+ print("NeuronClassesFasciculatingHere query not found in term info")
149
+
150
+ def test_with_different_tracts(self):
151
+ """Test with multiple tract/nerve types"""
152
+ print(f"\n=== Testing with different tracts/nerves ===")
153
+
154
+ test_tracts = [
155
+ ("FBbt_00003987", "broad root"),
156
+ ("FBbt_00007354", "adult antenno-subesophageal tract"),
157
+ ("FBbt_00003985", "adult medial antennal lobe tract"),
158
+ ]
159
+
160
+ for tract_id, tract_name in test_tracts:
161
+ print(f"\nTesting {tract_name} ({tract_id})...")
162
+
163
+ try:
164
+ result = get_neuron_classes_fasciculating_here(tract_id, return_dataframe=False, limit=3)
165
+
166
+ if result and 'data' in result:
167
+ print(f" ✓ Query successful, found {len(result['data'])} results")
168
+ else:
169
+ print(f" ✓ Query successful, no results found")
170
+
171
+ except Exception as e:
172
+ print(f" ✗ Query failed: {str(e)}")
173
+ # Don't fail the test, just log the error
174
+ # raise
175
+
176
+
177
+ def run_tests():
178
+ """Run the test suite"""
179
+ suite = unittest.TestLoader().loadTestsFromTestCase(NeuronClassesFasciculatingTest)
180
+ runner = unittest.TextTestRunner(verbosity=2)
181
+ result = runner.run(suite)
182
+ return result.wasSuccessful()
183
+
184
+
185
+ if __name__ == '__main__':
186
+ success = run_tests()
187
+ sys.exit(0 if success else 1)