Skip to content

Commit d3ce6b7

Browse files
committed
opt(table): Update database properties & rows sorting
1 parent 8152987 commit d3ce6b7

File tree

3 files changed

+148
-26
lines changed

3 files changed

+148
-26
lines changed

notion_page.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,8 @@ def _parse_collection(self, page_blocks: typing.List[PageBaseBlock], block):
10501050
# Parse column weight configuration from description
10511051
description = database.get('description', [])
10521052
column_weights = self._parse_column_weights(description)
1053+
if column_weights:
1054+
print(f"[Database Parsing] Found property-order config: {column_weights}")
10531055

10541056
# Prepare HTTP client
10551057
http_client_req = None
@@ -1076,13 +1078,25 @@ def _parse_collection(self, page_blocks: typing.List[PageBaseBlock], block):
10761078

10771079
# Determine sorting logic using utility class
10781080
from utils.database_utils import DatabaseColumnOrderingUtils
1079-
sorts = DatabaseColumnOrderingUtils.get_database_sorts(properties)
1080-
1081-
# If properties are empty (common with Inline Databases), we miss schema info for sorting.
1082-
# Perform a pre-query to infer schema from the first row to check for 'Order' column.
1083-
is_default_sort = len(sorts) == 1 and sorts[0].get('timestamp') == 'created_time'
1081+
1082+
# Check for page-order configuration in description first
1083+
description_text = NotionUtils.get_plain_text(description)
1084+
page_order_sorts = DatabaseColumnOrderingUtils.parse_page_order(description_text)
1085+
1086+
if page_order_sorts:
1087+
print(f"[Database Parsing] Found page-order config: {page_order_sorts}")
1088+
sorts = page_order_sorts
1089+
# Bypassing pre-query if page-order is configured
1090+
is_default_sort = False
1091+
else:
1092+
sorts = DatabaseColumnOrderingUtils.get_database_sorts(properties)
1093+
print(f"[Database Parsing] Using schema-based/default sorts: {sorts}")
1094+
# If properties are empty (common with Inline Databases), we miss schema info for sorting.
1095+
# Perform a pre-query to infer schema from the first row to check for 'Order' column.
1096+
is_default_sort = len(sorts) == 1 and sorts[0].get('timestamp') == 'created_time'
10841097

10851098
if not properties and is_default_sort:
1099+
print(f"[Database Parsing] Schema properties empty & default sort detected. Attempting Pre-query inference...")
10861100
# Pre-query one row to check schema
10871101
pre_query_body = {
10881102
"page_size": 1,
@@ -1096,9 +1110,14 @@ def _parse_collection(self, page_blocks: typing.List[PageBaseBlock], block):
10961110
if pre_results:
10971111
# Infer properties from row data
10981112
inferred_props = pre_results[0].get('properties', {})
1113+
print(f"[Database Parsing] Inferred properties from Pre-query: {list(inferred_props.keys())}")
1114+
# Re-calculate sorts with inferred properties
10991115
sorts = DatabaseColumnOrderingUtils.get_database_sorts(inferred_props)
1116+
print(f"[Database Parsing] Recalculated sorts after inference: {sorts}")
1117+
else:
1118+
print("[Database Parsing] Pre-query returned no results. Cannot infer schema.")
11001119
except Exception as e:
1101-
print(f"Failed to infer schema for sorting: {e}")
1120+
print(f"[Database Parsing] Failed to infer schema for sorting: {e}")
11021121

11031122
query_body = {
11041123
"sorts": sorts
@@ -1108,16 +1127,22 @@ def _parse_collection(self, page_blocks: typing.List[PageBaseBlock], block):
11081127

11091128
response.raise_for_status()
11101129
results = response.json().get('results', [])
1130+
print(f"[Database Parsing] Query returned {len(results)} rows.")
11111131

11121132
headers = list(properties.keys()) if properties else []
11131133
if not headers and results:
11141134
# Infer headers from the first row if schema properties are empty
11151135
first_row_props = results[0].get('properties', {})
11161136
headers = list(first_row_props.keys())
1137+
print(f"[Database Parsing] Inferred headers from first row: {headers}")
11171138

11181139
# Apply column ordering
11191140
if headers and column_weights:
1141+
# Only apply weighted ordering if explicitly configured
11201142
headers = self._sort_columns_by_weight(headers, column_weights)
1143+
print(f"[Database Parsing] Applied column weights. Final headers: {headers}")
1144+
elif headers:
1145+
print(f"[Database Parsing] No column weights config. Using default headers: {headers}")
11211146

11221147
rows = []
11231148
for page in results:

tests/notion-sdk-py-official-apis/test_database_parsing.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,5 +248,57 @@ def test_database_row_sorting(self):
248248

249249
print("✓ All row sorting tests passed")
250250

251+
def test_page_order_configuration(self):
252+
"""Test parsing of page-order configuration."""
253+
print("\n[Test] Page Order Configuration")
254+
from utils.database_utils import DatabaseColumnOrderingUtils
255+
256+
# Test 1: Simple page-order
257+
desc_1 = "Some text\npage-order: Order1, Created\nMore text"
258+
sorts_1 = DatabaseColumnOrderingUtils.parse_page_order(desc_1)
259+
print(f"Test 1 - Simple: {sorts_1}")
260+
self.assertEqual(len(sorts_1), 2)
261+
self.assertEqual(sorts_1[0]['property'], 'Order1')
262+
self.assertEqual(sorts_1[1]['timestamp'], 'created_time')
263+
264+
# Test 2: Multi-line and case insensitive
265+
desc_2 = "page-order: Priority, Name, CREATED"
266+
sorts_2 = DatabaseColumnOrderingUtils.parse_page_order(desc_2)
267+
print(f"Test 2 - Multi keys: {sorts_2}")
268+
self.assertEqual(len(sorts_2), 3)
269+
self.assertEqual(sorts_2[0]['property'], 'Priority')
270+
self.assertEqual(sorts_2[2]['timestamp'], 'created_time')
271+
272+
# Test 3: No config
273+
desc_3 = "Just description"
274+
sorts_3 = DatabaseColumnOrderingUtils.parse_page_order(desc_3)
275+
self.assertIsNone(sorts_3)
276+
277+
print("✓ All page-order tests passed")
278+
279+
def test_column_hiding(self):
280+
"""Test hiding columns via negative weights."""
281+
print("\n[Test] Column Hiding")
282+
from utils.database_utils import DatabaseColumnOrderingUtils
283+
284+
headers = ['Title', 'Tags', 'Secret', 'Date']
285+
weights = {
286+
'Title': 100,
287+
'Secret': -1, # Should be hidden
288+
'Tags': 50
289+
}
290+
291+
sorted_headers = DatabaseColumnOrderingUtils.sort_columns_by_weight(headers, weights)
292+
print(f"Original: {headers}")
293+
print(f"Weights: {weights}")
294+
print(f"Result: {sorted_headers}")
295+
296+
self.assertNotIn('Secret', sorted_headers)
297+
self.assertEqual(sorted_headers[0], 'Title')
298+
self.assertEqual(sorted_headers[1], 'Tags')
299+
self.assertIn('Date', sorted_headers) # Default weight 0
300+
301+
print("✓ Column hiding tests passed")
302+
251303
if __name__ == '__main__':
252304
unittest.main()

utils/database_utils.py

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -57,38 +57,83 @@ def parse_column_weights(description_text: str) -> Optional[Dict[str, int]]:
5757

5858
return weights if weights else None
5959

60+
@staticmethod
61+
def parse_page_order(description_text: str) -> Optional[List[Dict[str, str]]]:
62+
"""
63+
Parses page-order configuration from description text.
64+
Format: page-order: Order1, Order2, Created
65+
Returned format: Notion API sorts parameter
66+
"""
67+
if not description_text:
68+
return None
69+
70+
pattern = r'(?:^|\n)\s*page-order:\s*(.+?)(?:\n|$)'
71+
match = re.search(pattern, description_text, re.IGNORECASE | re.MULTILINE)
72+
73+
if not match:
74+
return None
75+
76+
config_line = match.group(1).strip()
77+
if not config_line:
78+
return None
79+
80+
sorts = []
81+
parts = [p.strip() for p in config_line.split(',')]
82+
83+
for part in parts:
84+
if not part:
85+
continue
86+
87+
if part.lower() == 'created':
88+
sorts.append({
89+
"timestamp": "created_time",
90+
"direction": "ascending"
91+
})
92+
else:
93+
sorts.append({
94+
"property": part,
95+
"direction": "ascending"
96+
})
97+
98+
return sorts if sorts else None
99+
60100
@staticmethod
61101
def sort_columns_by_weight(headers: List[str], weights: Optional[Dict[str, int]]) -> List[str]:
62-
"""Sort columns based on weight configuration.
102+
"""
103+
Sorts the headers based on the weights.
63104
64-
Columns with higher weights appear first. Columns not in the weights dict
65-
get a default weight of 0. Stable sort is used to preserve original order
66-
for columns with the same weight.
105+
If weights is provided, headers are sorted descending by weight.
106+
Headers not in weights get a default weight of 0.
107+
Negative weights mean the column should be hidden (removed).
67108
68109
Args:
69110
headers: List of column names
70-
weights: dict of {column_name: weight}, or None
111+
weights: dict of {column_name: weight}
71112
72113
Returns:
73-
Sorted list of column names (higher weight first)
114+
Sorted and filtered list of column names
74115
75-
Examples:
76-
>>> headers = ['ID', 'Status', 'Name', 'Age', 'Email']
77-
>>> weights = {'Name': 100, 'Email': 90, 'Status': 80}
78-
>>> DatabaseColumnOrderingUtils.sort_columns_by_weight(headers, weights)
79-
['Name', 'Email', 'Status', 'ID', 'Age']
116+
Example:
117+
>>> headers = ['Title', 'Tags', 'Secret']
118+
>>> weights = {'Title': 100, 'Secret': -1}
119+
>>> result = sort_columns_by_weight(headers, weights)
120+
['Title', 'Tags']
80121
"""
81122
if not weights:
82123
return headers
83-
84-
# Assign weight to each column (default 0 for unspecified)
85-
def get_weight(col_name: str) -> int:
86-
return weights.get(col_name, 0)
87-
88-
# Sort by weight descending (higher weight = earlier position)
89-
# Use stable sort to preserve original order for same-weight columns
90-
sorted_headers = sorted(headers, key=get_weight, reverse=True)
91-
124+
125+
# Default weight for unassigned headers is 0
126+
def get_weight(header):
127+
return weights.get(header, 0)
128+
129+
# Filter out headers with negative weights
130+
visible_headers = [
131+
h for h in headers
132+
if get_weight(h) >= 0
133+
]
134+
135+
# Use a stable sort to keep relative order of items with same weight
136+
sorted_headers = sorted(visible_headers, key=get_weight, reverse=True)
92137
return sorted_headers
93138

94139
@staticmethod

0 commit comments

Comments
 (0)