Etter dagens økt. (Database mot database)

This commit is contained in:
Erol 2026-02-26 16:32:34 +01:00
parent 477faf5b69
commit b88753a8e7
600 changed files with 1985 additions and 263 deletions

Binary file not shown.

View file

@ -1,7 +1,65 @@
import asyncio, asyncpg, urllib.request, json, re
import asyncio, asyncpg, urllib.request, json, re, os, requests
# --- KONFIGURASJON ---
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"
MEDIA_ENDPOINT = "https://teeoff.no/wp-json/wp/v2/media"
MEDIA_DIR = "./public/media"
os.makedirs(MEDIA_DIR, exist_ok=True)
# Cache for å slippe å spørre om samme bilde-ID flere ganger
media_cache = {}
def get_url_from_id(media_id):
"""Slår opp en WordPress Media-ID og returnerer den faktiske bilde-URLen"""
if not media_id or not isinstance(media_id, int):
return None
if media_id in media_cache:
return media_cache[media_id]
try:
print(f" 🔍 Slår opp Media-ID: {media_id}...")
resp = requests.get(f"{MEDIA_ENDPOINT}/{media_id}", timeout=10)
if resp.status_code == 200:
url = resp.json().get('source_url')
media_cache[media_id] = url
return url
except Exception as e:
print(f" ⚠️ Kunne ikke finne URL for Media-ID {media_id}: {e}")
return None
def download_media(url, slug, prefix):
if not isinstance(url, str) or not url:
return None
# Reparer "triple-slash" og andre WP-feil
clean_url = url.replace("https:///", "https://").replace("http:///", "http://")
if "teeoff.no" not in clean_url:
return clean_url
try:
ext = clean_url.split('.')[-1].split('?')[0].lower()
if len(ext) > 4 or len(ext) < 3: ext = "jpg"
filename = f"{prefix}_{slug}.{ext}"
filepath = os.path.join(MEDIA_DIR, filename)
if os.path.exists(filepath):
return f"/media/{filename}"
response = requests.get(clean_url, timeout=15)
if response.status_code == 200:
with open(filepath, 'wb') as f:
f.write(response.content)
return f"/media/{filename}"
except Exception as e:
print(f" ⚠️ Feil ved nedlasting: {e}")
return None
def decode_html(text):
if not text: return ""
@ -14,105 +72,127 @@ def parse_int(val):
return int(nums[0]) if nums else None
except: return None
def extract_url(val):
if isinstance(val, dict): return val.get('url')
if isinstance(val, str): return val
return None
async def run_master_import():
print("🚀 Starter MASTER IMPORT v6.0 (ACF Mapped)...")
print("🚀 Starter MASTER IMPORT v8.9.2 (Media ID Resolver)...")
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'})
req = urllib.request.Request(f"{WP_API_URL}&page={page}", headers={'User-Agent': 'TeeOff-V8.9.2'})
with urllib.request.urlopen(req) as response:
data = json.loads(response.read().decode())
except: break
except Exception: break
if not data: break
for post in data:
acf = post.get('acf', {})
slug = post['slug']
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'))
}
}
# --- 1. HOVEDBILDE ---
featured_img = post.get('_embedded', {}).get('wp:featuredmedia', [{}])[0].get('source_url')
local_main_img = download_media(featured_img, slug, "main")
# --- 2. LOGO ---
logo_field = acf.get('logo')
logo_url = extract_url(logo_field)
if not logo_url and isinstance(logo_field, int):
logo_url = get_url_from_id(logo_field)
local_logo = download_media(logo_url, slug, "logo")
# --- 3. GALLERI (SLIDER) ---
slides = acf.get('slides') or []
local_gallery = []
if isinstance(slides, list):
for idx, s in enumerate(slides):
url = None
if isinstance(s, int): # DIN CASE: Vi har en ID
url = get_url_from_id(s)
elif isinstance(s, dict):
url = s.get('url')
elif isinstance(s, str):
url = s
if url:
res = download_media(url, f"{slug}_{idx}", "slide")
if res: local_gallery.append(res)
# 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')
}
# --- GOLFBOX & SOSIALE ---
booking_id = acf.get('golfbox_booking_id')
gb_booking_url = f"http://www.golfbox.no/site/system/redirect.asp?locale=nb_NO&rUrl=%2Fsite%2Fressources%2Fbooking%2Fgrid.asp%3FRessource_GUID%3D%{{{str(booking_id).strip().replace('{','').replace('}','')}}}" if booking_id else None
# 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 FACILITY ---
await conn.execute('''
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
status_updated_at, logo_url, video_url, guest_requirements,
faqs, shotzoom, gallery, ngf_number, golfbox_club_id, golfbox_booking_url,
facebook_url, instagram_url, baneguide_url, flyfoto_url, golfbox_tournament_url,
footnote, social_links, webcam_url, weather_url, architect,
navn_standard_medlemskap, standard_medlemskap, standard_medlemskap_kommentarer,
navn_rimeligste_alternativ, rimeligste_alternativ, rimeligste_alternativ_kommentarer,
medlemskap_url
) 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'),
$16::jsonb, TO_DATE(NULLIF($17, ''), 'YYYYMMDD'),
$18, $19, $20, $21::jsonb, $22::jsonb, $23::jsonb,
$24, $25, $26, $27, $28, $29, $30, $31, $32, $33::jsonb, $34, $35, $36,
$37, $38, $39, $40, $41, $42, $43)
''',
name, 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({}))
float(acf.get('banekart', {}).get('lat', 0)) if acf.get('banekart') else None,
float(acf.get('banekart', {}).get('lng', 0)) if acf.get('banekart') else None,
acf.get('e-post'), acf.get('telefon'), extract_url(acf.get('hjemmeside')),
local_main_img,
json.dumps({"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")), "kolleutleie": decode_html(acf.get("kolleutleie")), "pro": decode_html(acf.get("pro")), "simulator": decode_html(acf.get("golfsimulator")), "antall_hull": decode_html(acf.get("antall_hull"))}),
json.dumps(acf.get('greenfee_-_voksne') or []),
acf.get('dato_for_oppdatert_status'), local_logo,
None, decode_html(acf.get('krav_til_gjestespillere')),
json.dumps([]), json.dumps(acf.get('shotzoom') or []), json.dumps(local_gallery),
parse_int(acf.get('klubbnummer_norges_golfforbund')), parse_int(acf.get('klubbnummer_golfbox')),
gb_booking_url, extract_url(acf.get('facebook_url')), extract_url(acf.get('instagram_url')),
extract_url(acf.get('baneguide')), extract_url(acf.get('flyfoto')), extract_url(acf.get('golfbox')),
decode_html(acf.get('fotnote')), json.dumps(acf.get('sosiale_lenker') or []),
decode_html(acf.get('webkamera')), extract_url(acf.get('varmelding_yr')), decode_html(acf.get('arkitekt')),
decode_html(acf.get('navn_standard_medlemskap')), parse_int(acf.get('standard_medlemskap')),
decode_html(acf.get('standard_medlemskap_kommentarer')), decode_html(acf.get('navn_rimeligste_alternativ')),
parse_int(acf.get('rimeligste_alternativ')), decode_html(acf.get('rimeligste_alternativ_kommentarer')),
extract_url(acf.get('medlemskap_url')))
# 5. Baner og Hull (Bruker ACF-felt for Hovedbane og Bane 2)
# Hent facility id for baner
fac_row = await conn.fetchrow("SELECT id FROM facilities WHERE slug = $1", slug)
fac_id = fac_row['id']
# --- BANER OG HULL (Samme som før) ---
fac_main_len = 0
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')
c_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}")}))
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, is_main_course, tee_boxes, architect) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id', fac_id, c_name, status, parse_int(acf.get('totalt_par' if suffix == '' else 'totalt_par_bane_to')), (suffix == ''), json.dumps({"herrer": acf.get(f"utslag_herrer{suffix}"), "damer": acf.get(f"utslag_damer{suffix}")}), decode_html(acf.get('arkitekt')))
curr_len = 0
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))
lens = {k: parse_int(acf.get(f'{k}_hull_{h_num}{suffix}')) for k in ['lengst', 'lang', 'mellomlang', 'mellomkort', 'kort', 'kortest']}
curr_len += (lens['lengst'] or 0)
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(lens))
await conn.execute("UPDATE courses SET length_meters = $1 WHERE id = $2", curr_len, course_id)
if suffix == '': fac_main_len = curr_len
await conn.execute("UPDATE facilities SET length_meters = $1 WHERE id = $2", fac_main_len, fac_id)
page += 1
await conn.close()
print("✅ Ferdig! All data er nå korrekt importert.")
print("GALLERI-BILDER RESOLVED OG IMPORT FERDIG!")
if __name__ == "__main__":
asyncio.run(run_master_import())
asyncio.run(run_master_import())

View file

@ -3,3 +3,4 @@ uvicorn[standard]
asyncpg
httpx
beautifulsoup4
requests

View file

@ -17,6 +17,11 @@ services:
container_name: teeoff_api
ports:
- "8001:8000"
volumes:
- ./backend:/app
# Denne linjen sørger for at bilder lastet ned av import_wp.py
# lagres direkte i frontendens public-mappe på serveren din:
- ./frontend/public/media:/app/public/media
depends_on:
- db
restart: unless-stopped
@ -33,4 +38,4 @@ services:
restart: unless-stopped
volumes:
teeoff_db_data:
teeoff_db_data:

72
eksport_script.py Normal file
View file

@ -0,0 +1,72 @@
import os
import shutil
from pathlib import Path
# --- KONFIGURASJON ---
KILDE_MAPPE = "/opt/teeoff/"
EKSPORT_MAPPE = "/opt/teeoff/kode_eksport/"
TRE_FIL = "/opt/teeoff/filtre.txt"
# Filtyper vi vil kopiere
FILTYPER = ['.py', '.ts', '.tsx']
# Mapper vi IKKE vil ha med i treet eller skanne (sparer tid og rot)
IGNORER_MAPPER = ['.git', 'node_modules', '__pycache__', 'kode_eksport', '.next']
def generer_tre_og_kopier():
kilde_sti = Path(KILDE_MAPPE)
eksport_sti = Path(EKSPORT_MAPPE)
# 1. Opprett eksportmappen hvis den ikke finnes
eksport_sti.mkdir(parents=True, exist_ok=True)
tre_linjer = []
kopierte_filer = 0
print("Skanner filer og genererer tre...")
# 2. Gå gjennom alle mapper og filer
for root, dirs, files in os.walk(kilde_sti):
# Fjern ignorerte mapper så vi ikke går inn i dem
dirs[:] = [d for d in dirs if d not in IGNORER_MAPPER]
# Regn ut innrykk basert på hvor dypt vi er i mappestrukturen
nivaa = root.replace(KILDE_MAPPE, '').count(os.sep)
innrykk = ' ' * 4 * nivaa
mappe_navn = os.path.basename(root)
# Legg til mappen i treet
if mappe_navn:
tre_linjer.append(f"{innrykk}📁 {mappe_navn}/")
else:
tre_linjer.append(f"📁 {kilde_sti.name}/")
sub_innrykk = ' ' * 4 * (nivaa + 1)
# 3. Gå gjennom filene i mappen
for fil in files:
tre_linjer.append(f"{sub_innrykk}📄 {fil}")
fil_sti = Path(root) / fil
# 4. Sjekk om filen har riktig endelse og skal kopieres
if fil_sti.suffix in FILTYPER:
# Lag et unikt filnavn for å unngå overskriving
relativ_sti = fil_sti.relative_to(kilde_sti)
nytt_navn = str(relativ_sti).replace(os.sep, '_').replace('.', '_') + '.txt'
ny_sti = eksport_sti / nytt_navn
# Kopier filen
shutil.copy2(fil_sti, ny_sti)
kopierte_filer += 1
# 5. Lagre filteret til tekstfilen
with open(TRE_FIL, 'w', encoding='utf-8') as f:
f.write('\n'.join(tre_linjer))
print(f"\n✅ Ferdig!")
print(f"📁 Filtre er lagret i: {TRE_FIL}")
print(f"📝 Kopierte {kopierte_filer} kodefiler til: {EKSPORT_MAPPE}")
if __name__ == "__main__":
generer_tre_og_kopier()

47
filtre.txt Normal file
View file

@ -0,0 +1,47 @@
📁 teeoff/
📄 seed.sql
📄 docker-compose.yml
📄 schema.sql
📄 init.sql
📁 frontend/
📄 eslint.config.mjs
📄 next-env.d.ts
📄 tsconfig.json
📄 README.md
📄 next.config.ts
📄 postcss.config.mjs
📄 package-lock.json
📄 .gitignore
📄 package.json
📄 Dockerfile
📁 public/
📄 globe.svg
📄 vercel.svg
📄 Toppbilde-standard.jpg
📄 TeeOff-logo-Retina-1.png
📄 window.svg
📄 next.svg
📄 file.svg
📁 src/
📁 config/
📄 constants.ts
📁 app/
📄 FacilitySearch.tsx
📄 HeroSlider.tsx
📄 favicon.ico
📄 globals.css
📄 page.tsx
📄 layout.tsx
📁 golfbaner/
📁 [slug]/
📄 CourseDisplay.tsx
📄 page.tsx
📄 FacilityDetailView.tsx
📁 backend/
📄 scrape_nsg_3.py
📄 import_gallery.py
📄 scrape_golfamore1.3.py
📄 requirements.txt
📄 import_wp.py
📄 main.py
📄 Dockerfile

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Some files were not shown because too many files have changed in this diff Show more