diff --git a/backend/src/alphapulse/calendar/archives.py b/backend/src/alphapulse/calendar/archives.py index 3c4faae..6603540 100644 --- a/backend/src/alphapulse/calendar/archives.py +++ b/backend/src/alphapulse/calendar/archives.py @@ -1,7 +1,7 @@ """官方当期公告解析;归档内容不被标为公布时就已采集的原始版本。""" import re from dataclasses import dataclass -from datetime import UTC, datetime +from datetime import UTC, datetime, timedelta from parsel import Selector @@ -20,6 +20,37 @@ class Announcement: values: dict +def parse_umich_report(text: str) -> Announcement: + """取当期 PDF 最后一列;前十二列只是报告当时所列历史值。""" + text = compact(text) + # 2026-02 PDF 的首字母 P 是单独的文字块,提取后与 reliminary 之间有换行。 + heading = re.search(r"(?:P\s*reliminary results from the (" + MONTH_PATTERN + r") (20\d\d) survey|(" + + MONTH_PATTERN + r") (20\d\d) survey results)\s+(" + MONTH_PATTERN + r" \d{1,2}, 20\d\d)", text) + if heading is None: + raise ValueError("密歇根历史报告缺少统计期、发布阶段或正式日期") + month, year = (heading[1], heading[2]) if heading[1] else (heading[3], heading[4]) + period = f"{year}-{MONTHS[month]:02d}" + stage = "preliminary" if heading[1] else "final" + at = datetime.strptime(heading[5], "%B %d, %Y").replace(hour=10, tzinfo=EASTERN).astimezone(UTC) + table = re.search(r"((?:[A-Z][a-z]{2}\s+){13})((?:(?:20\d\d|Prelim)\s+){13})" + r"Index of Consumer Sentiment\s+((?:\d+(?:\.\d+)?\s+){13})Current Economic Conditions", text) + if table is None: + raise ValueError("密歇根历史报告缺少完整的消费者信心表头或数值行") + months, years, values = (group.split() for group in table.groups()) + current = datetime(int(year), MONTHS[month], 1) + previous = (current - timedelta(days=1)).strftime("%Y-%m") + # 初值最后一列表头用 Prelim 代替年份;其他十二列必须与统计月份逐列一致。 + for index in range(13): + expected_month = (current.month - 1 + index) % 12 + 1 + expected_year = current.year - 1 + (current.month - 1 + index) // 12 + expected_label = "Prelim" if index == 12 and stage == "preliminary" else str(expected_year) + if months[index] != datetime(2000, expected_month, 1).strftime("%b") or years[index] != expected_label: + raise ValueError("密歇根历史报告表头与统计期或发布阶段不符") + return Announcement(at, period, stage, {"umich_sentiment": { + "actual": number(values[-1]), "previous": number(values[-2]), "previous_basis": f"{previous} 官方上月终值", + }}) + + def signed(match: re.Match) -> str: sign = "-" if match[1].lower() in {"declined", "decreased", "fell", "edged down", "moved down"} else "" return number(sign + match[2]) diff --git a/backend/src/alphapulse/calendar/umich.py b/backend/src/alphapulse/calendar/umich.py index 822f7ad..855aaa5 100644 --- a/backend/src/alphapulse/calendar/umich.py +++ b/backend/src/alphapulse/calendar/umich.py @@ -29,6 +29,28 @@ def document_links(body: str, base: str, labels: set[str]) -> dict[str, str]: return {label: links[label] for label in labels if label in links} +def report_links(body: str, year: int) -> dict[tuple[str, str], str]: + """年度索引只取初值、终值报告,不把数据手册或专题报告当作当期公告。""" + sel = Selector(text=body, type="html") + if compact(sel.css("#report_heading .title").xpath("string(.)").get()) != f"{year} Reports": + raise ValueError("密歇根历史报告索引年份不符") + reports = {} + for node in sel.css("a[href]"): + match = re.fullmatch(r"([A-Z][a-z]+) (Preliminary|Final) Results", compact(node.xpath("string(.)").get())) + if match is None: + continue + identity = (f"{year}-{MONTHS[match[1]]:02d}", "preliminary" if match[2] == "Preliminary" else "final") + url = urljoin("https://data.sca.isr.umich.edu/", node.attrib["href"]) + if not re.fullmatch(r"https://data\.sca\.isr\.umich\.edu/fetchdoc\.php\?docid=\d+", url): + raise ValueError("密歇根历史报告链接不是官方文档地址") + if identity in reports: + raise ValueError("密歇根历史报告索引存在重复期次") + reports[identity] = url + if not reports: + raise ValueError("密歇根年度索引没有可识别的初值或终值报告") + return reports + + def parse_schedule(text: str) -> list[Scheduled]: events = [] # 历史 PDF 明确列出调查月份、初值日期和终值日期,包括跨月公布。 diff --git a/backend/tests/fixtures/calendar/umich-2025-01-final.txt b/backend/tests/fixtures/calendar/umich-2025-01-final.txt new file mode 100644 index 0000000..4757f7a --- /dev/null +++ b/backend/tests/fixtures/calendar/umich-2025-01-final.txt @@ -0,0 +1,78 @@ +© The University of Michigan, 2025. All rights reserved. + + + + + Monitoring trends for over 75 years + +January 2025 survey results January 24, 2025 +Joanne Hsu, PhD, Director +Consumer sentiment fell for the first time in six months, edging down 4% +from December. While assessments of personal finances inched up for the +fifth consecutive month, all other index components pulled back. Indeed, +sentiment declines were broad based and seen across incomes, wealth, and +age groups. Buying conditions for durables softened but remained about +30% better than six months ago amid persistent views that purchasing now +would avoid future price increases. Despite reporting stronger incomes +this month, concerns about unemployment rose; about 47% of consumers +expect unemployment to rise in the year ahead, the highest since the +pandemic recession. January’s data closed on Inauguration Day, and +consumers of all political leanings will continue to refine their views as Trump’s policies are clarified and implemented. +Year-ahead inflation expectations soared from 2.8% last month to 3.3% this month. The current reading is the highest since +May 2024 and is above the 2.3-3.0% range seen in the two years prior to the pandemic. Long-run inflation expectations rose +from 3.0% last month to 3.2% this month, the same reading seen in November 2024. For both the short and long run, +inflation expectations rose across income and educational groups. Concerns over the future trajectory of inflation were +visible throughout the interviews and were tied to beliefs about anticipated policies like tariffs. Consumers continued to +spontaneously express motives for buying-in-advance to avoid future price increases, and robust auto and retail sales data +suggest that consumers are indeed acting on these views. +Consumers’ concerns over the path of inflation appear closely tied to their views on anticipated economic policies under +the new president. Since the election, inflation expectations have risen for Democrats and fallen for Republicans. These +trends are consistent with historical patterns. During the Trump to Biden transition, the inflation expectations of Republicans +and Democrats similarly rotated around the national average, albeit against a backdrop of sustained low inflation in the +2010s. This month’s interviews make it abundantly clear that the current divergence in inflation expectations is shaped by +beliefs about future policy, rather than a form of political expression divorced from economic factors. +In January, consumers were asked if they thought higher tariffs would be better for the economy, or if lower tariffs would +be better, or if tariff policy would make no difference for the economy. As seen in the chart, about 19% of consumers +reported higher tariffs would be better for the economy, while 62% favored lower tariffs. Strikingly, those favoring higher +tariffs had much lower inflation expectations, both year-ahead and long-run, than those favoring lower tariffs and those who +believed tariff policy has little economic impact. These patterns suggest that consumers expect Trump to hold true to his +plan to hike tariffs and have incorporated the anticipated policy change into their economic expectations. +Responses to the tariff question were not fully correlated with party affiliation; less than half of Republicans reported that +higher tariffs would be better for the economy. Younger, more-educated, or higher-income consumers were more likely to +favor lower tariffs for the economy. Note that the interview focused on the perceived impact of tariffs on the economy, +rather than the overall policy preferences of the consumer. Some consumers may believe that lower tariffs are better for the +economy but prefer higher tariffs for non-economic reasons, say as a negotiating strategy or to advance other goals. +Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Jan +2024 2024 2024 2024 2024 2024 2024 2024 2024 2024 2024 2024 2025 +Index of Consumer Sentiment 79.0 76.9 79.4 77.2 69.1 68.2 66.4 67.9 70.1 70.5 71.8 74.0 71.1 +Current Economic Conditions 81.9 79.4 82.5 79.0 69.6 65.9 62.7 61.3 63.3 64.9 63.9 75.1 74.0 +Index of Consumer Expectations 77.1 75.2 77.4 76.0 68.8 69.6 68.8 72.1 74.4 74.1 76.9 73.3 69.3 +Index Components +Personal Finances - Current 100 98 104 95 91 81 79 79 80 81 82 85 86 +Personal Finances - Expected 122 116 122 116 111 106 105 108 111 110 116 111 108 +Economic Outlook - 12 Months 93 93 95 92 79 84 86 88 93 92 97 88 81 +Economic Outlook - 5 Years 94 92 94 96 84 88 84 92 94 95 96 94 88 +Buying Conditions - Durables 111 106 109 109 88 88 81 78 82 85 82 108 104 +-1 +0 +1 +2 +3 +4 +5 +Higher Tariffs +Better for Economy +(19% of Consumers) +Lower Tariffs +Better for Economy +(62% of Consumers) +No Difference +for Economy +(19% of Consumers) +Median Inflation Expectations % +Inflation Expectations Reflect Views on Policies +Consumers who Favor Higher Tariffs Expect Lower Inflation +Year-Ahead Inflation +Expectations +Long-Run Inflation +Expectations diff --git a/backend/tests/fixtures/calendar/umich-2025-01-preliminary.txt b/backend/tests/fixtures/calendar/umich-2025-01-preliminary.txt new file mode 100644 index 0000000..c4c0440 --- /dev/null +++ b/backend/tests/fixtures/calendar/umich-2025-01-preliminary.txt @@ -0,0 +1,68 @@ +© The University of Michigan, 2025. All rights reserved. + + + + + Monitoring trends for over 75 years + +Preliminary results from the January 2025 survey January 10, 2025 +Joanne Hsu, PhD, Director +Consumer sentiment was essentially unchanged in January, inching down +less than one index point from December, well within the margin of error. +Assessments of personal finances improved about 5%, while the economic +outlook fell back 7% for the short run and 5% for the long r un. January’s +divergence in views of the present and the future reflects easing concerns +over the current cost of living this month, but surging worries over the future +path of inflation. Overall, this month’s deterioration in the expectations index +was se en acro ss political affiliation s, including decline s of about 3% for +Independents and 1.5% for Republicans. +Year-ahead inflation expectations soared from 2.8% last month to 3.3% this month. The current reading is the highest since +May 2024 and is above the 2.3-3.0% range seen in the two years prior to the pandemic. Long-run inflation expectations rose +from 3.0% last month to 3.3% this month. This is only the third time in the last four years that long-run expectations have +exhibited such a large one -month change. For both the short and long run , inflation expectations rose across multiple +demographic groups, with particularly strong increases among lower -income co nsumers and Independents. Note that +inflation uncertainty—as estimated using the interquartile range in inflation expectations —has climbed considerably over +the past year, though it remains well below levels seen in the 1970s (see chart). October 2024’s special report provides +additional context for understanding the path of long-run inflation expectations over the last seven years. +Inflation expectations are being shaped by beliefs about future policy. The share of consumers worried about the prospect +of tariff hikes continues to rise. Nearly one-third of consumers spontaneously mentioned tariffs, up from 24% in December +and less than 2% prior to the election. These consumers generally report that tariff hikes will pass through to consumers in +the form of higher prices, thus pushing up their inflation expectations. +Concerningly, recent data show an emergence of inflationary psychology —motives for buying-in-advance to avoid future +price increases, the proliferation of which would generate further momentum for inf lation. Like in December, 22% of +consumers this month reported that purchasing durables now would enable buyers to avoid future price hikes, the highest +since 1990. That said, these buy-in-advance price rationales were far more prevalent in the 1970s, averaging 41% in 1978. +Expectations for the future weakened on other dimensions as well. Half of consumers expect unemployment rates to rise in +the year ahead, up from 40% last month and 31% a year ago. This month’s worsening was concentrated among Independents +and Republicans. Consumers’ expectations over their own income growth also fell from last month in both nominal and real +terms. Only one-third of consumers expect interest rates to fall over the next year, down from 41% in December and falling +for the fourth straight month. All told, consumers perceive numerous sources of downside risk to the economy. +Despite their worries for the future, consumers are fully aware that current inflation has slowed over the past two or so +years. In fact, the 4% increase in Current Conditions this month was largely supported by softening worries over current +prices. About 32% of consumers spontaneously cited high prices as weighing down their personal finances, down from 47% +in August 2024 and the lowest seen since February 2022. For buying conditions for large purchases, spontaneous mentions +of high prices have fallen over the last five months for durable goods and are the lowest since May 2021 for vehicles. +Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Jan +2024 2024 2024 2024 2024 2024 2024 2024 2024 2024 2024 2024 Prelim +Index of Consumer Sentiment 79.0 76.9 79.4 77.2 69.1 68.2 66.4 67.9 70.1 70.5 71.8 74.0 73.2 +Current Economic Conditions 81.9 79.4 82.5 79.0 69.6 65.9 62.7 61.3 63.3 64.9 63.9 75.1 77.9 +Index of Consumer Expectations 77.1 75.2 77.4 76.0 68.8 69.6 68.8 72.1 74.4 74.1 76.9 73.3 70.2 +Index Components +Personal Finances - Current 100 98 104 95 91 81 79 79 80 81 82 85 89 +Personal Finances - Expected 122 116 122 116 111 106 105 108 111 110 116 111 110 +Economic Outlook - 12 Months 93 93 95 92 79 84 86 88 93 92 97 88 82 +Economic Outlook - 5 Years 94 92 94 96 84 88 84 92 94 95 96 94 89 +Buying Conditions - Durables 111 106 109 109 88 88 81 78 82 85 82 108 112 +0 +2 +4 +6 +8 +10 +1977 1982 1987 1992 1997 2002 2007 2012 2017 2022 +Uncertainty - Year-Ahead Inflation Expectations +Uncertainty - 5-10 Year Inflation Expectations +Consumers Express Rising Uncertainty Over Path of Inflation +Uncertainty Estimated by 75th Percentile – 25th Percentile of Expectations +3-Month Moving Averages +University of Michigan, January 10, 2025 diff --git a/backend/tests/fixtures/calendar/umich-2025-07-final.txt b/backend/tests/fixtures/calendar/umich-2025-07-final.txt new file mode 100644 index 0000000..64a2e74 --- /dev/null +++ b/backend/tests/fixtures/calendar/umich-2025-07-final.txt @@ -0,0 +1,77 @@ +© The University of Michigan, 2025. All rights reserved. + + + + + Monitoring trends for over 75 years + +July 2025 survey results August 1, 2025 +Joanne Hsu, PhD, Director +Consumer sentiment improved for the second straight month, inching up +a scant single index point from June. Current conditions rose about 5% to +its highest reading since February 2025, while the expectations index fell +slightly. A rise in sentiment among stock holders was partially offset by a +decline among consumers who do not own stocks . Perceptions of this +month’s economic developments were similar across the political +spectrum; Republicans, Independents, and Democrats all saw some minor +increases in sentiment this month. Although recent trends show sentiment +moving in a favorable direction, sentiment remains broadly negative. +Consumers are hardly optimistic about the trajectory of the economy, even as their worries have softened since April 2025. +Year-ahead inflation expectations fell for a second straight month, plunging from 5.0% last month to 4.5% this month. This +is the lowest reading since February 2025 but above December 2024 just after the election. Long-run inflation expectations +receded for the third consecut ive month, falling back from 4.0% in June to 3.4% in July. This is the lowest reading since +January 2025 but, again, still considerably higher than the December 2024 reading. +Labor market expectations improved just a touch but remain considerably weaker than a year ago. About 57% of consumers +expect unemployment to rise in the year ahead, down from the two-thirds seen in March and April, but well above the 35% +seen a year ago (see chart, red solid line). Aside from the last five months, this is the highest reading since 2009. Moreover, +consumers continue to perceive elevated risks of personal job loss (see chart, black dotted line). Expectations of own +incomes remain extremely low and are less than half of what was seen in December 2024. +Sentiment this year has tracked the salience of tariffs to consumers. The share of consumers spontaneously offering +comments about tariffs has fallen from a height of two-thirds of consumers in May to 57%, still a solid majority, in July. +While tariff policy appears to be stabilizing below the levels threatened in April, recent announcements include tariff rates +that are still very high from a historical perspective. Tariff concerns are particularly strong among higher -income, high- +stock-wealth, and college-educated consumers. These consumers note that their personal finances have been boosted by +strong asset values, but their personal views of the economy are still relatively gloomy, which presents its own risks given +their large contribution to aggregate spending. +Overall, the survey reveals risks to both of the Fed’s mandates. While inflation expectations have fallen considerably from +the initial shock of the April tariff threats, they remain notably higher than the pre-pandemic period. Consumers clearly do +not believe that the risk of accelerating inflation has passed. Meanwhile, expectations remain poor for business conditions +and elevated for unemployment; critically, consumers anticipate they may be personally affected. Despite recent +improvements, this combination of views is consistent with a slowdown in spending, as consumers may respond to these +risks with more cautious financial behavior. At the same time, a cautionary pullback in spending could potentially mediate +any inflationary effects of tariffs. +Jul Aug Sep Oct Nov Dec Jan Feb Mar Apr May Jun Jul +2024 2024 2024 2024 2024 2024 2025 2025 2025 2025 2025 2025 2025 +Index of Consumer Sentiment 66.4 67.9 70.1 70.5 71.8 74.0 71.7 64.7 57.0 52.2 52.2 60.7 61.7 +Current Economic Conditions 62.7 61.3 63.3 64.9 63.9 75.1 75.1 65.7 63.8 59.8 58.9 64.8 68.0 +Index of Consumer Expectations 68.8 72.1 74.4 74.1 76.9 73.3 69.5 64.0 52.6 47.3 47.9 58.1 57.7 +Index Components +Personal Finances - Current 79 79 80 81 82 85 87 82 74 71 67 78 83 +Personal Finances - Expected 105 108 111 110 116 111 109 99 86 78 79 94 91 +Economic Outlook - 12 Months 86 88 93 92 97 88 81 73 56 48 50 63 66 +Economic Outlook - 5 Years 84 92 94 95 96 94 88 83 66 60 60 74 72 +Buying Conditions - Durables 81 78 82 85 82 108 106 86 89 82 83 88 91 +13 +15 +17 +19 +21 +23 +25 +0 +10 +20 +30 +40 +50 +60 +70 +80 +1997 2001 2005 2009 2013 2017 2021 2025 +Probability of Losing a Job During the +Next 5 Years (3-Month Movng Average) +% of Consumers Expecting More Unemployment +During the Next Year (3-Month Moving Average) +Despite Recent Improvements, Consumers Still +Anticipate Rising Unemployment and Elevated +Risks of Personal Job Loss diff --git a/backend/tests/fixtures/calendar/umich-2026-02-preliminary.txt b/backend/tests/fixtures/calendar/umich-2026-02-preliminary.txt new file mode 100644 index 0000000..b5265a4 --- /dev/null +++ b/backend/tests/fixtures/calendar/umich-2026-02-preliminary.txt @@ -0,0 +1,73 @@ +© The University of Michigan, 2026. All rights reserved. + Monitoring trends for over 75 years +P +reliminary results from the February 2026 survey February 6, 2026 +Joanne Hsu, PhD, Director +Consumer sentiment was essentially unchanged, inching up less than +one index point from last month and sitting about 20% below January +2025. Sentiment surged for consumers with the largest stock +portfolios, while it stagnated and remained at dismal levels for +consumers without stock holdings . On net, modest increases in +current personal finances and buying conditions for durables were +offset by a small decline in long- run business conditions. W hile +sentiment is currently the highest since August 2025, recent monthly +increases have been small—well under the margin of error—and the +overall level of sentiment remains very low from a historical +Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Jan Feb +2025 2025 2025 2025 2025 2025 2025 2025 2025 2025 2025 2026 Prelim +Index of Consumer Sentiment 64.7 57.0 52.2 52.2 60.7 61.7 58.2 55.1 53.6 51.0 52.9 56.4 57.3 +Current Economic Conditions 65.7 63.8 59.8 58.9 64.8 68.0 61.7 60.4 58.6 51.1 50.4 55.4 58.3 +Index of Consumer Expectations 64.0 52.6 47.3 47.9 58.1 57.7 55.9 51.7 50.3 51.0 54.6 57.0 56.6 +Index Components +Personal Finances - Current 82 74 71 67 78 83 77 76 78 66 68 74 77 +Personal Finances - Expected 99 86 78 79 94 91 91 86 82 85 93 95 95 +Economic Outlook - 12 Months 73 56 48 50 63 66 61 57 57 56 59 63 64 +Economic Outlook - 5 Years 83 66 60 60 74 72 70 62 59 61 64 68 65 +Buying Conditions - Durables 86 89 82 83 88 91 80 78 72 64 60 67 72 +40 +50 +60 +70 +80 +90 +100 +110 +2018 2019 2020 2021 2022 2023 2024 2025 2026 +Index of Consumer Sentiment +Top Tercile of Stock +Holdings, 3mma +No Stock Holdings, +3mma +Wealth Gaps in Sentiment Continue to Widen, +Mirroring Trends Seen in 2023 and 2024 + +perspective. Concerns about the erosion of personal finances from high prices and elevated risk of job loss continue to + +be +widespread. Interviews for this release cover the two-week period that ended this past Monday. +Year-ahead inflation expectations fell from 4.0% last month to 3.5 % this month, the lowest reading since January 2025. +This month’s reading still exceeds those seen in 2024 and remains well above the 2.3-3.0% range seen in the two years pre- +pandemic. Long-run inflation expectations inched up for the second straight month, from 3.3% last month to 3.4% this +month. In comparison, readings ranged between 2.8% and 3.2% in 2024, and were below 2.8% throughout 2019 and 2020. +Overall views of labor markets remain considerably weaker than a year ago, though changes from last month are a bit mixed. +This month, a substantial 60% of consumers expect unemployment to rise in the year ahead, down a touch from January +and the lowest share seen since July 2025. Meanwhile, the expected probability of own job loss climbed to its highest +reading since July 2020. Thus, consumers continue to perceive risks to both sides of the Fed’s dual mandate. +While views of the economy converged across population subgroups last spring amid announcements of the new tariff +regimes, the 10 or so months thereafter have been characterized by a sharp divergence between higher -wealth, higher- +income consumers and their lower-wealth, lower-income counterparts. As seen in the chart, sentiment for the largest +stockholders is currently nearly 50% higher than May 2025, when national sentiment reached a trough. In contrast, +sentiment for non-stockholders fell 6% during this period. +These trends are consistent with the fact that asset values have soared, which benefits asset owners but not others. Notably, +similar divergent patterns in sentiment were seen in 2023-2024 as well. Sentiment for high and low wealth groups converged +in 2022 when post -Covid inflation peaked. In the two years that followed , sentiment for the wealthy soared while it +languished for lower-wealth consumers. As such, the K-shape of sentiment seen over the past year is not a new phenomenon. +These patterns highlight the ongoing heterogeneity in experiences and expectations for the economy that are obscured by +headline aggregate statistics about the economy. Robust aggregate spending is consistent both with strength stemming from +the disproportionate share of spending generated by wealthier consumers who are more confident, as well as the fact that +less-confident, lower-wealth consumers may be exercising much more care over their spending amid pressures from high +prices and weakening income prospects. +Top Tercile of +Stock Holdings +Feb-2026 (monthly) +No Stock Holdings Feb-2026 (monthly) diff --git a/backend/tests/test_calendar_history.py b/backend/tests/test_calendar_history.py index 4ba4a03..5643f6e 100644 --- a/backend/tests/test_calendar_history.py +++ b/backend/tests/test_calendar_history.py @@ -1,12 +1,57 @@ import asyncio from datetime import UTC, date, datetime +from pathlib import Path from types import SimpleNamespace import pytest -from alphapulse.calendar.archives import parse_announcements, parse_bls_text +from alphapulse.calendar.archives import parse_announcements, parse_bls_text, parse_umich_report from alphapulse.calendar.parsers import bls_periods, release_metadata -from alphapulse.calendar.umich import current_result, monthly_series, parse_schedule +from alphapulse.calendar.umich import current_result, monthly_series, parse_schedule, report_links + + +@pytest.mark.parametrize("name,stage,at,actual,previous", [ + ("2025-01-preliminary", "preliminary", datetime(2025, 1, 10, 15, tzinfo=UTC), "73.2", "74.0"), + ("2025-01-final", "final", datetime(2025, 1, 24, 15, tzinfo=UTC), "71.1", "74.0"), + ("2025-07-final", "final", datetime(2025, 8, 1, 14, tzinfo=UTC), "61.7", "60.7"), + ("2026-02-preliminary", "preliminary", datetime(2026, 2, 6, 15, tzinfo=UTC), "57.3", "56.4"), +]) +def test_umich_report_preserves_original_vintage_stage_and_real_publication_date(name, stage, at, actual, previous): + body = (Path(__file__).parent / "fixtures/calendar" / f"umich-{name}.txt").read_text() + result = parse_umich_report(body) + assert (result.period, result.stage, result.at) == (name[:7], stage, at) + assert result.values["umich_sentiment"]["actual"] == actual + assert result.values["umich_sentiment"]["previous"] == previous + + +@pytest.mark.parametrize("old,new", [ + ("January 2025 survey results", "February 2025 survey results"), + ("2024 2024 2024 2025", "2024 2024 2024 Prelim"), + ("74.0 71.1", "74.0"), + ("January 24, 2025", "Date unavailable"), +]) +def test_umich_report_rejects_inconsistent_header_missing_value_or_missing_date(old, new): + body = (Path(__file__).parent / "fixtures/calendar/umich-2025-01-final.txt").read_text() + assert old in body + with pytest.raises(ValueError): + parse_umich_report(body.replace(old, new)) + + +def test_umich_index_only_accepts_official_stage_reports_for_the_requested_year(): + body = '''