Nye-TeeOff/backend/import_wp.py

119 lines
6.4 KiB
Python
Raw Normal View History

2026-02-26 09:20:51 +01:00
import asyncio, asyncpg, urllib.request, json, re
DB_URL = "postgresql://teeoff_admin:teeoff_secret_password@db:5432/teeoff"
WP_API_URL = "https://teeoff.no/wp-json/wp/v2/golfbaner?per_page=100&_embed"
def decode_html(text):
if not text: return ""
return str(text).replace('&', '&').replace('&', '&').replace(' ', ' ').strip()
def parse_int(val):
if val is None or val == '': return None
try:
nums = re.findall(r'\d+', str(val))
return int(nums[0]) if nums else None
except: return None
async def run_master_import():
print("🚀 Starter MASTER IMPORT v6.0 (ACF Mapped)...")
conn = await asyncpg.connect(DB_URL)
await conn.execute("TRUNCATE facilities, courses, holes RESTART IDENTITY CASCADE;")
page = 1
while True:
try:
req = urllib.request.Request(f"{WP_API_URL}&page={page}", headers={'User-Agent': 'TeeOff-V6'})
with urllib.request.urlopen(req) as response:
data = json.loads(response.read().decode())
except: break
if not data: break
for post in data:
acf = post.get('acf', {})
name = decode_html(post.get('title', {}).get('rendered', ''))
print(f"📦 Mapper {name}...")
# 1. Medlemskap (Mappet mot field_6040...)
membership = {
"url": acf.get('medlemskap_url'),
"standard": {
"navn": decode_html(acf.get('navn_standard_medlemskap')),
"pris": parse_int(acf.get('standard_medlemskap')),
"kommentar": decode_html(acf.get('standard_medlemskap_kommentarer'))
},
"rimeligste": {
"navn": decode_html(acf.get('navn_rimeligste_alternativ')),
"pris": parse_int(acf.get('rimeligste_alternativ')),
"kommentar": decode_html(acf.get('rimeligste_alternativ_kommentarer'))
}
}
# 2. Greenfee (Repeatere)
greenfee = {
"voksne": acf.get('greenfee_-_voksne') or [],
"junior": acf.get('greenfee_-_junior') or [],
"golfpakke": decode_html(acf.get('golfpakke')),
"rabattert": acf.get('rabattert_greenfee')
}
# 3. Amenities (Fasiliteter)
amenities = {
"drivingrange": decode_html(acf.get("drivingrange")),
"treningsgreen": decode_html(acf.get("treningsgreen")),
"proshop": decode_html(acf.get("proshop")),
"kafe": decode_html(acf.get("kafe")),
"bilutleie": decode_html(acf.get("bilutleie")),
"pro": decode_html(acf.get("pro")),
"antall_hull": decode_html(acf.get("antall_hull"))
}
# 4. Lagre Facility
fac_id = await conn.fetchval('''
INSERT INTO facilities (
name, slug, description, established_year, season, address, city, county,
lat, lng, email, phone, website_url, image_url, amenities, greenfee,
membership, vtg, status_updated_at, logo_url, video_url, guest_requirements,
faqs, shotzoom, translations
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15::jsonb,
$16::jsonb, $17::jsonb, $18::jsonb, TO_DATE(NULLIF($19, ''), 'YYYYMMDD'), $20, $21, $22, $23::jsonb, $24::jsonb, $25::jsonb)
RETURNING id
''', name, post['slug'], decode_html(acf.get('beskrivelse')), parse_int(acf.get('byggear')), acf.get('sesong'),
acf.get('gateadresse'), acf.get('postnummer_og_poststed'), acf.get('fylke'),
float(acf.get('banekart', {}).get('lat', 0)) or None, float(acf.get('banekart', {}).get('lng', 0)) or None,
acf.get('e-post'), acf.get('telefon'), acf.get('hjemmeside'),
post.get('_embedded', {}).get('wp:featuredmedia', [{}])[0].get('source_url'),
json.dumps(amenities), json.dumps(greenfee), json.dumps(membership), json.dumps(acf.get('vtg') or {}),
acf.get('dato_for_oppdatert_status'), acf.get('logo'),
f"https://youtube.com/embed/{acf.get('videopresentasjon_youtube')}" if acf.get('videopresentasjon_youtube') else None,
decode_html(acf.get('krav_til_gjestespillere')), json.dumps(acf.get('faq') or []), json.dumps(acf.get('shotzoom') or []), json.dumps({}))
# 5. Baner og Hull (Bruker ACF-felt for Hovedbane og Bane 2)
for suffix in ['', '_bane_to']:
course_name = acf.get('navn_pa_hovedbane' if suffix == '' else 'navn_pa_sekundar_bane') or ('Hovedbane' if suffix == '' else 'Bane 2')
status = acf.get('banestatus' if suffix == '' else 'banestatus_sekundar_bane')
# Sjekk om det i det hele tatt finnes data for denne banen
if suffix == '_bane_to' and (status == 'finnes_ingen_bane_to' or not parse_int(acf.get('hull_1_par_bane_to'))):
continue
course_id = await conn.fetchval('''
INSERT INTO courses (facility_id, name, status, par, length_meters, is_main_course, tee_boxes)
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb) RETURNING id
''', fac_id, course_name, status, parse_int(acf.get('totalt_par' if suffix == '' else 'totalt_par_bane_to')),
parse_int(acf.get('lengde' if suffix == '' else 'lengst_totalt_bane_to')), (suffix == ''),
json.dumps({"herrer": acf.get(f"utslag_herrer{suffix}"), "damer": acf.get(f"utslag_damer{suffix}")}))
for h_num in range(1, 19):
p = parse_int(acf.get(f'hull_{h_num}_par{suffix}'))
if p:
idx = parse_int(acf.get(f'hull_{h_num}_index{suffix}'))
lengths = {k: parse_int(acf.get(f'{k}_hull_{h_num}{suffix}')) for k in ['lengst', 'lang', 'mellomlang', 'mellomkort', 'kort', 'kortest']}
await conn.execute('INSERT INTO holes (course_id, hole_number, par, hcp_index, lengths) VALUES ($1, $2, $3, $4, $5::jsonb)',
course_id, h_num, p, idx, json.dumps(lengths))
page += 1
await conn.close()
print("✅ Ferdig! All data er nå korrekt importert.")
if __name__ == "__main__":
asyncio.run(run_master_import())