From 3146fa0d26dc737e001defde60a14ce0b0fc967d Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 23 May 2024 04:42:26 +0200 Subject: [PATCH] Cleanup of NSI feature breanch --- public/assets/data/nsi/stats/brand.BE.json | 398 +----------------- scripts/generateStats.ts | 37 +- src/Logic/Web/NameSuggestionIndex.ts | 98 +++-- .../ThemeConfig/Conversion/Validation.ts | 19 +- .../QuestionableTagRenderingConfigJson.ts | 6 - src/Models/ThemeConfig/TagRenderingConfig.ts | 45 +- src/Models/ThemeViewState.ts | 1 - src/UI/Base/CloseAnimation.svelte | 1 - .../BigComponents/SelectedElementView.svelte | 3 +- .../TagRenderingAnswerDynamic.svelte | 22 + .../TagRendering/TagRenderingEditable.svelte | 9 +- .../TagRenderingEditableDynamic.svelte | 32 -- .../TagRendering/TagRenderingQuestion.svelte | 6 +- src/test.ts | 3 - 14 files changed, 143 insertions(+), 537 deletions(-) create mode 100644 src/UI/Popup/TagRendering/TagRenderingAnswerDynamic.svelte delete mode 100644 src/UI/Popup/TagRendering/TagRenderingEditableDynamic.svelte diff --git a/public/assets/data/nsi/stats/brand.BE.json b/public/assets/data/nsi/stats/brand.BE.json index 2e80f76fe..07bca63ee 100644 --- a/public/assets/data/nsi/stats/brand.BE.json +++ b/public/assets/data/nsi/stats/brand.BE.json @@ -1,397 +1 @@ -{ - "& Other Stories": 3, - "5àsec": 5, - "A.S.Adventure": 26, - "AD Delhaize": 126, - "ALDI": 309, - "AS 24": 5, - "AXA": 172, - "Accent Jobs": 3, - "Ace & Tate": 6, - "Action": 171, - "Adecco": 21, - "Alain Afflelou": 22, - "Albert Heijn": 79, - "Alfa Romeo": 8, - "Alvo": 14, - "America Today": 6, - "American Vintage": 7, - "Amplifon": 24, - "Apple Store": 3, - "Argenta": 290, - "Armand Thiery": 7, - "Attijariwafa Bank": 3, - "Audi": 41, - "Audika": 5, - "Aveve": 77, - "Avia": 63, - "Avis": 7, - "BMW": 60, - "BNP Paribas": 34, - "BNP Paribas Fortis": 329, - "BOGGI Milano": 3, - "BRAX": 8, - "Ba&sh": 3, - "Bancontact CASH": 93, - "Bang & Olufsen": 3, - "Basic-Fit": 130, - "Belfius": 391, - "Beobank": 130, - "Bershka": 10, - "Best Western": 7, - "Beter Bed": 13, - "Big Bazar": 5, - "BigMat": 5, - "Blokker": 8, - "Blue-Bike": 16, - "Boels": 5, - "Bolia": 3, - "Bonjour": 8, - "Bosch Car Service": 3, - "Bravo": 27, - "Brice": 3, - "Brico": 130, - "Bristol": 88, - "Burger King": 49, - "C&A": 86, - "CASA": 22, - "COS": 4, - "Calvin Klein": 3, - "Calzedonia": 13, - "Camaïeu": 3, - "Campanile": 3, - "Carglass": 26, - "Caroll": 4, - "Carpetright": 11, - "Carrefour": 140, - "Carrefour Express": 232, - "Carrefour Market": 252, - "Cash Converters": 15, - "Celio": 16, - "Century 21": 24, - "Chateau d'Ax": 7, - "Chaussea": 19, - "Citroën": 83, - "Colruyt": 238, - "Coolblue": 7, - "Cora": 4, - "Courir": 7, - "Crelan": 203, - "Croix-Rouge de Belgique": 5, - "DATS 24": 162, - "DHL": 3, - "DVV": 19, - "Dacia": 13, - "Damart": 32, - "De Kringwinkel": 122, - "Decathlon": 35, - "Delhaize": 257, - "Deloitte": 3, - "Desigual": 5, - "Deutsche Bank": 21, - "Devernois": 3, - "Devred": 3, - "Domino's": 71, - "Donkey Republic": 7, - "Ducati": 5, - "Dunkin'": 8, - "Durex": 4, - "E5 Mode": 49, - "Ekoplaza": 7, - "Eldi": 28, - "Engel & Völkers": 7, - "Esprit": 25, - "Essentiel Antwerp": 4, - "Esso": 247, - "Esso Express": 105, - "Etam": 21, - "Europabank": 14, - "Europcar": 10, - "Eurospar": 17, - "Exki": 18, - "Expert": 9, - "Extra": 10, - "Ferrari": 3, - "Fiat": 26, - "Fintro": 60, - "First Stop": 3, - "Flying Tiger Copenhagen": 10, - "Fnac": 12, - "Foot Locker": 16, - "Ford": 75, - "Franck Provost": 5, - "G-Star Raw": 9, - "Gabriëls": 82, - "Game Mania": 11, - "Gamma": 76, - "Geox": 7, - "Gerry Weber": 15, - "GrandOptical": 20, - "Guess": 6, - "Gulf": 70, - "H&M": 53, - "HEMA": 66, - "Hans Anders": 61, - "Harley-Davidson": 7, - "Hermès": 3, - "Hertz": 3, - "Heytens": 13, - "Hilti": 3, - "Histoire d'Or": 12, - "Holiday Inn": 5, - "Holiday Inn Express": 3, - "Holland & Barrett": 8, - "Honda": 21, - "Hubo": 141, - "Hugo Boss": 7, - "Hunkemöller": 50, - "Hyundai": 42, - "Häagen-Dazs": 5, - "ICI PARIS XL": 122, - "IKEA": 8, - "IMO Car Wash": 7, - "ING": 324, - "Ibis": 18, - "Ibis Budget": 11, - "Ibis Styles": 4, - "Ikks": 6, - "Indigo": 17, - "Inno": 4, - "Intermarché": 86, - "Intermarché Contact": 3, - "Intermarché Super": 15, - "Intersport": 10, - "Intimissimi": 4, - "Ionity": 21, - "Ixina": 31, - "IzyCoffee": 15, - "JBC": 102, - "JD Sports": 5, - "JYSK": 32, - "Jack & Jones": 18, - "Jack Wolfskin": 3, - "Jaguar": 8, - "Jeep": 6, - "John Deere": 3, - "Jules": 18, - "Jumbo": 30, - "KBC": 401, - "KFC": 12, - "KIKO Milano": 7, - "Karl Lagerfeld": 3, - "Kawasaki": 4, - "Keurslager": 3, - "Kia": 32, - "Kiabi": 6, - "Kinepolis": 8, - "King Jouet": 3, - "Kreatos": 31, - "Kruidvat": 301, - "Krys": 5, - "Krëfel": 65, - "Kwantum": 9, - "Kärcher": 4, - "L'Occitane": 7, - "La Foir'Fouille": 3, - "Lacoste": 4, - "Ladbrokes": 36, - "Land Rover": 12, - "Le Creuset": 5, - "Le Pain Quotidien": 24, - "Leader Price": 6, - "Lecot": 7, - "Leen Bakker": 20, - "Leonidas": 177, - "Levi's": 11, - "Lexus": 10, - "Lidl": 304, - "Little Free Library": 49, - "Liu Jo": 4, - "Lloyds Pharmacy": 13, - "Longchamp": 4, - "Louis Vuitton": 3, - "Lovisa": 4, - "Lukoil": 173, - "Lunch Garden": 30, - "Lush": 4, - "MAC Cosmetics": 4, - "MS Mode": 9, - "Maes": 60, - "Maison Dandoy": 3, - "Maisons du Monde": 15, - "Mango": 13, - "Manpower": 10, - "Marc O'Polo": 3, - "Massimo Dutti": 8, - "Match": 14, - "Max Mara": 5, - "Maxi Toys": 14, - "Maxi Zoo": 36, - "Mazda": 31, - "McDonald's": 110, - "MediaMarkt": 14, - "Mephisto": 8, - "Mercedes-Benz": 56, - "Mercure": 7, - "Metro": 3, - "Michelin": 3, - "Midas": 15, - "Miele": 5, - "Mini": 12, - "Mini Mix": 3, - "Mister Minit": 8, - "Mitsubishi": 6, - "Mix Markt": 4, - "Mobalpa": 8, - "Mondial Relay": 6, - "Mr.Bricolage": 24, - "Multipharma": 140, - "My Jewellery": 5, - "Nespresso": 7, - "Neuhaus": 39, - "New Yorker": 5, - "Nicolas": 4, - "Nike": 7, - "Nissan": 45, - "Novotel": 7, - "O'Tacos": 27, - "ONLY": 17, - "Octa+": 12, - "Oil & Vinegar": 3, - "Okay": 140, - "Okaïdi": 22, - "Opel": 70, - "Optical Center": 8, - "Orange": 54, - "Orchestra": 8, - "Oxfam": 88, - "Palais des Thés": 5, - "Pandora": 13, - "Panos": 108, - "Paprika": 9, - "Park Inn": 5, - "Paul": 16, - "Pearle": 98, - "Pearle Vision": 5, - "Petit Bateau": 7, - "Peugeot": 85, - "Pharmacie Principale": 6, - "Photomaton": 5, - "Picard": 6, - "Pimkie": 3, - "Pizza Hut": 89, - "Point S": 3, - "Pokawa": 7, - "Polestar": 3, - "Poltronesofà": 4, - "Porsche": 12, - "Power": 57, - "Primark": 8, - "Promod": 9, - "Proximus": 34, - "Proxy Delhaize": 152, - "Pull & Bear": 5, - "Q8": 282, - "Q8 Easy": 119, - "Quick": 77, - "Randstad": 18, - "Regus": 3, - "Relay": 24, - "Renault": 73, - "Renmans": 53, - "Rexel": 8, - "Rituals": 41, - "Roche Bobois": 3, - "RougeGorge": 5, - "Sacha": 3, - "Schmidt": 6, - "Schoenen Torfs": 49, - "Schwalbe": 11, - "Scotch & Soda": 8, - "Seat": 16, - "Sergent Major": 11, - "Shell": 141, - "Shell Express": 109, - "Shop & Go": 15, - "Shurgard Storage Centers": 4, - "SieMatic": 3, - "Smatch": 9, - "Snipes": 8, - "Solaris": 3, - "Spar": 221, - "Speedy": 4, - "Sports Direct": 23, - "Springfield": 7, - "Standaard Boekhandel": 98, - "Starbucks": 31, - "Stella": 4, - "Street One": 4, - "Subway": 5, - "Suitsupply": 3, - "Superdry": 9, - "Sushi Shop": 3, - "Suzuki": 21, - "Swapfiets": 6, - "Swarovski": 13, - "Swiss Sense": 8, - "Synlab": 11, - "TUI": 38, - "Tamaris": 4, - "Tape à l'Œil": 11, - "Telenet": 15, - "Tempo-Team": 4, - "Tesla": 11, - "Tesla Supercharger": 17, - "Tesla, Inc.": 10, - "Texaco": 139, - "Tezenis": 4, - "The Body Shop": 8, - "The Little Gym": 5, - "Thomas Cook": 5, - "TinQ": 7, - "Tom & Co": 83, - "Tom Tailor": 3, - "Tommy Hilfiger": 11, - "Toolstation": 4, - "Total": 435, - "TotalEnergies": 86, - "ToyChamp": 12, - "Toyota": 88, - "Trafic": 21, - "Triumph": 7, - "UGC": 5, - "Ulla Popken": 4, - "Undiz": 6, - "Uniqlo": 3, - "Urban Outfitters": 3, - "VOO": 8, - "Van der Valk": 12, - "Vanden Borre": 56, - "Vans": 3, - "Velo": 223, - "Veritas": 53, - "Vero Moda": 4, - "Versace": 3, - "Villo!": 361, - "Volkswagen": 52, - "Volvo": 44, - "WE": 10, - "Weekday": 3, - "Western Union": 12, - "Wibra": 41, - "Women'secret": 4, - "Würth": 10, - "Yamaha": 10, - "Yves Rocher": 47, - "Zadig & Voltaire": 3, - "Zara": 22, - "Zara Home": 4, - "Zeeman": 165, - "claire's": 13, - "eyes + more": 5, - "pipoos": 4, - "s.Oliver": 3, - "vanHaren": 42, - "Škoda": 17, - "Лукойл": 3 -} +{"& Other Stories":3,"5àsec":5,"A.S.Adventure":26,"AD Delhaize":126,"ALDI":309,"AS 24":5,"AXA":172,"Accent Jobs":3,"Ace & Tate":6,"Action":171,"Adecco":21,"Alain Afflelou":22,"Albert Heijn":79,"Alfa Romeo":8,"Alvo":14,"America Today":6,"American Vintage":7,"Amplifon":24,"Apple Store":3,"Argenta":290,"Armand Thiery":7,"Attijariwafa Bank":3,"Audi":41,"Audika":5,"Aveve":77,"Avia":63,"Avis":7,"BMW":60,"BNP Paribas":34,"BNP Paribas Fortis":329,"BOGGI Milano":3,"BRAX":8,"Ba&sh":3,"Bancontact CASH":93,"Bang & Olufsen":3,"Basic-Fit":130,"Belfius":391,"Beobank":130,"Bershka":10,"Best Western":7,"Beter Bed":13,"Big Bazar":5,"BigMat":5,"Blokker":8,"Blue-Bike":16,"Boels":5,"Bolia":3,"Bonjour":8,"Bosch Car Service":3,"Bravo":27,"Brice":3,"Brico":130,"Bristol":88,"Burger King":49,"C&A":86,"CASA":22,"COS":4,"Calvin Klein":3,"Calzedonia":13,"Camaïeu":3,"Campanile":3,"Carglass":26,"Caroll":4,"Carpetright":11,"Carrefour":140,"Carrefour Express":232,"Carrefour Market":252,"Cash Converters":15,"Celio":16,"Century 21":24,"Chateau d'Ax":7,"Chaussea":19,"Citroën":83,"Colruyt":238,"Coolblue":7,"Cora":4,"Courir":7,"Crelan":203,"Croix-Rouge de Belgique":5,"DATS 24":162,"DHL":3,"DVV":19,"Dacia":13,"Damart":32,"De Kringwinkel":122,"Decathlon":35,"Delhaize":257,"Deloitte":3,"Desigual":5,"Deutsche Bank":21,"Devernois":3,"Devred":3,"Domino's":71,"Donkey Republic":7,"Ducati":5,"Dunkin'":8,"Durex":4,"E5 Mode":49,"Ekoplaza":7,"Eldi":28,"Engel & Völkers":7,"Esprit":25,"Essentiel Antwerp":4,"Esso":247,"Esso Express":105,"Etam":21,"Europabank":14,"Europcar":10,"Eurospar":17,"Exki":18,"Expert":9,"Extra":10,"Ferrari":3,"Fiat":26,"Fintro":60,"First Stop":3,"Flying Tiger Copenhagen":10,"Fnac":12,"Foot Locker":16,"Ford":75,"Franck Provost":5,"G-Star Raw":9,"Gabriëls":82,"Game Mania":11,"Gamma":76,"Geox":7,"Gerry Weber":15,"GrandOptical":20,"Guess":6,"Gulf":70,"H&M":53,"HEMA":66,"Hans Anders":61,"Harley-Davidson":7,"Hermès":3,"Hertz":3,"Heytens":13,"Hilti":3,"Histoire d'Or":12,"Holiday Inn":5,"Holiday Inn Express":3,"Holland & Barrett":8,"Honda":21,"Hubo":141,"Hugo Boss":7,"Hunkemöller":50,"Hyundai":42,"Häagen-Dazs":5,"ICI PARIS XL":122,"IKEA":8,"IMO Car Wash":7,"ING":324,"Ibis":18,"Ibis Budget":11,"Ibis Styles":4,"Ikks":6,"Indigo":17,"Inno":4,"Intermarché":86,"Intermarché Contact":3,"Intermarché Super":15,"Intersport":10,"Intimissimi":4,"Ionity":21,"Ixina":31,"IzyCoffee":15,"JBC":102,"JD Sports":5,"JYSK":32,"Jack & Jones":18,"Jack Wolfskin":3,"Jaguar":8,"Jeep":6,"John Deere":3,"Jules":18,"Jumbo":30,"KBC":401,"KFC":12,"KIKO Milano":7,"Karl Lagerfeld":3,"Kawasaki":4,"Keurslager":3,"Kia":32,"Kiabi":6,"Kinepolis":8,"King Jouet":3,"Kreatos":31,"Kruidvat":301,"Krys":5,"Krëfel":65,"Kwantum":9,"Kärcher":4,"L'Occitane":7,"La Foir'Fouille":3,"Lacoste":4,"Ladbrokes":36,"Land Rover":12,"Le Creuset":5,"Le Pain Quotidien":24,"Leader Price":6,"Lecot":7,"Leen Bakker":20,"Leonidas":177,"Levi's":11,"Lexus":10,"Lidl":304,"Little Free Library":49,"Liu Jo":4,"Lloyds Pharmacy":13,"Longchamp":4,"Louis Vuitton":3,"Lovisa":4,"Lukoil":173,"Lunch Garden":30,"Lush":4,"MAC Cosmetics":4,"MS Mode":9,"Maes":60,"Maison Dandoy":3,"Maisons du Monde":15,"Mango":13,"Manpower":10,"Marc O'Polo":3,"Massimo Dutti":8,"Match":14,"Max Mara":5,"Maxi Toys":14,"Maxi Zoo":36,"Mazda":31,"McDonald's":110,"MediaMarkt":14,"Mephisto":8,"Mercedes-Benz":56,"Mercure":7,"Metro":3,"Michelin":3,"Midas":15,"Miele":5,"Mini":12,"Mini Mix":3,"Mister Minit":8,"Mitsubishi":6,"Mix Markt":4,"Mobalpa":8,"Mondial Relay":6,"Mr.Bricolage":24,"Multipharma":140,"My Jewellery":5,"Nespresso":7,"Neuhaus":39,"New Yorker":5,"Nicolas":4,"Nike":7,"Nissan":45,"Novotel":7,"O'Tacos":27,"ONLY":17,"Octa+":12,"Oil & Vinegar":3,"Okay":140,"Okaïdi":22,"Opel":70,"Optical Center":8,"Orange":54,"Orchestra":8,"Oxfam":88,"Palais des Thés":5,"Pandora":13,"Panos":108,"Paprika":9,"Park Inn":5,"Paul":16,"Pearle":98,"Pearle Vision":5,"Petit Bateau":7,"Peugeot":85,"Pharmacie Principale":6,"Photomaton":5,"Picard":6,"Pimkie":3,"Pizza Hut":89,"Point S":3,"Pokawa":7,"Polestar":3,"Poltronesofà":4,"Porsche":12,"Power":57,"Primark":8,"Promod":9,"Proximus":34,"Proxy Delhaize":152,"Pull & Bear":5,"Q8":282,"Q8 Easy":119,"Quick":77,"Randstad":18,"Regus":3,"Relay":24,"Renault":73,"Renmans":53,"Rexel":8,"Rituals":41,"Roche Bobois":3,"RougeGorge":5,"Sacha":3,"Schmidt":6,"Schoenen Torfs":49,"Schwalbe":11,"Scotch & Soda":8,"Seat":16,"Sergent Major":11,"Shell":141,"Shell Express":109,"Shop & Go":15,"Shurgard Storage Centers":4,"SieMatic":3,"Smatch":9,"Snipes":8,"Solaris":3,"Spar":221,"Speedy":4,"Sports Direct":23,"Springfield":7,"Standaard Boekhandel":98,"Starbucks":31,"Stella":4,"Street One":4,"Subway":5,"Suitsupply":3,"Superdry":9,"Sushi Shop":3,"Suzuki":21,"Swapfiets":6,"Swarovski":13,"Swiss Sense":8,"Synlab":11,"TUI":38,"Tamaris":4,"Tape à l'Œil":11,"Telenet":15,"Tempo-Team":4,"Tesla":11,"Tesla Supercharger":17,"Tesla, Inc.":10,"Texaco":139,"Tezenis":4,"The Body Shop":8,"The Little Gym":5,"Thomas Cook":5,"TinQ":7,"Tom & Co":83,"Tom Tailor":3,"Tommy Hilfiger":11,"Toolstation":4,"Total":435,"TotalEnergies":86,"ToyChamp":12,"Toyota":88,"Trafic":21,"Triumph":7,"UGC":5,"Ulla Popken":4,"Undiz":6,"Uniqlo":3,"Urban Outfitters":3,"VOO":8,"Van der Valk":12,"Vanden Borre":56,"Vans":3,"Velo":223,"Veritas":53,"Vero Moda":4,"Versace":3,"Villo!":361,"Volkswagen":52,"Volvo":44,"WE":10,"Weekday":3,"Western Union":12,"Wibra":41,"Women'secret":4,"Würth":10,"Yamaha":10,"Yves Rocher":47,"Zadig & Voltaire":3,"Zara":22,"Zara Home":4,"Zeeman":165,"claire's":13,"eyes + more":5,"pipoos":4,"s.Oliver":3,"vanHaren":42,"Škoda":17,"Лукойл":3} \ No newline at end of file diff --git a/scripts/generateStats.ts b/scripts/generateStats.ts index 96ca4c398..952a54a4a 100644 --- a/scripts/generateStats.ts +++ b/scripts/generateStats.ts @@ -20,6 +20,7 @@ class Utilities { } } + class GenerateStats extends Script { async createOptimizationFile(includeTags = true) { @@ -71,8 +72,8 @@ class GenerateStats extends Script { tagTotal.set(key, new Map()) await Promise.all( Array.from(values).map(async (value) => { - const tagData: TagInfoStats= await TagInfo.global.getStats(key, value) - const count = tagData.data .find((item) => item.type === "all").count + const tagData: TagInfoStats = await TagInfo.global.getStats(key, value) + const count = tagData.data.find((item) => item.type === "all").count tagTotal.get(key).set(value, count) console.log(key + "=" + value, "-->", count) }) @@ -134,33 +135,33 @@ class GenerateStats extends Script { } - async createNameSuggestionIndexFile(basepath: string,type: "brand" | "operator") { - const path = basepath+type+'.json' + async createNameSuggestionIndexFile(basepath: string, type: "brand" | "operator" | string) { + const path = basepath + type + ".json" let allBrands = >>{} if (existsSync(path)) { allBrands = JSON.parse(readFileSync(path, "utf8")) - console.log("Loaded",Object.keys(allBrands).length," previously loaded brands") + console.log("Loaded", Object.keys(allBrands).length, " previously loaded brands") } - let lastWrite = new Date() + const lastWrite = new Date() let skipped = 0 const allBrandNames: string[] = Utils.Dedup(NameSuggestionIndex.allPossible(type).map(item => item.tags[type])) - for (let i = 0; i < allBrandNames.length; i++){ - if(i % 100 == 0){ - console.log("Downloading ",i+"/"+allBrandNames.length,"; skipped",skipped) + for (let i = 0; i < allBrandNames.length; i++) { + if (i % 100 == 0) { + console.log("Downloading ", i + "/" + allBrandNames.length, "; skipped", skipped) } const brand = allBrandNames[i] - if(!!allBrands[brand] && Object.keys(allBrands[brand]).length == 0){ + if (!!allBrands[brand] && Object.keys(allBrands[brand]).length == 0) { delete allBrands[brand] console.log("Deleted", brand, "as no entries at all") } - if(allBrands[brand] !== undefined){ + if (allBrands[brand] !== undefined) { const max = Math.max(...Object.values(allBrands[brand])) skipped++ - if(max < 0){ + if (max < 0) { console.log("HMMMM:", allBrands[brand]) delete allBrands[brand] - }else{ + } else { continue } } @@ -179,11 +180,13 @@ class GenerateStats extends Script { } async main(_: string[]) { - await this.createOptimizationFile() - const type = "brand" const basepath = "./src/assets/generated/stats/" - await this.createNameSuggestionIndexFile(basepath, type) - this.summarizeNSI(basepath+type+".json", "./public/assets/data/stats/"+type) + for (const type of ["operator","brand"]) { + await this.createNameSuggestionIndexFile(basepath, type) + this.summarizeNSI(basepath + type + ".json", "./public/assets/data/nsi/stats/" + type) + } + await this.createOptimizationFile() + } diff --git a/src/Logic/Web/NameSuggestionIndex.ts b/src/Logic/Web/NameSuggestionIndex.ts index 604852072..7438297be 100644 --- a/src/Logic/Web/NameSuggestionIndex.ts +++ b/src/Logic/Web/NameSuggestionIndex.ts @@ -58,6 +58,10 @@ export interface NSIItem { export default class NameSuggestionIndex { private static readonly nsiFile: Readonly = nsi + private static readonly nsiWdFile: Readonly> = nsiWD["wikidata"] + private static loco = new LocationConflation(nsiFeatures) // Some additional boundaries private static _supportedTypes: string[] @@ -68,7 +72,12 @@ export default class NameSuggestionIndex { } const keys = Object.keys(NameSuggestionIndex.nsiFile.nsi) const all = keys.map(k => NameSuggestionIndex.nsiFile.nsi[k].properties.path.split("/")[0]) - this._supportedTypes = Utils.Dedup(all) + this._supportedTypes = Utils.Dedup(all).map(s => { + if(s.endsWith("s")){ + s = s.substring(0, s.length - 1) + } + return s + }) return this._supportedTypes } @@ -82,7 +91,6 @@ export default class NameSuggestionIndex { private static async fetchFrequenciesFor(type: string, countries: string[]) { let stats = await Promise.all(countries.map(c => { try { - return Utils.downloadJsonCached>(`./assets/data/nsi/stats/${type}.${c.toUpperCase()}.json`, 24 * 60 * 60 * 1000) } catch (e) { console.error("Could not fetch " + type + " statistics due to", e) @@ -93,6 +101,9 @@ export default class NameSuggestionIndex { if (stats.length === 1) { return stats[0] } + if(stats.length === 0){ + return {} + } const merged = stats[0] for (let i = 1; i < stats.length; i++) { for (const countryCode in stats[i]) { @@ -103,10 +114,7 @@ export default class NameSuggestionIndex { } public static isSvg(nsiItem: NSIItem, type: string): boolean | undefined { - const logos = nsiWD["wikidata"][nsiItem?.tags?.[type + ":wikidata"]]?.logos - if(nsiItem.id === "axa-2f6feb"){ - console.trace(">>> HI") - } + const logos = this.nsiWdFile[nsiItem?.tags?.[type + ":wikidata"]]?.logos if (!logos) { return undefined } @@ -120,37 +128,45 @@ export default class NameSuggestionIndex { return false } - public static async generateMappings(type: string, key: string, value: string, country: string[], location?: [number, number]) { + public static async generateMappings(type: string, tags: Record, country: string[], location?: [number, number]): Promise { const mappings: Mapping[] = [] const frequencies = await NameSuggestionIndex.fetchFrequenciesFor(type, country) - const actualBrands = NameSuggestionIndex.getSuggestionsFor(type, key, value, country.join(";"), location) - for (const nsiItem of actualBrands) { - const tags = nsiItem.tags - const frequency = frequencies[nsiItem.displayName] - const logos = nsiWD["wikidata"][nsiItem.tags[type + ":wikidata"]]?.logos - let iconUrl = logos?.facebook ?? logos?.wikidata - const hasIcon = iconUrl !== undefined - let icon = undefined - if (hasIcon) { - // Using works fine without an extension for JPG and PNG, but _not_ svg :( - icon = "./assets/data/nsi/logos/" + nsiItem.id - if (NameSuggestionIndex.isSvg(nsiItem, type)) { - console.log("Is svg:", nsiItem.displayName) - icon = icon + ".svg" - } + for (const key in tags) { + if (key.startsWith("_")) { + continue + } + const value = tags[key] + const actualBrands = NameSuggestionIndex.getSuggestionsForKV(type, key, value, country.join(";"), location) + if(!actualBrands){ + continue + } + for (const nsiItem of actualBrands) { + const tags = nsiItem.tags + const frequency = frequencies[nsiItem.displayName] + const logos = this.nsiWdFile[nsiItem.tags[type + ":wikidata"]]?.logos + const iconUrl = logos?.facebook ?? logos?.wikidata + const hasIcon = iconUrl !== undefined + let icon = undefined + if (hasIcon) { + // Using works fine without an extension for JPG and PNG, but _not_ svg :( + icon = "./assets/data/nsi/logos/" + nsiItem.id + if (NameSuggestionIndex.isSvg(nsiItem, type)) { + icon = icon + ".svg" + } + } + mappings.push({ + if: new Tag(type, tags[type]), + addExtraTags: Object.keys(tags).filter(k => k !== type).map(k => new Tag(k, tags[k])), + then: new TypedTranslation>({ "*": nsiItem.displayName }), + hideInAnswer: false, + ifnot: undefined, + alsoShowIf: undefined, + icon, + iconClass: "medium", + priorityIf: frequency > 0 ? new RegexTag("id", /.*/) : undefined, + searchTerms: { "*": [nsiItem.displayName, nsiItem.id] } + }) } - mappings.push({ - if: new Tag(type, tags[type]), - addExtraTags: Object.keys(tags).filter(k => k !== type).map(k => new Tag(k, tags[k])), - then: new TypedTranslation<{}>({ "*": nsiItem.displayName }), - hideInAnswer: false, - ifnot: undefined, - alsoShowIf: undefined, - icon, - iconClass: "medium", - priorityIf: frequency > 0 ? new RegexTag("id", /.*/) : undefined, - searchTerms: { "*": [nsiItem.displayName, nsiItem.id] } - }) } return mappings } @@ -184,7 +200,7 @@ export default class NameSuggestionIndex { for (const osmKey in tags) { const values = tags[osmKey] for (const osmValue of values) { - const suggestions = this.getSuggestionsFor(type, osmKey, osmValue) + const suggestions = this.getSuggestionsForKV(type, osmKey, osmValue) options.push(...suggestions) } } @@ -193,11 +209,19 @@ export default class NameSuggestionIndex { /** * - * @param path * @param country: a string containing one or more country codes, separated by ";" * @param location: center point of the feature, should be [lon, lat] */ - public static getSuggestionsFor(type: string, key: string, value: string, country: string = undefined, location: [number, number] = undefined): NSIItem[] { + public static getSuggestionsFor(type: string, tags: {key: string, value: string}[], country: string = undefined, location: [number, number] = undefined): NSIItem[] { + return tags.flatMap(tag => this.getSuggestionsForKV(type, tag.key, tag.value, country, location)) + } + + /** + * + * @param country: a string containing one or more country codes, separated by ";" + * @param location: center point of the feature, should be [lon, lat] + */ + public static getSuggestionsForKV(type: string, key: string, value: string, country: string = undefined, location: [number, number] = undefined): NSIItem[] { const path = `${type}s/${key}/${value}` const entry = NameSuggestionIndex.nsiFile.nsi[path] return entry?.items?.filter(i => { diff --git a/src/Models/ThemeConfig/Conversion/Validation.ts b/src/Models/ThemeConfig/Conversion/Validation.ts index 8a348b245..c6e198fa8 100644 --- a/src/Models/ThemeConfig/Conversion/Validation.ts +++ b/src/Models/ThemeConfig/Conversion/Validation.ts @@ -927,8 +927,10 @@ class CheckTranslation extends DesugaringStep { } class MiscTagRenderingChecks extends DesugaringStep { - constructor() { + private readonly _layerConfig: LayerConfigJson + constructor(layerConfig?: LayerConfigJson) { super("Miscellaneous checks on the tagrendering", ["special"], "MiscTagRenderingChecks") + this._layerConfig = layerConfig } convert( @@ -1092,13 +1094,14 @@ class MiscTagRenderingChecks extends DesugaringStep { ) } } - if(json.freeform.type === "nsi"){ - const [key, value] = json.freeform.helperArgs[0].split("=") - const path = `${json.freeform.key}s/${key}/${value}` - const suggestions = NameSuggestionIndex.getSuggestionsFor(json.freeform.key, key, value) + if(this._layerConfig?.source?.osmTags && NameSuggestionIndex.supportedTypes().indexOf(json.freeform.key) >= 0){ + const tags= TagUtils.TagD(this._layerConfig?.source?.osmTags)?.usedTags() + const suggestions = NameSuggestionIndex.getSuggestionsFor(json.freeform.key, tags) if(suggestions === undefined){ - context.enters("freeform","type").err("No entry found in the 'Name Suggestion Index' for "+path) + context.enters("freeform","type").err("No entry found in the 'Name Suggestion Index'. None of the 'osmSource'-tags match an entry in the NSI.\n\tOsmSource-tags are "+tags.map(t => t.asHumanString()).join(" ; ")) } + }else if(json.freeform.type === "nsi"){ + context.enters("freeform","type").warn("No need to explicitly set type to 'NSI', autodetected based on freeform type") } } if (json.render && json["question"] && json.freeform === undefined) { @@ -1145,7 +1148,7 @@ export class ValidateTagRenderings extends Fuse { constructor(layerConfig?: LayerConfigJson, doesImageExist?: DoesImageExist) { super( "Various validation on tagRenderingConfigs", - new MiscTagRenderingChecks(), + new MiscTagRenderingChecks(layerConfig), new DetectShadowedMappings(layerConfig), new DetectMappingsShadowedByCondition(), @@ -1156,7 +1159,7 @@ export class ValidateTagRenderings extends Fuse { new On("question", new ValidatePossibleLinks()), new On("questionHint", new ValidatePossibleLinks()), new On("mappings", new Each(new On("then", new ValidatePossibleLinks()))), - new MiscTagRenderingChecks() + new MiscTagRenderingChecks(layerConfig) ) } } diff --git a/src/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson.ts b/src/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson.ts index 8642e3d31..75cde452a 100644 --- a/src/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson.ts +++ b/src/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson.ts @@ -233,12 +233,6 @@ export interface QuestionableTagRenderingConfigJson extends TagRenderingConfigJs */ placeholder?: Translatable - /** - * Extra parameters to initialize the input helper arguments. - * For semantics, see the 'SpecialInputElements.md' - * group: expert - */ - helperArgs?: (string | number | boolean | any)[] /** * If a value is added with the textfield, these extra tag is addded. * Useful to add a 'fixme=freeform textfield used - to be checked' diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index ac5ee404a..613546a94 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -72,12 +72,11 @@ export default class TagRenderingConfig { readonly addExtraTags: UploadableTag[] readonly inline: boolean readonly default?: string - readonly helperArgs?: (string | number | boolean)[] } public readonly multiAnswer: boolean - public readonly mappings: Mapping[] + public mappings: Mapping[] public readonly editButtonAriaLabel?: Translation public readonly labels: string[] public readonly classes: string[] | undefined @@ -202,8 +201,7 @@ export default class TagRenderingConfig { TagUtils.ParseUploadableTag(tg, `${context}.extratag[${i}]`) ) ?? [], inline: json.freeform.inline ?? false, - default: json.freeform.default, - helperArgs: json.freeform.helperArgs + default: json.freeform.default } if (json.freeform["extraTags"] !== undefined) { throw `Freeform.extraTags is defined. This should probably be 'freeform.addExtraTag' (at ${context})` @@ -251,7 +249,7 @@ export default class TagRenderingConfig { commonIconSize ) ) - }else{ + } else { this.mappings = [] } @@ -371,8 +369,8 @@ export default class TagRenderingConfig { let icon = undefined let iconClass = commonSize - if (!!mapping.icon) { - if (typeof mapping.icon === "string" && mapping.icon !== "") { + if (mapping.icon) { + if (typeof mapping.icon === "string") { icon = mapping.icon.trim() } else if (mapping.icon["path"]) { icon = mapping.icon["path"].trim() @@ -467,8 +465,8 @@ export default class TagRenderingConfig { // A flag to check that the freeform key isn't matched multiple times // If it is undefined, it is "used" already, or at least we don't have to check for it anymore - let freeformKeyDefined = this.freeform?.key !== undefined - let usedFreeformValues = new Set() + const freeformKeyDefined = this.freeform?.key !== undefined + const usedFreeformValues = new Set() // We run over all the mappings first, to check if the mapping matches const applicableMappings: { then: TypedTranslation> @@ -556,9 +554,6 @@ export default class TagRenderingConfig { EnumerateTranslations(): Translation[] { const translations: Translation[] = [] for (const key in this) { - if (!this.hasOwnProperty(key)) { - continue - } const o = this[key] if (o instanceof Translation) { translations.push(o) @@ -572,7 +567,7 @@ export default class TagRenderingConfig { const key = this.freeform?.key const answerMappings = this.mappings?.filter((m) => m.hideInAnswer !== true) if (key === undefined) { - let values: { k: string; v: string }[][] = Utils.NoNull( + const values: { k: string; v: string }[][] = Utils.NoNull( answerMappings?.map((m) => m.if.asChange({})) ?? [] ) if (values.length === 0) { @@ -870,21 +865,25 @@ export default class TagRenderingConfig { export class TagRenderingConfigUtils { public static withNameSuggestionIndex(config: TagRenderingConfig, tags: UIEventSource>, feature?: Feature): Store { - if(config.freeform?.type !== "nsi"){ + const isNSI = NameSuggestionIndex.supportedTypes().indexOf(config.freeform?.key) >= 0 + if (!isNSI) { return new ImmutableStore(config) } - const extraMappings = tags.mapD(tags => tags._country).bindD(country => { - const [k, v] = ("" + config.freeform.helperArgs[0]).split("=") - const center = GeoOperations.centerpointCoordinates(feature) - return UIEventSource.FromPromise(NameSuggestionIndex.generateMappings(config.freeform.key, k, v, country.split(";"), center)) - }) - return extraMappings.map(extraMappings => { - if(!extraMappings || extraMappings.length == 0){ + const extraMappings = tags + .bindD(tags => { + const country = tags._country + if(country === undefined){ + return undefined + } + const center = GeoOperations.centerpointCoordinates(feature) + return UIEventSource.FromPromise(NameSuggestionIndex.generateMappings(config.freeform.key, tags, country.split(";"), center)) + }) + return extraMappings.map(extraMappings => { + if (!extraMappings || extraMappings.length == 0) { return config } const clone: TagRenderingConfig = Object.create(config) - /// SHHHTTT, this is not cheating at all! - clone.mappings.splice(clone.mappings.length, 0, ...extraMappings) + clone.mappings = [...clone.mappings ?? [], ...extraMappings] return clone }) } diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index 0ce24eafc..9ae12e26c 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -391,7 +391,6 @@ export default class ThemeViewState implements SpecialVisualizationState { if (fs.layer.layerDef.source.geojsonSource !== undefined) { return // We don't cache external data layers } - console.log("Setting up a local store feature sink for", layerId) const storage = new SaveFeatureSourceToLocalStorage( this.osmConnection.Backend(), fs.layer.layerDef.id, diff --git a/src/UI/Base/CloseAnimation.svelte b/src/UI/Base/CloseAnimation.svelte index 0c2ed2838..e16473179 100644 --- a/src/UI/Base/CloseAnimation.svelte +++ b/src/UI/Base/CloseAnimation.svelte @@ -18,7 +18,6 @@ function animate(opened: boolean) { const moveToElem = moveTo.data -console.log("Animating", debug," to", opened) if (opened) { copySizeOf(targetOuter) elem.style.background = "var(--background-color)" diff --git a/src/UI/BigComponents/SelectedElementView.svelte b/src/UI/BigComponents/SelectedElementView.svelte index 17e24e44f..09ca04744 100644 --- a/src/UI/BigComponents/SelectedElementView.svelte +++ b/src/UI/BigComponents/SelectedElementView.svelte @@ -11,7 +11,6 @@ import UserRelatedState from "../../Logic/State/UserRelatedState" import Delete_icon from "../../assets/svg/Delete_icon.svelte" import BackButton from "../Base/BackButton.svelte" - import TagRenderingEditableDynamic from "../Popup/TagRendering/TagRenderingEditableDynamic.svelte" export let state: SpecialVisualizationState export let selectedElement: Feature @@ -69,7 +68,7 @@ tabindex="-1" > {#each $knownTagRenderings as config (config.id)} - + import TagRenderingConfig, { TagRenderingConfigUtils } from "../../../Models/ThemeConfig/TagRenderingConfig" + import type { SpecialVisualizationState } from "../../SpecialVisualization" + import type { Feature } from "geojson" + import { UIEventSource } from "../../../Logic/UIEventSource" + import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" + import TagRenderingAnswer from "./TagRenderingAnswer.svelte" + + export let tags: UIEventSource | undefined> + + export let state: SpecialVisualizationState + export let selectedElement: Feature + export let layer: LayerConfig + export let config: TagRenderingConfig + export let extraClasses: string | undefined = undefined + + export let id: string = undefined + let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement) + + + + diff --git a/src/UI/Popup/TagRendering/TagRenderingEditable.svelte b/src/UI/Popup/TagRendering/TagRenderingEditable.svelte index 8bbb001dd..635d97698 100644 --- a/src/UI/Popup/TagRendering/TagRenderingEditable.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingEditable.svelte @@ -3,8 +3,6 @@ import { Store, UIEventSource } from "../../../Logic/UIEventSource" import type { Feature } from "geojson" import type { SpecialVisualizationState } from "../../SpecialVisualization" - import TagRenderingAnswer from "./TagRenderingAnswer.svelte" - import { PencilAltIcon } from "@rgossiaux/svelte-heroicons/solid" import TagRenderingQuestion from "./TagRenderingQuestion.svelte" import { onDestroy } from "svelte" import Tr from "../../Base/Tr.svelte" @@ -12,9 +10,8 @@ import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import { Utils } from "../../../Utils" import { twMerge } from "tailwind-merge" - import { ariaLabel } from "../../../Utils/ariaLabel" import EditButton from "./EditButton.svelte" - import EditItemButton from "../../Studio/EditItemButton.svelte" + import TagRenderingAnswerDynamic from "./TagRenderingAnswerDynamic.svelte" export let config: TagRenderingConfig export let tags: UIEventSource> @@ -103,7 +100,7 @@ {:else}
- + - +
{/if} diff --git a/src/UI/Popup/TagRendering/TagRenderingEditableDynamic.svelte b/src/UI/Popup/TagRendering/TagRenderingEditableDynamic.svelte deleted file mode 100644 index f018bb33f..000000000 --- a/src/UI/Popup/TagRendering/TagRenderingEditableDynamic.svelte +++ /dev/null @@ -1,32 +0,0 @@ - - - diff --git a/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte b/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte index 91a036a95..7fed07be3 100644 --- a/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte @@ -72,7 +72,6 @@ /** * Prepares and fills the checkedMappings */ - console.log("Initing ", config.id) function initialize(tgs: Record, confg: TagRenderingConfig): void { mappings = confg.mappings?.filter((m) => { @@ -160,7 +159,6 @@ } } if (somethingChanged) { - console.log("Updating minimal tags to", newMinimal, "of", config.id) minimalTags.setData(newMinimal) } }) @@ -224,7 +222,7 @@ } } - if (extraTags.data) { + if (extraTags.data && selectedTags !== undefined) { // Map the extraTags into an array of Tag objects const extraTagsArray = Object.entries(extraTags.data).map(([k, v]) => new Tag(k, v)) @@ -236,7 +234,7 @@ // Add the extraTags to the existing And selectedTags = new And([...selectedTags.and, ...extraTagsArray]) } else { - console.error("selectedTags is not of type Tag or And") + console.error("selectedTags is not of type Tag or And, it is a "+JSON.stringify(selectedTags)) } } } diff --git a/src/test.ts b/src/test.ts index d27cca31a..2fd82def8 100644 --- a/src/test.ts +++ b/src/test.ts @@ -2,7 +2,4 @@ import SvelteUIElement from "./UI/Base/SvelteUIElement" import Test from "./UI/Test.svelte" import NameSuggestionIndex from "./Logic/Web/NameSuggestionIndex" -const nsi = NameSuggestionIndex.get() -const secondhandshops = nsi.getSuggestionsFor("brands/shop/second_hand", ["be"]) -console.log(secondhandshops) new SvelteUIElement(Test).AttachTo("maindiv")