Add new Wunderground-backed cities and source links

This commit is contained in:
2569718930@qq.com
2026-04-05 01:23:10 +08:00
parent 15a16f59fe
commit 343c5c9c2f
4 changed files with 263 additions and 0 deletions
+102
View File
@@ -92,6 +92,23 @@ const CITY_SPECIFIC_SOURCES: Record<string, OfficialSourceLink[]> = {
kind: "metar",
},
],
busan: [
{
label: "Wunderground RKPK",
href: "https://www.wunderground.com/history/daily/kr/busan/RKPK",
kind: "agency",
},
{
label: "金海国际机场",
href: "https://www.airport.co.kr/gimhaeeng/index.do",
kind: "airport",
},
{
label: "RKPK METAR",
href: "https://aviationweather.gov/data/metar/?id=RKPK&decoded=1&taf=1",
kind: "metar",
},
],
istanbul: [
{
label: "MGM",
@@ -526,6 +543,91 @@ const CITY_SPECIFIC_SOURCES: Record<string, OfficialSourceLink[]> = {
kind: "metar",
},
],
"kuala lumpur": [
{
label: "Wunderground WMKK",
href: "https://www.wunderground.com/history/daily/my/sepang-district/WMKK",
kind: "agency",
},
{
label: "吉隆坡国际机场",
href: "https://airports.malaysiaairports.com.my/klia",
kind: "airport",
},
{
label: "WMKK METAR",
href: "https://aviationweather.gov/data/metar/?id=WMKK&decoded=1&taf=1",
kind: "metar",
},
],
jakarta: [
{
label: "Wunderground WIHH",
href: "https://www.wunderground.com/history/daily/id/jakarta/WIHH",
kind: "agency",
},
{
label: "Halim Perdanakusuma Airport",
href: "https://www.angkasapura2.co.id/en/airport/read/HLP",
kind: "airport",
},
{
label: "WIHH METAR",
href: "https://aviationweather.gov/data/metar/?id=WIHH&decoded=1&taf=1",
kind: "metar",
},
],
helsinki: [
{
label: "Wunderground EFHK",
href: "https://www.wunderground.com/history/daily/fi/vantaa/EFHK",
kind: "agency",
},
{
label: "Helsinki Airport",
href: "https://www.finavia.fi/en/airports/helsinki-airport",
kind: "airport",
},
{
label: "EFHK METAR",
href: "https://aviationweather.gov/data/metar/?id=EFHK&decoded=1&taf=1",
kind: "metar",
},
],
amsterdam: [
{
label: "Wunderground EHAM",
href: "https://www.wunderground.com/history/daily/nl/schiphol/EHAM",
kind: "agency",
},
{
label: "Amsterdam Airport Schiphol",
href: "https://www.schiphol.nl/en/",
kind: "airport",
},
{
label: "EHAM METAR",
href: "https://aviationweather.gov/data/metar/?id=EHAM&decoded=1&taf=1",
kind: "metar",
},
],
"panama city": [
{
label: "Wunderground MPMG",
href: "https://www.wunderground.com/history/daily/pa/panama-city/MPMG",
kind: "agency",
},
{
label: "Marcos A. Gelabert Airport",
href: "https://www.aeropuertos.net/aeropuerto-internacional-marcos-a-gelabert/",
kind: "airport",
},
{
label: "MPMG METAR",
href: "https://aviationweather.gov/data/metar/?id=MPMG&decoded=1&taf=1",
kind: "metar",
},
],
beijing: [
{
label: "中国天气网",
+122
View File
@@ -92,6 +92,24 @@ CITY_REGISTRY = {
"distance_km": 48.8,
"warning": "距离太远,海洋性vs大陆性气候差异大。",
},
"busan": {
"name": "Busan",
"lat": 35.1795,
"lon": 128.9382,
"icao": "RKPK",
"settlement_source": "wunderground",
"settlement_station_code": "RKPK",
"settlement_station_label": "Busan/Gimhae International Airport Station",
"settlement_url": "https://www.wunderground.com/history/daily/kr/busan/RKPK",
"tz_offset": 32400,
"use_fahrenheit": False,
"is_major": True,
"risk_level": "medium",
"risk_emoji": "🟡",
"airport_name": "金海国际机场",
"distance_km": 14.0,
"warning": "市场现按 Wunderground 金海国际机场站整度°C口径结算;海风与沿海云量会影响午后峰值。",
},
"hong kong": {
"name": "Hong Kong",
"lat": 22.3019,
@@ -175,6 +193,42 @@ CITY_REGISTRY = {
"distance_km": 17.5,
"warning": "全年温度振幅较小,主要受午后阵雨时段影响。",
},
"kuala lumpur": {
"name": "Kuala Lumpur",
"lat": 2.7456,
"lon": 101.7072,
"icao": "WMKK",
"settlement_source": "wunderground",
"settlement_station_code": "WMKK",
"settlement_station_label": "Kuala Lumpur International Airport Station",
"settlement_url": "https://www.wunderground.com/history/daily/my/sepang-district/WMKK",
"tz_offset": 28800,
"use_fahrenheit": False,
"is_major": True,
"risk_level": "medium",
"risk_emoji": "🟡",
"airport_name": "吉隆坡国际机场",
"distance_km": 45.0,
"warning": "市场现按 Wunderground 吉隆坡国际机场站整度°C口径结算;午后对流和热带湿热会让峰值窗口偏短。",
},
"jakarta": {
"name": "Jakarta",
"lat": -6.2666,
"lon": 106.8910,
"icao": "WIHH",
"settlement_source": "wunderground",
"settlement_station_code": "WIHH",
"settlement_station_label": "Jakarta/Halim Perdanakusuma Airport Station",
"settlement_url": "https://www.wunderground.com/history/daily/id/jakarta/WIHH",
"tz_offset": 25200,
"use_fahrenheit": False,
"is_major": True,
"risk_level": "medium",
"risk_emoji": "🟡",
"airport_name": "Halim Perdanakusuma Airport",
"distance_km": 12.0,
"warning": "市场现按 Wunderground Halim 机场站整度°C口径结算;热带午后阵雨常在峰值前后快速压制气温。",
},
"tokyo": {
"name": "Tokyo",
"lat": 35.5523,
@@ -486,6 +540,42 @@ CITY_REGISTRY = {
"distance_km": 10.0,
"warning": "平原地形下辐射冷却显著,清晨与夜间温度回落可能偏快。",
},
"helsinki": {
"name": "Helsinki",
"lat": 60.3172,
"lon": 24.9633,
"icao": "EFHK",
"settlement_source": "wunderground",
"settlement_station_code": "EFHK",
"settlement_station_label": "Helsinki Airport Station",
"settlement_url": "https://www.wunderground.com/history/daily/fi/vantaa/EFHK",
"tz_offset": 7200,
"use_fahrenheit": False,
"is_major": True,
"risk_level": "medium",
"risk_emoji": "🟡",
"airport_name": "赫尔辛基机场",
"distance_km": 17.0,
"warning": "市场现按 Wunderground 赫尔辛基机场站整度°C口径结算;高纬度日照变化快,冷平流会明显影响日峰值。",
},
"amsterdam": {
"name": "Amsterdam",
"lat": 52.3086,
"lon": 4.7639,
"icao": "EHAM",
"settlement_source": "wunderground",
"settlement_station_code": "EHAM",
"settlement_station_label": "Amsterdam Airport Schiphol Station",
"settlement_url": "https://www.wunderground.com/history/daily/nl/schiphol/EHAM",
"tz_offset": 3600,
"use_fahrenheit": False,
"is_major": True,
"risk_level": "medium",
"risk_emoji": "🟡",
"airport_name": "阿姆斯特丹史基浦机场",
"distance_km": 17.0,
"warning": "市场现按 Wunderground 史基浦机场站整度°C口径结算;北海海风和平流层云容易限制午后升温。",
},
"madrid": {
"name": "Madrid",
"lat": 40.4722,
@@ -576,6 +666,24 @@ CITY_REGISTRY = {
"distance_km": 26.0,
"warning": "江汉平原,水汽充足,夏季常现长时闷热高温。",
},
"panama city": {
"name": "Panama City",
"lat": 8.9733,
"lon": -79.5556,
"icao": "MPMG",
"settlement_source": "wunderground",
"settlement_station_code": "MPMG",
"settlement_station_label": "Panama City/Marcos A. Gelabert Airport Station",
"settlement_url": "https://www.wunderground.com/history/daily/pa/panama-city/MPMG",
"tz_offset": -18000,
"use_fahrenheit": False,
"is_major": True,
"risk_level": "medium",
"risk_emoji": "🟡",
"airport_name": "Marcos A. Gelabert Airport",
"distance_km": 6.0,
"warning": "市场现按 Wunderground Marcos A. Gelabert 机场站整度°C口径结算;热带对流常让峰值在午后被快速截断。",
},
}
ALIASES = {
@@ -593,8 +701,14 @@ ALIASES = {
"seo": "seoul", "hkg": "hong kong", "hk": "hong kong",
"lfs": "lau fau shan", "laufaushan": "lau fau shan",
"tpe": "taipei", "tp": "taipei", "taipei": "taipei",
"pus": "busan", "bus": "busan", "rkpk": "busan",
"sha": "shanghai", "sh": "shanghai", "sin": "singapore",
"sg": "singapore", "tok": "tokyo", "tyo": "tokyo",
"kul": "kuala lumpur", "wmkk": "kuala lumpur", "sepang": "kuala lumpur",
"jkt": "jakarta", "wihh": "jakarta", "halim": "jakarta",
"hel": "helsinki", "efhk": "helsinki", "vantaa": "helsinki",
"ams": "amsterdam", "eham": "amsterdam", "schiphol": "amsterdam",
"pty": "panama city", "mpmg": "panama city", "panamacity": "panama city",
"tlv": "tel aviv", "telaviv": "tel aviv",
"ba": "buenos aires", "wel": "wellington",
"luc": "lucknow", "sp": "sao paulo", "mun": "munich",
@@ -626,8 +740,12 @@ ALIASES = {
"流浮山": "lau fau shan",
"台北": "taipei",
"臺北": "taipei",
"釜山": "busan",
"上海": "shanghai",
"新加坡": "singapore",
"吉隆坡": "kuala lumpur",
"雅加达": "jakarta",
"雅加達": "jakarta",
"东京": "tokyo",
"東京": "tokyo",
"特拉维夫": "tel aviv",
@@ -640,6 +758,10 @@ ALIASES = {
"米蘭": "milan",
"华沙": "warsaw",
"華沙": "warsaw",
"赫尔辛基": "helsinki",
"赫爾辛基": "helsinki",
"阿姆斯特丹": "amsterdam",
"巴拿马城": "panama city",
"马德里": "madrid",
"馬德里": "madrid",
"ctu": "chengdu", "cd": "chengdu",
+24
View File
@@ -44,6 +44,7 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour
"mexico city": ["MMMX", "MMSM", "MMTO"],
"paris": ["LFPG", "LFPO", "LFPB"],
"seoul": ["RKSI", "RKSS"],
"busan": ["RKPK", "RKSS"],
"hong kong": ["VHHH", "VMMC", "ZGSZ"],
"taipei": ["RCSS", "RCTP"],
"chengdu": ["ZUUU", "ZUTF"],
@@ -53,11 +54,16 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour
"wuhan": ["ZHHH", "ZHES"],
"shanghai": ["ZSPD", "ZSSS", "ZSNB", "ZSHC"],
"singapore": ["WSSS", "WSAP", "WMKK"],
"kuala lumpur": ["WMKK", "WMSA"],
"jakarta": ["WIHH", "WIII"],
"tokyo": ["RJTT", "RJAA", "RJAH", "RJTJ"],
"tel aviv": ["LLBG"],
"milan": ["LIMC", "LIML", "LIME", "LIPO"],
"toronto": ["CYYZ", "CYTZ", "CYKF"],
"warsaw": ["EPWA", "EPMO", "EPLL"],
"helsinki": ["EFHK", "EETN"],
"amsterdam": ["EHAM", "EHRD"],
"panama city": ["MPMG", "MPTO"],
"madrid": ["LEMD", "LETO", "LEGT"],
"chicago": ["KORD", "KMDW", "KPWK", "KDPA"],
"dallas": ["KDAL", "KDFW", "KADS", "KGKY"],
@@ -445,6 +451,9 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour
"伊斯坦布尔": "Istanbul",
"seoul": "Seoul",
"首尔": "Seoul",
"busan": "Busan",
"pusan": "Busan",
"釜山": "Busan",
"hong kong": "Hong Kong",
"hong kong international airport": "Hong Kong",
"香港": "Hong Kong",
@@ -472,6 +481,21 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour
"上海": "Shanghai",
"singapore": "Singapore",
"新加坡": "Singapore",
"kuala lumpur": "Kuala Lumpur",
"sepang": "Kuala Lumpur",
"吉隆坡": "Kuala Lumpur",
"jakarta": "Jakarta",
"雅加达": "Jakarta",
"雅加達": "Jakarta",
"helsinki": "Helsinki",
"vantaa": "Helsinki",
"赫尔辛基": "Helsinki",
"赫爾辛基": "Helsinki",
"amsterdam": "Amsterdam",
"schiphol": "Amsterdam",
"阿姆斯特丹": "Amsterdam",
"panama city": "Panama City",
"巴拿马城": "Panama City",
"tokyo": "Tokyo",
"东京": "Tokyo",
"東京": "Tokyo",
+15
View File
@@ -54,6 +54,21 @@ def test_cities_endpoint_uses_denver_display_name_for_aurora_market():
assert aurora["display_name"] == "Denver"
def test_cities_endpoint_includes_new_wunderground_cities():
response = client.get("/api/cities")
assert response.status_code == 200
payload = response.json()
names = {item["name"] for item in payload["cities"]}
assert {
"busan",
"panama city",
"kuala lumpur",
"jakarta",
"helsinki",
"amsterdam",
}.issubset(names)
def test_payment_runtime_endpoint_returns_shape():
response = client.get('/api/payments/runtime')
assert response.status_code == 200