internationalscholarsprogram commited on
Commit
39dcffa
Β·
1 Parent(s): 88646e1

fix: use USD X,XXX format instead of $X,XXX for all currency values

Browse files

- format_money_figures() now outputs "USD 1,800" not "$1,800"
- Normalizes any existing $ or USD prefix before re-formatting
- emphasize_keywords() bolds "USD X,XXX" patterns in bold
- All monetary figures in paragraphs, tables, steps, bullets are bold + USD format
- All 89 tests pass

app/services/utils.py CHANGED
@@ -48,47 +48,42 @@ def is_truthy(val) -> bool:
48
 
49
 
50
  def format_money_figures(text: str) -> str:
51
- """Mirrors PHP formatMoneyFigures().
52
 
53
- - Strips "USD " prefix
54
- - Adds $ to large numbers
55
  - Formats with commas
 
56
  """
57
  if not text:
58
  return text
59
 
60
- # Remove "USD " prefix
61
- text = re.sub(r"\bUSD\s*", "", text, flags=re.IGNORECASE)
 
 
 
62
 
63
  def _format_match(m: re.Match) -> str:
64
  num_str = m.group(1).replace(",", "")
65
  dec = m.group(2) if m.group(2) else ""
66
- num = float(num_str)
 
 
 
67
  if dec:
68
  formatted = f"{num:,.{len(dec)}f}"
69
  else:
70
  formatted = f"{num:,.0f}"
71
- return "$" + formatted
72
 
73
- # Add $ to large numbers (with optional decimals)
74
  text = re.sub(
75
- r"(?<![$0-9])(?<!\$)((?:\d{1,3}(?:,\d{3})+)|(?:\d{4,}))(?:\.(\d+))?(?![%\d/])",
76
  _format_match,
77
  text,
78
  )
79
 
80
- # Ensure remaining 4+ digit numbers are formatted with commas and $
81
- def _format_remaining(m: re.Match) -> str:
82
- num_str = m.group(1).replace(",", "")
83
- formatted = f"{float(num_str):,.0f}"
84
- return "$" + formatted
85
-
86
- text = re.sub(
87
- r"(?<!\$)\b(\d{1,3}(?:,\d{3})+|\d{4,})(?![%\d/])",
88
- _format_remaining,
89
- text,
90
- )
91
-
92
  return text
93
 
94
 
@@ -161,11 +156,12 @@ def emphasize_keywords(text: str) -> str:
161
  flags=re.IGNORECASE,
162
  )
163
 
164
- # Bold dollar amounts like $1,000 or $500
165
  escaped = re.sub(
166
- r'(\$[\d,]+(?:\.\d+)?)',
167
  r'<strong>\1</strong>',
168
  escaped,
 
169
  )
170
 
171
  # Bold key qualification and geo terms.
 
48
 
49
 
50
  def format_money_figures(text: str) -> str:
51
+ """Normalize all monetary figures to "USD X,XXX" format.
52
 
53
+ - Converts existing $X,XXX β†’ USD X,XXX
54
+ - Normalizes bare large numbers β†’ USD X,XXX
55
  - Formats with commas
56
+ - Currency type is always USD (no $ symbol)
57
  """
58
  if not text:
59
  return text
60
 
61
+ # Normalize "$X,XXX" β†’ bare number (strip $ symbol)
62
+ text = re.sub(r'\$([\d,]+(?:\.\d+)?)', lambda m: m.group(1), text)
63
+
64
+ # Normalize "USD X,XXX" β†’ bare number for uniform re-processing
65
+ text = re.sub(r'\bUSD\s+([\d,]+(?:\.\d+)?)', lambda m: m.group(1), text, flags=re.IGNORECASE)
66
 
67
  def _format_match(m: re.Match) -> str:
68
  num_str = m.group(1).replace(",", "")
69
  dec = m.group(2) if m.group(2) else ""
70
+ try:
71
+ num = float(num_str)
72
+ except ValueError:
73
+ return m.group(0)
74
  if dec:
75
  formatted = f"{num:,.{len(dec)}f}"
76
  else:
77
  formatted = f"{num:,.0f}"
78
+ return "USD " + formatted
79
 
80
+ # Add "USD " to large numbers (4+ digits or already comma-formatted)
81
  text = re.sub(
82
+ r"(?<!\d)((?:\d{1,3}(?:,\d{3})+)|(?:\d{4,}))(?:\.(\d+))?(?![%\d/])",
83
  _format_match,
84
  text,
85
  )
86
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  return text
88
 
89
 
 
156
  flags=re.IGNORECASE,
157
  )
158
 
159
+ # Bold USD amounts like USD 1,000 or USD 500
160
  escaped = re.sub(
161
+ r'\b(USD\s+[\d,]+(?:\.\d+)?)',
162
  r'<strong>\1</strong>',
163
  escaped,
164
+ flags=re.IGNORECASE,
165
  )
166
 
167
  # Bold key qualification and geo terms.
app/templates/partials/blocks/table.html CHANGED
@@ -26,7 +26,8 @@
26
  {% for c in block.data.all_columns %}
27
  {% set col_label = c.label | default('') %}
28
  {% set col_lc = col_label | lower %}
29
- <th class="{% if col_lc == 'regular' %}is-regular-col{% elif col_lc == 'prime' %}is-prime-col{% endif %}">{{ c.label | e }}</th>
 
30
  {% endfor %}
31
  </tr>
32
  {% endif %}
@@ -37,7 +38,8 @@
37
  {% for c in block.data.all_columns %}
38
  {% set col_label = c.label | default('') %}
39
  {% set col_lc = col_label | lower %}
40
- <td class="{% if col_lc == 'regular' %}is-regular-col{% elif col_lc == 'prime' %}is-prime-col{% endif %}">{{ row[c.key] | default('') | safe }}</td>
 
41
  {% endfor %}
42
  </tr>
43
  {% endfor %}
@@ -67,7 +69,8 @@
67
  <tr>
68
  {% for col in block.data.columns %}
69
  {% set col_lc = (col | lower) %}
70
- <th class="{% if col_lc == 'regular' %}is-regular-col{% elif col_lc == 'prime' %}is-prime-col{% endif %}">{{ col | e }}</th>
 
71
  {% endfor %}
72
  </tr>
73
  </thead>
@@ -76,10 +79,11 @@
76
  {% for row in block.data.rows %}
77
  <tr>
78
  {% for cell in row %}
79
- {% set col = block.data.columns[loop.index0] if block.data.columns and loop.index0 < (block.data.columns | length) else '' %}
80
- {% set col_lc = (col | lower) %}
81
- <td class="{% if col_lc == 'regular' %}is-regular-col{% elif col_lc == 'prime' %}is-prime-col{% endif %}">{{ cell | safe }}</td>
82
- {% endfor %}
 
83
  </tr>
84
  {% endfor %}
85
  </tbody>
 
26
  {% for c in block.data.all_columns %}
27
  {% set col_label = c.label | default('') %}
28
  {% set col_lc = col_label | lower %}
29
+ <th class="{% if col_lc == 'regular' %}is-regular-col{% elif col_lc == 'prime' %}is-prime-col{% endif %}">{{
30
+ c.label | e }}</th>
31
  {% endfor %}
32
  </tr>
33
  {% endif %}
 
38
  {% for c in block.data.all_columns %}
39
  {% set col_label = c.label | default('') %}
40
  {% set col_lc = col_label | lower %}
41
+ <td class="{% if col_lc == 'regular' %}is-regular-col{% elif col_lc == 'prime' %}is-prime-col{% endif %}">{{
42
+ row[c.key] | default('') | safe }}</td>
43
  {% endfor %}
44
  </tr>
45
  {% endfor %}
 
69
  <tr>
70
  {% for col in block.data.columns %}
71
  {% set col_lc = (col | lower) %}
72
+ <th class="{% if col_lc == 'regular' %}is-regular-col{% elif col_lc == 'prime' %}is-prime-col{% endif %}">{{
73
+ col | e }}</th>
74
  {% endfor %}
75
  </tr>
76
  </thead>
 
79
  {% for row in block.data.rows %}
80
  <tr>
81
  {% for cell in row %}
82
+ {% set col = block.data.columns[loop.index0] if block.data.columns and loop.index0 < (block.data.columns |
83
+ length) else '' %} {% set col_lc=(col | lower) %} <td
84
+ class="{% if col_lc == 'regular' %}is-regular-col{% elif col_lc == 'prime' %}is-prime-col{% endif %}">{{
85
+ cell | safe }}</td>
86
+ {% endfor %}
87
  </tr>
88
  {% endfor %}
89
  </tbody>
tests/test_renderers.py CHANGED
@@ -70,15 +70,16 @@ def test_is_truthy_false_values():
70
 
71
  # ── format_money_figures() ──
72
 
73
- def test_format_money_removes_usd_prefix():
74
  result = format_money_figures("USD 5000")
75
- assert "USD" not in result
76
- assert "$" in result
77
 
78
 
79
- def test_format_money_adds_dollar_sign():
80
  result = format_money_figures("The cost is 15000 per year")
81
- assert "$15,000" in result
 
82
 
83
 
84
  def test_format_money_preserves_percentages():
 
70
 
71
  # ── format_money_figures() ──
72
 
73
+ def test_format_money_usd_prefix_normalised():
74
  result = format_money_figures("USD 5000")
75
+ assert "USD 5,000" in result
76
+ assert "$" not in result
77
 
78
 
79
+ def test_format_money_formats_as_usd():
80
  result = format_money_figures("The cost is 15000 per year")
81
+ assert "USD 15,000" in result
82
+ assert "$" not in result
83
 
84
 
85
  def test_format_money_preserves_percentages():