j-js commited on
Commit
a5d5985
·
verified ·
1 Parent(s): 3adacda

Update explainers/explainer_percent.py

Browse files
Files changed (1) hide show
  1. explainers/explainer_percent.py +221 -48
explainers/explainer_percent.py CHANGED
@@ -1,65 +1,238 @@
1
  import re
2
- from models import ExplainerResult
3
 
4
 
5
- def explain_percent_question(text: str) -> ExplainerResult | None:
6
- t = text.lower()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
- has_percent = "%" in t or "percent" in t or "percentage" in t
9
- if not has_percent:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  return None
11
 
12
- givens = []
13
- relationships = []
14
- constraints = []
15
- trap_notes = []
16
 
17
- percent_matches = re.findall(r"\b\d+(?:\.\d+)?%", t)
18
- number_matches = re.findall(r"\b\d+(?:\.\d+)?\b", t)
 
 
 
19
 
20
- if percent_matches:
21
- givens.append(f"A percentage is given: {', '.join(percent_matches[:3])}")
22
- else:
23
- givens.append("A percent relationship is described")
 
 
24
 
25
- if "of" in t:
26
- givens.append("The wording uses an 'of' relationship")
 
 
 
27
 
28
- if number_matches:
29
- givens.append(f"Numbers mentioned: {', '.join(number_matches[:5])}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
- relationships.append("This is a part-percent-whole relationship")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- if "increase" in t:
34
- relationships.append("This may involve a percent increase from an original value")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
- if "decrease" in t:
37
- relationships.append("This may involve a percent decrease from an original value")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
- if "original" in t or "base" in t:
40
- constraints.append("Identify the original/base value carefully")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- asks_for = "the missing value in the percent relationship"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
- trap_notes.extend([
45
- "Do not confuse the part with the whole",
46
- "Check whether you are solving for percent, part, or whole",
47
- "Make sure the percent is applied to the correct base value",
48
- ])
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
- return ExplainerResult(
51
- understood=True,
52
- topic="percent",
53
- asks_for=asks_for,
54
- givens=givens,
55
- constraints=constraints,
56
- relationships=relationships,
57
- needed_concepts=[
58
- "percent equation",
59
- "part = percent × whole",
60
- "base value identification",
61
- ],
62
- trap_notes=trap_notes,
63
- strategy_hint="Translate the sentence into a percent equation before calculating anything.",
64
- plain_english="The question is describing a percent relationship and asking you to identify a missing value within that structure.",
65
- )
 
1
  import re
2
+ from explainer_types import ExplainerResult, ExplainerScaffold
3
 
4
 
5
+ _PERCENT_PATTERNS = [
6
+ r"\bpercent\b",
7
+ r"\bpercentage\b",
8
+ r"\b%\b",
9
+ r"\bwhat percent\b",
10
+ r"\bpercent of\b",
11
+ r"\bpercent greater than\b",
12
+ r"\bpercent less than\b",
13
+ r"\bincrease(?:d)? by \d+ ?%\b",
14
+ r"\bdecrease(?:d)? by \d+ ?%\b",
15
+ r"\bdiscount\b",
16
+ r"\bmarkup\b",
17
+ r"\binterest\b",
18
+ r"\btax\b",
19
+ r"\btip\b",
20
+ r"\bprofit margin\b",
21
+ r"\bcommission\b",
22
+ r"\bpart of\b",
23
+ r"\bof a number\b",
24
+ ]
25
 
26
+
27
+ def _looks_like_percent_question(text: str) -> bool:
28
+ low = (text or "").lower()
29
+ if "%" in low:
30
+ return True
31
+ return any(re.search(p, low) for p in _PERCENT_PATTERNS)
32
+
33
+
34
+ def _infer_percent_subtype(text: str) -> str:
35
+ low = (text or "").lower()
36
+
37
+ if any(k in low for k in ["increase", "decrease", "increased", "decreased", "percent change", "changed by"]):
38
+ return "percent_change"
39
+ if any(k in low for k in ["discount", "sale", "off", "markdown"]):
40
+ return "discount"
41
+ if any(k in low for k in ["interest", "simple interest", "compound interest"]):
42
+ return "interest"
43
+ if any(k in low for k in ["tax", "vat", "tip", "gratuity"]):
44
+ return "tax_tip"
45
+ if any(k in low for k in ["what percent", "is what percent of", "as a percent of"]):
46
+ return "percent_of_percent"
47
+ if any(k in low for k in ["percent of", "of a number", "% of"]):
48
+ return "basic_percent"
49
+ return "generic_percent"
50
+
51
+
52
+ def explain_percent(text: str):
53
+ if not _looks_like_percent_question(text):
54
  return None
55
 
56
+ subtype = _infer_percent_subtype(text)
 
 
 
57
 
58
+ result = ExplainerResult(
59
+ understood=True,
60
+ topic="percent",
61
+ summary="This is a percent problem. The key is to identify which quantity is the base, which is the compared amount, and what role the percent is playing."
62
+ )
63
 
64
+ scaffold = ExplainerScaffold(
65
+ concept="Percent problems are built around a base amount, a compared amount, and a rate per hundred.",
66
+ ask="Decide which quantity is the whole/base, which quantity is the part or changed amount, and whether the question is asking for a percent, a value, or a new total.",
67
+ target="Translate the wording into the correct percent relationship before doing any arithmetic.",
68
+ answer_hidden=True,
69
+ )
70
 
71
+ teaching_points = [
72
+ "Percent means per hundred, so it should usually be converted to a decimal or fraction before setting up the relationship.",
73
+ "Most percent questions reduce to one of a small number of templates, so identifying the template matters more than rushing into calculation.",
74
+ "The biggest source of error is choosing the wrong base quantity."
75
+ ]
76
 
77
+ if subtype == "basic_percent":
78
+ scaffold.setup_actions = [
79
+ "Label the known quantities as part, whole, and percent.",
80
+ "Convert the percent to a decimal or fraction.",
81
+ "Match the statement to the relationship: part = percent × whole."
82
+ ]
83
+ scaffold.intermediate_steps = [
84
+ "If the whole is unknown, assign it a variable.",
85
+ "Substitute the known values into the relationship.",
86
+ "Solve only after the structure is correct."
87
+ ]
88
+ scaffold.first_move = "Start by identifying the whole and the part."
89
+ scaffold.next_hint = "Once those roles are clear, write one equation connecting part, percent, and whole."
90
+ scaffold.variables_to_define = [
91
+ "Let x represent the unknown quantity if either the part or whole is missing."
92
+ ]
93
+ scaffold.equations_to_form = [
94
+ "part = percent × whole"
95
+ ]
96
+ scaffold.common_traps = [
97
+ "Using the wrong quantity as the whole.",
98
+ "Using 40 instead of 0.40.",
99
+ "Treating the wording as a percent change problem when it is only a basic percent-of problem."
100
+ ]
101
 
102
+ elif subtype == "percent_of_percent":
103
+ scaffold.setup_actions = [
104
+ "Identify the comparison being made.",
105
+ "Place the compared quantity over the base quantity.",
106
+ "Convert the resulting fraction to a percent."
107
+ ]
108
+ scaffold.intermediate_steps = [
109
+ "Make sure the denominator is the reference/base amount.",
110
+ "Simplify the fraction if helpful.",
111
+ "Only convert to percent at the end."
112
+ ]
113
+ scaffold.first_move = "Ask: percent of what?"
114
+ scaffold.next_hint = "Use the base quantity as the denominator in the fraction before converting."
115
+ scaffold.equations_to_form = [
116
+ "percent = (part / whole) × 100"
117
+ ]
118
+ scaffold.common_traps = [
119
+ "Reversing numerator and denominator.",
120
+ "Using the larger number automatically as the denominator instead of the stated base.",
121
+ "Forgetting to multiply by 100 at the end if the question asks for a percent."
122
+ ]
123
 
124
+ elif subtype == "percent_change":
125
+ scaffold.setup_actions = [
126
+ "Identify the original value and the new value.",
127
+ "Find the amount of change first.",
128
+ "Use the original value as the base in the percent-change formula."
129
+ ]
130
+ scaffold.intermediate_steps = [
131
+ "Compute change = new − original.",
132
+ "Place that change over the original value.",
133
+ "Convert the result to a percent."
134
+ ]
135
+ scaffold.first_move = "Find the amount of increase or decrease before thinking about the percent."
136
+ scaffold.next_hint = "After that, divide by the original amount, not the new amount."
137
+ scaffold.equations_to_form = [
138
+ "percent change = (new − original) / original × 100"
139
+ ]
140
+ scaffold.common_traps = [
141
+ "Using the new amount as the denominator.",
142
+ "Skipping the change step and comparing the wrong two quantities.",
143
+ "Losing track of whether the result should be described as an increase or decrease."
144
+ ]
145
 
146
+ elif subtype == "discount":
147
+ scaffold.setup_actions = [
148
+ "Identify the original price and the discount rate.",
149
+ "Find the discount amount from the original price.",
150
+ "Subtract the discount from the original price if the question asks for the sale price."
151
+ ]
152
+ scaffold.intermediate_steps = [
153
+ "Treat the listed price as the base.",
154
+ "Compute the discount amount separately from the final price.",
155
+ "Check whether the question asks for discount amount or discounted price."
156
+ ]
157
+ scaffold.first_move = "Use the original price as the percent base."
158
+ scaffold.next_hint = "Find the discount amount first, then decide whether to stop or subtract."
159
+ scaffold.equations_to_form = [
160
+ "discount = rate × original price",
161
+ "sale price = original price − discount"
162
+ ]
163
+ scaffold.common_traps = [
164
+ "Applying the discount to the wrong quantity.",
165
+ "Giving the discount amount when the question asks for the final price.",
166
+ "Treating multiple discounts as one simple combined subtraction without checking the structure."
167
+ ]
168
 
169
+ elif subtype == "interest":
170
+ scaffold.setup_actions = [
171
+ "Identify principal, rate, and time.",
172
+ "Determine whether the question is simple interest or compound interest.",
173
+ "Set up the matching formula structure."
174
+ ]
175
+ scaffold.intermediate_steps = [
176
+ "For simple interest, keep the principal fixed.",
177
+ "For compound interest, recognize that the base changes from period to period.",
178
+ "Check whether the question asks for interest earned or final amount."
179
+ ]
180
+ scaffold.first_move = "Work out whether the interest is simple or compound."
181
+ scaffold.next_hint = "Then identify which quantity stays fixed and which quantity grows."
182
+ scaffold.common_traps = [
183
+ "Using a simple-interest structure on a compound-interest question.",
184
+ "Confusing interest earned with total amount.",
185
+ "Ignoring the time unit matching between rate and period."
186
+ ]
187
 
188
+ elif subtype == "tax_tip":
189
+ scaffold.setup_actions = [
190
+ "Identify the pre-tax or pre-tip amount.",
191
+ "Use that original amount as the base.",
192
+ "Compute the tax or tip amount before adding if a total is required."
193
+ ]
194
+ scaffold.intermediate_steps = [
195
+ "Separate the added charge from the final amount.",
196
+ "Check whether multiple percentages are applied independently or sequentially.",
197
+ "Make sure the question wants the fee amount or the full total."
198
+ ]
199
+ scaffold.first_move = "Use the original listed amount as the base unless the wording clearly says otherwise."
200
+ scaffold.next_hint = "Find the added percent amount first, then combine only if the question asks for the total."
201
+ scaffold.equations_to_form = [
202
+ "added amount = rate × base amount",
203
+ "total = base amount + added amount"
204
+ ]
205
+ scaffold.common_traps = [
206
+ "Using the final total as the percent base.",
207
+ "Stopping at the tax or tip amount when the question asks for the total bill.",
208
+ "Blending separate percentages together without checking the wording."
209
+ ]
210
 
211
+ else:
212
+ scaffold.setup_actions = [
213
+ "Identify the base quantity.",
214
+ "Identify the compared quantity or changed amount.",
215
+ "Match the wording to a standard percent relationship."
216
+ ]
217
+ scaffold.intermediate_steps = [
218
+ "Convert the percent appropriately.",
219
+ "Build one clear relationship before solving.",
220
+ "Check whether the question asks for a value, a rate, or a final amount."
221
+ ]
222
+ scaffold.first_move = "Work out what the percent is being taken of."
223
+ scaffold.next_hint = "Once the base quantity is clear, write the relationship before calculating."
224
+ scaffold.common_traps = [
225
+ "Choosing the wrong base.",
226
+ "Not converting the percent properly.",
227
+ "Using the right numbers in the wrong relationship."
228
+ ]
229
 
230
+ result.teaching_points = teaching_points
231
+ result.scaffold = scaffold
232
+ result.meta = {
233
+ "intent": "explain_question",
234
+ "bridge_ready": True,
235
+ "hint_style": "step_ready",
236
+ "subtype": subtype,
237
+ }
238
+ return result