118 lines
6.4 KiB
Python
118 lines
6.4 KiB
Python
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())
|