From 3d63bb08c62afccc405a15132631cb903ff0c269 Mon Sep 17 00:00:00 2001 From: alec_dev Date: Wed, 5 Nov 2025 11:42:17 -0600 Subject: [PATCH 1/2] Fix blank_nulls wrapping numeric CatalogNumber cast instead of raw column --- specifyweb/backend/stored_queries/format.py | 23 ++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/specifyweb/backend/stored_queries/format.py b/specifyweb/backend/stored_queries/format.py index 65fc4388c30..d9e3abc9381 100644 --- a/specifyweb/backend/stored_queries/format.py +++ b/specifyweb/backend/stored_queries/format.py @@ -200,15 +200,17 @@ def make_expr(self, formatter, aggregator, previous_tables) + raw_expr = new_expr else: new_query, table, model, specify_field = query.build_join( specify_model, orm_table, formatter_field_spec.join_path) new_expr = getattr(table, specify_field.name) - + raw_expr = new_expr + if self.format_expr: new_expr = self._fieldformat(formatter_field_spec.table, formatter_field_spec.get_field(), new_expr) - if 'trimzeros' in fieldNodeAttrib: + if 'trimzeros' in fieldNodeAttrib and fieldNodeAttrib['trimzeros'] == 'true': # new_expr = case( # [(new_expr.op('REGEXP')('^-?[0-9]+(\\.[0-9]+)?$'), cast(new_expr, types.Numeric(65)))], # else_=new_expr @@ -225,7 +227,22 @@ def make_expr(self, if 'sep' in fieldNodeAttrib: new_expr = concat(fieldNodeAttrib['sep'], new_expr) - return new_query, blank_nulls(new_expr) if do_blank_null else new_expr, formatter_field_spec + if do_blank_null: + sf = formatter_field_spec.get_field() + is_catalog_num = ( + sf is not None + and sf is CollectionObject_model.get_field('catalogNumber') + ) + if ( + is_catalog_num + and self.numeric_catalog_number + and all_numeric_catnum_formats(self.collection) + ): + return new_query, blank_nulls(raw_expr), formatter_field_spec + + return new_query, blank_nulls(new_expr), formatter_field_spec + + return new_query, new_expr, formatter_field_spec def objformat(self, query: QueryConstruct, orm_table: SQLTable, formatter_name, cycle_detector=[]) -> tuple[QueryConstruct, blank_nulls]: From 40125dd16a2956c0021aadb864b65fc315f4c18a Mon Sep 17 00:00:00 2001 From: alec_dev Date: Wed, 5 Nov 2025 14:27:32 -0600 Subject: [PATCH 2/2] fix custom formatters --- specifyweb/backend/stored_queries/format.py | 50 ++++++++++++--------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/specifyweb/backend/stored_queries/format.py b/specifyweb/backend/stored_queries/format.py index d9e3abc9381..b3b42b8d831 100644 --- a/specifyweb/backend/stored_queries/format.py +++ b/specifyweb/backend/stored_queries/format.py @@ -207,25 +207,33 @@ def make_expr(self, new_expr = getattr(table, specify_field.name) raw_expr = new_expr + # Apply field-level formatting, which may include numeric cast if self.format_expr: - new_expr = self._fieldformat(formatter_field_spec.table, formatter_field_spec.get_field(), new_expr) - - if 'trimzeros' in fieldNodeAttrib and fieldNodeAttrib['trimzeros'] == 'true': - # new_expr = case( - # [(new_expr.op('REGEXP')('^-?[0-9]+(\\.[0-9]+)?$'), cast(new_expr, types.Numeric(65)))], - # else_=new_expr - # ) - numeric_str = cast(cast(new_expr, types.Numeric(65)), types.String()) - new_expr = case( - (new_expr.op('REGEXP')('^-?[0-9]+(\\.[0-9]+)?$'), numeric_str), - else_=cast(new_expr, types.String()), - ) - - if 'format' in fieldNodeAttrib: - new_expr = self.pseudo_sprintf(fieldNodeAttrib['format'], new_expr) - - if 'sep' in fieldNodeAttrib: - new_expr = concat(fieldNodeAttrib['sep'], new_expr) + new_expr = self._fieldformat( + formatter_field_spec.table, + formatter_field_spec.get_field(), + new_expr + ) + + # Helper function to apply only string-ish transforms with no numeric casts + def apply_stringish(expr): + e = expr + if fieldNodeAttrib.get('trimzeros') == 'true': + numeric_str = cast(cast(e, types.Numeric(65)), types.String()) + e = case( + (e.op('REGEXP')('^-?[0-9]+(\\.[0-9]+)?$'), numeric_str), + else_=cast(e, types.String()), + ) + fmt = fieldNodeAttrib.get('format') + if fmt is not None: + e = self.pseudo_sprintf(fmt, e) + sep = fieldNodeAttrib.get('sep') + if sep is not None: + e = concat(sep, e) + return e + + stringish_expr = apply_stringish(raw_expr) + formatted_expr = apply_stringish(new_expr) if do_blank_null: sf = formatter_field_spec.get_field() @@ -238,11 +246,11 @@ def make_expr(self, and self.numeric_catalog_number and all_numeric_catnum_formats(self.collection) ): - return new_query, blank_nulls(raw_expr), formatter_field_spec + return new_query, blank_nulls(stringish_expr), formatter_field_spec - return new_query, blank_nulls(new_expr), formatter_field_spec + return new_query, blank_nulls(formatted_expr), formatter_field_spec - return new_query, new_expr, formatter_field_spec + return new_query, formatted_expr, formatter_field_spec def objformat(self, query: QueryConstruct, orm_table: SQLTable, formatter_name, cycle_detector=[]) -> tuple[QueryConstruct, blank_nulls]: