diff --git a/README.md b/README.md index 2d976c9..2fba972 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,8 @@ APP_SETTINGS="luupsmap.config.DevelopmentConfig" SECRET_KEY= +DB_NAME= +DB_ADDRESS= DB_USER = DB_PW = @@ -99,4 +101,11 @@ flask data update ``` This will update the CSV with missing location data and venue data. For more information see -[update.py](/luupsmap/cli/commands/update.py). +[update.py](/luupsmap/cli/commands/update.py). In order to set opening hours for a specific location edit the column +`opening_hours`. The data seeder expects opening hours of the format: + +``` +Day[-Day] Hour[-Hour] [Month[-Month]], ... +``` + +where expressions in brackets are optional, days and months are shortened english names (e.g. `Mo, Tu, We`, `May, Jun, Jul`) and hours have either the long format `hh:mm` or simply `hh`. diff --git a/data/locations.csv b/data/locations.csv index efc3d70..168b60f 100644 --- a/data/locations.csv +++ b/data/locations.csv @@ -1,112 +1,112 @@ -name | address | email | phone | opening_hours | latitude | longitude -12 - Karma Food | Ausstellungsstr. 63, 1020 Wien, Österreich | hallo@12.co.at | +43 676 914 8012 | Mo-Fr 9-17, Sa. 9-15 | 48.2181676 | 16.4078821 -12 - Karma Food | Laurenzerberg 3, 1010 Wien, Österreich | hallo@12.co.at | +43 676 913 8012 | Mo-Fr 9-17, Sa. 9-15 | 48.2109296 | 16.3778659 -1683 Handmade Bagels & Farm Coffee | Währinger Str. 12, 1090 Wien, Österreich | | +43 664 2639936 | Mo-Fr. 7:30-18:30, Sa + So + Feiertags 9:30-18:30 | 48.2167778 | 16.3600738 -Achtundzwanzig | Schlösselgasse 28, 1080 Wien, Österreich | | +43 664 562 3786 | Mo-Fr. 16-1, Sa. 18-2 | 48.2145523 | 16.3532458 -Adamah Biohof | Glinzendorf 7, 2282 Glinzendorf, Österreich | | +43 2248 2224 | | 48.2466401 | 16.6427625 -Allergiker Café | Wiedner Hauptstr. 35, 1040 Wien, Österreich | kontakt@allergikercafe.at | +43 660 5371007 | Di-Fr. 9:30-19,Sa. 10-16 | 48.1944784 | 16.3673893 -Archäonow | Weihburggasse 21, 1010 Wien, Österreich | | +43 2248 2224 | | 48.2059213 | 16.37517 -Arnulf Rainer Museum | Josefspl. 5, 2500 Baden, Österreich | office@arnulf-rainer-museum.at | +43 2252 209196 | | 48.0063245 | 16.2337794 -Au - Raum für Kunst und Kultur | Brunnengasse 76, 1160 Wien, Österreich | | | So-Do+Feiertag 18-2h, Fr-Sa. 18-4 | 48.2143648 | 16.3371565 -Avalon Kultur | Pfeilgasse 27, 1080 Wie, Österreichn | | +43 1 942 5365 | Mo-Fr. 17-20, Sa 18-20 | 48.2089484 | 16.3434956 -Bäckerei Felzl | Lerchenfelderstr. 99-101, 1070 Wien, Österreich | | +43 1 522 3809 | Mo-Fr. 6-19, Sa. 6-17, So + Feiertag 7-12:30 | 48.2074219 | 16.3435234 -Bank Austria Kunstforum Wien | Freyung 8, 1010 Wien, Österreich | | +43 1 5373326 | | 48.211486 | 16.3662557 -Bao Bar | Zollergasse 2, 1070 Wien, Österreich | | | Mo-Sa. 10-20h | 48.1992751 | 16.3512409 -Baschly 1090 | Schwarzspanierstr. 22, 1090 Wien, Österreich | | +43 1 729 7900 | Mo-Do. 11-19 | 48.2170063 | 16.3589762 -Beaver Brewing Company | Liechtensteinstraße 69, 1090 Wien, Österreich | office@beaverbrewing.at | +43 677 610 12253 | Mo-Do 16-0, Fr. 16-1, Sa. 10-1, So. 10-22 | 48.2245931 | 16.3569675 -Biodeli | Gumpendorfer Str. 36, 1060 Wien, Österreich | servus@biodeli.at | +43 1 347 3335 | Mo-Fr 9-16 | 48.1987291 | 16.3575349 -Biodeli | Otto-Bauer-Gasse 11, 1060 Wien, Österreich | servus@biodeli.at | +43 1 347 3335 | Mo-Fr 9-16 | 48.1957689 | 16.3487646 -Bio-Pizzeria Vero | Währinger Gürtel 162, 1090 Wien, Österreich | | +43 1 310 1179 | Mo-Fr. 11:30-14:30 & 17:30-22:30, Sa + So + Feiertag 11:30-22:30 | 48.230901 | 16.3525584 -Bits and Bites | Webgasse 27, 1060 Wien, Österreich | office@bitsandbites.at | +43 660 8372509 | Mi-Sa. 18-22h, Mi-So. 10-15h | 48.1945438 | 16.3458842 -Black Dogs Coffee | Wallgasse 27, 1060 Wien, Österreich | | +43 664 536 6088 | Di-Sa. 9-12:30 + 17-22 | 48.1922288 | 16.3393213 -Blün | Schafflerhofstraße 156, 1220 Wien, Österreich | | +43 1 7741333 | | 48.2281848 | 16.5364981 -Botanical Garden | Kolingasse 1, 1090 Wien, Österreich | | +43 676 4422553 | Mo-Do 17-2, Fr+Sa 17-3 | 48.21553429999999 | 16.361423 -Brasseria De La Marie | Amerlingstr. 15, 1060 Wien, Österreich | | +43 664 154 7737 | Mo-Fr. 11-23, Sa 10-23:45 | 48.1974896 | 16.3505101 -Brauwerk | Ottakringer Pl. 1, 1160 Wien, Österreich | | +43 1 491 005 480 | Siehe Homepage | 48.212931 | 16.3248221 -Brut | Karlsplatz 5, 1010 Wien, Österreich | | +43 1 5878774 | | 48.2006328 | 16.3720546 -Buchcafè Melange | Reindorfgasse 42, 1150 Wien, Österreich | | +43 677 619 705 28 | Di – Fr. 10-13 & 13:30-18:30, Sa. 10-15 | 48.1908533 | 16.3301859 -Burg Kino | Opernring 19, 1010 Wien, Österreich | | +43 1 5878406 | | 48.2030519 | 16.3650429 -Buschenschank & Bioweinbau Obermann | Cobenzlgasse 102, 1190 Wien, Österreich | | +43 664 451 9927 | Mi-So, ab Okt: Fr-So | 48.2620386 | 16.332417 -Café Caspar | Grillparzerstr. 6, 1010 Wien, Österreich | hallo@cafecaspar.com | +43 1 957 6793 | Mo-Fr. 11-23 | 48.2126564 | 16.3581608 -Café Comet / Fürth Kaffee | Kirchengasse 44, 1070 Wien, Austria | | +43 664 1303066 | | 48.2044242 | 16.351686 -Café Klitzeklein | Ausstellungsstraße 2, 1020 Wien, Österreich | | | Siehe Homepage | 48.2179008 | 16.3952356 -Café Zeitgenossin | Billrothstraße 18, 1190 Wien, Austria | | +43 699 1073 8865 | Mo. 12-17:30, Di-Fr. 9:30-17:30 | 48.23639249999999 | 16.3494354 -Café Z | Meiselstr. 2, 1150 Wien, Österreich | office@cafe-z.at | +43 680 308 7234 | Di-Sa. 10-22, So. 10-17 | 48.1974825 | 16.3232923 -Corns N'Pops | Gumpendorfer Str. 37, 1060 Wien, Österreich | | +43 664 131 2005 | Mo-Fr. 8-17, Sa. Siehe Homepage | 48.1986689 | 16.3580129 -Creme de la Creme | 76, Lange G., 1080 Wien, Österreich | hallo@cremedelacreme.at | +43 660 283 3769 | Di-Fr. 9-18, Sa-So 10-17 | 48.2144043 | 16.3511785 -Cyberlab | Schottenfeldgasse 51, 1070 Wien, Österreich | | +43 1 522400400 | | 48.202167 | 16.3430246 -Das Lokal Im Hof | Weyringerg. 36, 1040 Wien, Österreich | | +43 1 971 1914 | Mo-Fr. 11-23 | 48.1883846 | 16.3742402 -Deli Bluem | Hamerlingpl. 2, 1080 Wien, Österreich | | +43 1 8900 449 | Mo-Fr. 8-19h, Sa+So 9-18 | 48.211378 | 16.344551 -Deli Bluem | Laudongasse 17, 1080 Wien, Österreich | | +43 1 8900 449 | Mo-Fr. 8-19h, Sa+So 9-18 | 48.213209 | 16.350719 -Dialog im Dunkeln | Freyung 6, 1010 Wien, Österreich | | +43 1 8906060 | | 48.21231 | 16.3643799 -Die Buntique | Kirchengasse 26, 1070 Wien, Österreich | | +43 664 75110469 | | 48.20167 | 16.3520428 -Die Süsse | Phorusgassse 8, 1040 Wien, Österreich | | +43 699 11419453 | Mo-Fr. 10-18 | 48.1901246 | 16.3638214 -Donuteria | Landstraßer Hauptstr. 71/G01, 1030 Wien, Österreich | | +43 660 738 7264 | Mo-Fr. 9-19, Sa 9-18 | 48.2012309 | 16.3934624 -Dritte Mann Museum | Preßgasse 25, 1040 Wien, Österreich | | +43 1 5864872 | | 48.1960993 | 16.3624638 -Elefant & Castle | Neubaugasse 45, 1070 Wien, Österreich | | +43 699 192 08 459 | Mo-Fr. 11:30-19:30 | 48.20258640000001 | 16.3489424 -Fesch'Markt Wien | Ottakringer Str. 83, 1160 Wien, Österreich | | | | 48.21303169999999 | 16.3250144 -Gartenbaukino | Parkring 12, 1010 Wien, Österreich | | +43 1 5122354 | | 48.2056486 | 16.3783174 -Hands Up | Freyung 6, 1010 Wien, Österreich | | +43 1 3193701 | | 48.2123992 | 16.3644022 -Hase und Igel | Theobaldgasse 16, 1060 Wien, Österreich | | +43 1 971 6029 | Mo-Fr. 11-20, Sa. 11-18 | 48.2002855 | 16.3595533 -Haus der Musik | Seilerstätte 30, 1010 Wien, Österreich | | +43 1 5134850 | | 48.2038401 | 16.3730192 -Iko | Wipplingerstr. 6, 1010 Wien, Österreich | | +43 1 890 4200 | Mo-Fr. 11-22, Sa 12-22 | 48.2117344 | 16.3713102 -Jetzt Bar & Entertainment | Parhamerpl. 16, 1170 Wien, Österreich | info@dasjetzt.at | +43 1 4857 680 | Mo-Do 1803, Fr+Sa 18-4, So 18-2 | 48.2165786 | 16.3276148 -J. Hornig Kaffebar | Siebensterngasse 29, 1070 Wien, Österreich | kaffeebar@jhornig.at | +43 1 522 2251 | Mo-Fr 7:30-19, Sa+So+Feiertage 9-19h | 48.20203919999999 | 16.3522683 -Jonas Reindl Coffee | Währinger Str. 2-4, 1090 Wien, Österreich | | | Mo-Fr. 7:30-22, Sa. 10-22 | 48.2149624 | 16.3617342 -Kaffeefabrik | Favoritenstraße 4, 1040 Wien, Österreich | | +43 660 1789 092 | Mo-Fr.8-18, Sa. 11-17 | 48.1958824 | 16.3684117 -Kaffeemodul | Josefstädter Str. 35, 1080 Wien, Österreich | | +43 680 2228039 | Mo-Fr 7:30-17:30, Sa. 10-14 | 48.2095175 | 16.348442 -Kaffemik | Zollergasse 5/2, 1070 Wien, Österreich | | +43 650 4141 535 | Mo-Fr 8-18, Sa 10-18, So+Feiertags 12-18 | 48.1991733 | 16.350998 -Kartonage Kitchen | Burggasse 6-8, 1070 Wien, Österreich | | +43 660 6387346 | Mo-Fr 8-18:30 | 48.2043708 | 16.3554785 -Kauf Dich Glücklich | Kirchengasse 9, 1070 Wien, Österreich | | +43 1 9247755 | | 48.200681 | 16.3522777 -Kunsthalle Wien | Museumsplatz 1, 1070 Wien, Österreich | | +43 1 521890 | | 48.2026499 | 16.3591421 -Kunst Haus Wien Museum Hundertwasser | Untere Weißgerberstraße 13, 1030 Wien, Österreich | | +43 1 7120491 | | 48.211074 | 16.393286 -Leones Gelato | Lange Gasse 78, 1080 Wien, Österreich | ciao@leones.at | +43 1 352 5252 | Siehe Homepage | 48.21468309999999 | 16.3511642 -Leones Gelato | Praterstr. 16, 1020 Wien, Österreich | ciao@leones.at | +43 1 352 5252 | Siehe Homepage | 48.2133617 | 16.3822807 -Lingenhel | Landstraßer Hauptstr. 74, 1030 Wien, Österreich | | +43 1 710 1566 | Mo-Sa 8-22 | 48.2009278 | 16.391988 -Lunzers Mass-Greisslerei | Heinestr. 35, 1020 Wien, Österreich | | +43 1 212 1387 | Mo-Fr 9-19, Sa 9-17 | 48.2196131 | 16.3880717 -Macaroom | Landstraßer Hauptstr. 86, 1030 Wien, Österreich | | | Di-Fr 12-18, Sa 10-14 | 48.2000992 | 16.3930174 -Mani | Yppenplatz 153-155, 1160 Wien, Österreich | welcome@mani-wien.at | +43 1 402 4317 | Sommer: Di-Fr 15-23, Sa 9-23 | 48.21344010000001 | 16.3358906 -Metro Kinokulturhaus | Johannesgasse 4, 1010 Wien, Österreich | | +43 1 5121803 | | 48.2049288 | 16.3719383 -Mumok | Museumsplatz 1, 1070 Wien, Österreich | | +43 1 525000 | | 48.2037823 | 16.3578331 -Nam Nam - Der Inder | Webgasse 3, 1060 Wien, Österreich | office@nam-nam.at | +43 1 595 6127 | Di-So 11-14:30 & 18-23 | 48.19239340000001 | 16.3481239 -Österreichisches Filmmuseum | Augustinerstraße 1, 1010 Wien, Österreich | | +43 1 5337054 | | 48.20487989999999 | 16.3684482 -Ottakringer Brauerei | Ottakringer Pl. 1, 1160 Wien, Österreich | | +43 1 491000 | | 48.2125515 | 16.3243735 -Phil | Gumpendorfer Str. 10-12, 1060 Wien, Österreich | | +43 1 581 0489 | Mo 17-1, Di-So 9-1 | 48.2002824 | 16.3611614 -Pitawerk | Mariahilferstr. 147, 1150 Wien, Österreich | | +43 1 941 8190 | Mo-Fr 9-21, Sa+So+Feiertag 10-21h | 48.1943475 | 16.3359463 -Polkadot Vienna | Albertgasse 12, 1080 Wien, Österreich | | +43 1 407 4125 | Mo-Do 19-2h, Fr +Sa 20-4, So 19-0 | 48.2085441 | 16.3438194 -Pygmalion Theater Wien | Alser Str. 43, 1080 Wien, Österreich | | +43 681 20754496 | | 48.2149702 | 16.3471596 -Radio The Label Bar | Schottenfeldgasse 17, 1070 Wien, Österreich | | +43 676 9363129 | | 48.19837099999999 | 16.343331 -RE:TREAT - Home for Yoga, Meditation & Vegan | Nelkengasse 6, Tür 14, 3. Stock, 1060 Wien, Österreich | | | | 48.19864399999999 | 16.351989 -Sägewerk Wien | Währinger Str. 21, 1090 Wien, Österreich | | +43 1 942 2489 | Mo-So. 11-2 | 48.218772 | 16.357538 -Sanfish | Margaretenstraße 67, 1050 Wien, Österreich | | +43 664 4138090 | Siehe Homepage | 48.1920145 | 16.3602976 -Schachtelw!rt | Judengasse 5, 1010 Wien, Österreich | | +43 1 432 0707 | Di-Fr 11:30-21, Sa 12-22 | 48.2114641 | 16.3735361 -Schelato | Lerchenfelderstr. 34, 1070 Wien, Österreich | info@schelato.at | | im Sommer: Mo-So. 12-22 | 48.20646190000001 | 16.3502846 -Schelato | Schleifmühlgasse 11, 1040 Wien, Österreich | info@schelato.at | | im Sommer: Mo-So. 12-22 | 48.1971518 | 16.3652054 -Schikaneder Kino | Margaretenstraße 22-24, 1040 Wien, Österreich | | +43 1 5852867 | | 48.1967562 | 16.3651328 -Schokomuseum | Willendorfer G. 2, 1230 Wien, Österreich | | +43 1 667211019 | | 48.1510802 | 16.3326236 -Schrittesser Speck&Bar | Reichsratstr. 11, 1010 Wien, Österreich | | +43 1 890 6146 | Mo-Fr. 12-0, Sa. 18-0 | 48.2127553 | 16.3586973 -S'Frische - Das fitte Bistro | Alser Str. 19, 1080 Wien, Österreich | | +43 676 905 3520 | Mo-Fr 7-18, Sa 9-13 | 48.2147544 | 16.3518641 -Shoyu Ramen | Seilerstätte 10, 1010 Wien, Österreich | | +43 1 941 5018 | Mo-Sa. 11:30-22 | 48.2058159 | 16.3757767 -Simply Raw Bakery | Drahtgasse 2, 1010 Wien, Österreich | | +43 677 62469124 | Mo-Sa. 9-18 | 48.2114497 | 16.3689125 -Sophort | Türkenstraße 23, 1090 Wien, Österreich | | +43 676 7735872 | | 48.218007 | 16.364246 -Stadtkino im Künstlerhaus | Akademiestr. 13, 1010 Wien, Österreich | | +43 1 7126276 | | 48.2009524 | 16.3712387 -Tag | Gumpendorfer Str. 67, 1060 Wien, Österreich | | +43 1 5865222 | | 48.1954436 | 16.351336 -Tapete | Zentagasse 14, 1050 Wien, Österreich | office@tapete.bar | +43 19664 346 | Di-Do + So 18-0, Fr+Sa. 18-2 | 48.1869603 | 16.3601802 -Theater Akzent | Theresianumgasse 18, 1040 Wien, Österreich | | +43 1 5016513306 | | 48.1923268 | 16.3749978 -Theater im Zentrum | Liliengasse 3, 1010 Wien, Österreich | | +43 1 521100 | | 48.2071025 | 16.3729654 -The Loft | Lerchenfelder Gürtel 37, 1160 Wien, Österreich | | +43 1 9477619 | | 48.2088385 | 16.3381239 -The Room - Sofiensäle | Marxergasse 17, 1030 Wien, Österreich | | +43 1 710 5577 | Mo-Fr. 11:30-0, Sa. 10-0, So. 10-16 | 48.20667299999999 | 16.3910562 -Toma Tu Tiempo | Zieglergasse 44, 1070 Wien, Österreich | | +43 660 447 7069 | Mo. 17-23, Di-Mi. 12-23, Do-Fr. 12-0, Sa. 11-0, So, 11-22 | 48.2023445 | 16.3452483 -Top Kino | Rahlgasse 1, 1060 Wien, Österreich | office@topkino.at | +43 1 208 3000 | Mo-Mi. 11-2, Do-Sa. 11-4, So. 10:30-0 | 48.2012244 | 16.361548 -Viadukt Screen Prints | Gumpendorfer Str. 132, 1060 Wien, Österreich | | +43 664 9595714 | | 48.1906295 | 16.340484 -Volkskundemuseum Wien | Laudongasse 15–19, 1080 Wien, Österreich | | +43 1 4068905 | | 48.213209 | 16.350919 -Volkstheater | Arthur-Schnitzler-Platz 1, 1070 Wien, Österreich | | +43 1 521110 | | 48.2052266 | 16.357045 -Weingut & Heuriger Christ | Amtsstr. 10-14, 1210 Wien, Österreich | | +43 1 292 5152 | Ungerade Monate: Mo-So. 15-0 | 48.2780059 | 16.4070448 -Werk | Spittelauer Lände 12, 1090 Wien, Österreich | | | | 48.233785 | 16.360638 -Werk X | Oswaldgasse 35A, 1120 Wien, Österreich | | +43 1 9626110 | | 48.1660499 | 16.3250717 -Wiener Riesenrad | Riesenradplatz 1, 1022 Wien, Österreich | | +43 1 7295430 | | 48.2175095 | 16.3966444 -Wienführung | Reisnerstraße 59, 1030 Wien, Österreich | | +43 676 9227773 | | 48.1959889 | 16.383793 -Wrapstars | Popovweg 8, 1100 Wien, Österreich | | +43 664 247 3263 | Siehe Homepage | 48.154027 | 16.4028146 -WUK Werkstätten und Kulturhaus | Währinger Str. 59, 1090 Wien, Österreich | | +43 1 401210 | | 48.2232158 | 16.3514935 -Zuppa | Schwarszpanierstr. 22, 1090 Wien, Österreich | | +43 1 405 8500 | Mo-Do. 8-17, Fr. 8-16 | 48.2170263 | 16.3590272 -Zur Herknerin | Wiedner Hauptstr. 36, 1040 Wien, Österreich | | +43 699 1522 0522 | Di-Fr. 17-23 | 48.19506819999999 | 16.3668034 +name | address | email | phone | opening_hours | latitude | longitude +12 - Karma Food | Ausstellungsstr. 63, 1020 Wien, Österreich | hallo@12.co.at | +43 676 914 8012 | Mo-Fr 9-17, Sa 9-15 | 48.2181676 | 16.4078821 +12 - Karma Food | Laurenzerberg 3, 1010 Wien, Österreich | hallo@12.co.at | +43 676 913 8012 | Mo-Fr 11-15 | 48.2109296 | 16.3778659 +1683 Handmade Bagels & Farm Coffee | Währinger Str. 12, 1090 Wien, Österreich | | +43 664 2639936 | Mo-Fr 7:30-18:30, Sa-Ft 9:30-18:30 | 48.2167778 | 16.3600738 +Achtundzwanzig | Schlösselgasse 28, 1080 Wien, Österreich | | +43 664 562 3786 | Mo-Fr 16-1, Sa 18-2 | 48.2145523 | 16.3532458 +Adamah Biohof | Glinzendorf 7, 2282 Glinzendorf, Österreich | | +43 2248 2224 | | 48.2466401 | 16.6427625 +Allergiker Café | Wiedner Hauptstr. 35, 1040 Wien, Österreich | kontakt@allergikercafe.at | +43 660 5371007 | Tu-Fr 9:30-19, Sa 10-16 | 48.1944784 | 16.3673893 +Archäonow | Weihburggasse 21, 1010 Wien, Österreich | | +43 2248 2224 | | 48.2059213 | 16.37517 +Arnulf Rainer Museum | Josefspl. 5, 2500 Baden, Österreich | office@arnulf-rainer-museum.at | +43 2252 209196 | Mo-Su 10-17 | 48.0063245 | 16.2337794 +Au - Raum für Kunst und Kultur | Brunnengasse 76, 1160 Wien, Österreich | | | Mo-Th 18-2, Su-Ft 18-2, Fr-Sa 18-4 | 48.2143648 | 16.3371565 +Avalon Kultur | Pfeilgasse 27, 1080 Wie, Österreichn | | +43 1 942 5365 | Mo-Fr 17-2, Sa 18-2 | 48.2089484 | 16.3434956 +Bäckerei Felzl | Lerchenfelderstr. 99-101, 1070 Wien, Österreich | | +43 1 522 3809 | Mo-Fr 6-19, Sa 6-17, Su-Ft 7-12:30 | 48.2074219 | 16.3435234 +Bank Austria Kunstforum Wien | Freyung 8, 1010 Wien, Österreich | | +43 1 5373326 | Sa-Th 10-19, Fr 10-21 | 48.211486 | 16.3662557 +Bao Bar | Zollergasse 2, 1070 Wien, Österreich | | | Mo-Sa 10-20 | 48.1992751 | 16.3512409 +Baschly 1090 | Schwarzspanierstr. 22, 1090 Wien, Österreich | | +43 1 729 7900 | Mo-Th 11-19 | 48.2170063 | 16.3589762 +Beaver Brewing Company | Liechtensteinstraße 69, 1090 Wien, Österreich | office@beaverbrewing.at | +43 677 610 12253 | Mo-Th 16-0, Fr 16-1, Sa 10-1, Su 10-22 | 48.2245931 | 16.3569675 +Biodeli | Gumpendorfer Str. 36, 1060 Wien, Österreich | servus@biodeli.at | +43 1 347 3335 | Mo-Fr 9-16 | 48.1987291 | 16.3575349 +Biodeli | Otto-Bauer-Gasse 11, 1060 Wien, Österreich | servus@biodeli.at | +43 1 347 3335 | Mo-Fr 9-16 | 48.1957689 | 16.3487646 +Bio-Pizzeria Vero | Währinger Gürtel 162, 1090 Wien, Österreich | | +43 1 310 1179 | Mo-Fr 11:30-14:30, Mo-Fr 17:30-22:30, Sa-Ft 11:30-22:30 | 48.230901 | 16.3525584 +Bits and Bites | Webgasse 27, 1060 Wien, Österreich | office@bitsandbites.at | +43 660 8372509 | We-Sa 18-22, We-Su 10-15 | 48.1945438 | 16.3458842 +Black Dogs Coffee | Wallgasse 27, 1060 Wien, Österreich | | +43 664 536 6088 | Tu-Sa 9-12:30, Tu-Sa 17-22 | 48.1922288 | 16.3393213 +Blün | Schafflerhofstraße 156, 1220 Wien, Österreich | | +43 1 7741333 | | 48.2281848 | 16.5364981 +Botanical Garden | Kolingasse 1, 1090 Wien, Österreich | | +43 676 4422553 | Mo-Th 17-2, Fr 17-3, Sa 17-3 | 48.21553429999999 | 16.361423 +Brasseria De La Marie | Amerlingstr. 15, 1060 Wien, Österreich | | +43 664 154 7737 | Mo-Fr 11-23, Sa 10-23:45 | 48.1974896 | 16.3505101 +Brauwerk | Ottakringer Pl. 1, 1160 Wien, Österreich | | +43 1 491 005 480 | | 48.212931 | 16.3248221 +Brut | Karlsplatz 5, 1010 Wien, Österreich | | +43 1 5878774 | | 48.2006328 | 16.3720546 +Buchcafè Melange | Reindorfgasse 42, 1150 Wien, Österreich | | +43 677 619 705 28 | Tu-Fr 10-13, Tu-Fr 13:30-18:30, Sa 10-15 | 48.1908533 | 16.3301859 +Burg Kino | Opernring 19, 1010 Wien, Österreich | | +43 1 5878406 | | 48.2030519 | 16.3650429 +Buschenschank & Bioweinbau Obermann | Cobenzlgasse 102, 1190 Wien, Österreich | | +43 664 451 9927 | We-Fr 16-0, Sa 15-0, Su 13-0 | 48.2620386 | 16.332417 +Café Caspar | Grillparzerstr. 6, 1010 Wien, Österreich | hallo@cafecaspar.com | +43 1 957 6793 | Mo-Fr 11-23 | 48.2126564 | 16.3581608 +Café Comet / Fürth Kaffee | Kirchengasse 44, 1070 Wien, Austria | | +43 664 1303066 | Tu-Fr 9-18, Sa 10-17, Su 11-16 | 48.2044242 | 16.351686 +Café Klitzeklein | Ausstellungsstraße 2, 1020 Wien, Österreich | | | | 48.2179008 | 16.3952356 +Café Zeitgenossin | Billrothstraße 18, 1190 Wien, Austria | | +43 699 1073 8865 | Mo 12-17:30, Tu-Fr 9:30-17:30 | 48.23639249999999 | 16.3494354 +Café Z | Meiselstr. 2, 1150 Wien, Österreich | office@cafe-z.at | +43 680 308 7234 | Tu-Sa 10-22, Su 10-17 | 48.1974825 | 16.3232923 +Corns N'Pops | Gumpendorfer Str. 37, 1060 Wien, Österreich | | +43 664 131 2005 | Mo-Fr 9-15:30 | 48.1986689 | 16.3580129 +Creme de la Creme | 76, Lange G., 1080 Wien, Österreich | hallo@cremedelacreme.at | +43 660 283 3769 | Tu-Fr 9-18, Sa-Su 10-17 | 48.2144043 | 16.3511785 +Cyberlab | Schottenfeldgasse 51, 1070 Wien, Österreich | | +43 1 522400400 | Mo-Fr 10-18:30 | 48.202167 | 16.3430246 +Das Lokal Im Hof | Weyringerg. 36, 1040 Wien, Österreich | | +43 1 971 1914 | Mo-Fr 11-23 | 48.1883846 | 16.3742402 +Deli Bluem | Hamerlingpl. 2, 1080 Wien, Österreich | | +43 1 8900 449 | Mo-Fr 8-19, Sa-Su 9-18 | 48.211378 | 16.344551 +Deli Bluem | Laudongasse 17, 1080 Wien, Österreich | | +43 1 8900 449 | Mo-Fr 8-19, Sa-Su 9-18 | 48.213209 | 16.350719 +Dialog im Dunkeln | Freyung 6, 1010 Wien, Österreich | | +43 1 8906060 | Tu-Fr 9-18, Sa 10-19, Su 13-19 | 48.21231 | 16.3643799 +Die Buntique | Kirchengasse 26, 1070 Wien, Österreich | | +43 664 75110469 | | 48.20167 | 16.3520428 +Die Süsse | Phorusgassse 8, 1040 Wien, Österreich | | +43 699 11419453 | Mo-Fr 10-18 | 48.1901246 | 16.3638214 +Donuteria | Landstraßer Hauptstr. 71/G01, 1030 Wien, Österreich | | +43 660 738 7264 | Mo-Fr 9-19, Sa 9-18 | 48.2012309 | 16.3934624 +Dritte Mann Museum | Preßgasse 25, 1040 Wien, Österreich | | +43 1 5864872 | Sa 14-18 | 48.1960993 | 16.3624638 +Elefant & Castle | Neubaugasse 45, 1070 Wien, Österreich | | +43 699 192 08 459 | Mo-Fr 11:30-19:30 | 48.20258640000001 | 16.3489424 +Fesch'Markt Wien | Ottakringer Str. 83, 1160 Wien, Österreich | | | | 48.21303169999999 | 16.3250144 +Gartenbaukino | Parkring 12, 1010 Wien, Österreich | | +43 1 5122354 | Mo-Su 17-23 | 48.2056486 | 16.3783174 +Hands Up | Freyung 6, 1010 Wien, Österreich | | +43 1 3193701 | | 48.2123992 | 16.3644022 +Hase und Igel | Theobaldgasse 16, 1060 Wien, Österreich | | +43 1 971 6029 | Mo-Fr 11-20, Sa 11-18 | 48.2002855 | 16.3595533 +Haus der Musik | Seilerstätte 30, 1010 Wien, Österreich | | +43 1 5134850 | Mo-Su 10-22 | 48.2038401 | 16.3730192 +Iko | Wipplingerstr. 6, 1010 Wien, Österreich | | +43 1 890 4200 | Mo-Fr 11-22, Sa 12-22 | 48.2117344 | 16.3713102 +Jetzt Bar & Entertainment | Parhamerpl. 16, 1170 Wien, Österreich | info@dasjetzt.at | +43 1 4857 680 | Mo-Th 18-3, Fr-Sa 18-4, Su 18-2 | 48.2165786 | 16.3276148 +J. Hornig Kaffebar | Siebensterngasse 29, 1070 Wien, Österreich | kaffeebar@jhornig.at | +43 1 522 2251 | Mo-Fr 7:30-19, Sa-Ft 9-19 | 48.20203919999999 | 16.3522683 +Jonas Reindl Coffee | Währinger Str. 2-4, 1090 Wien, Österreich | | | Mo-Fr 7:30-22, Sa 10-22 | 48.2149624 | 16.3617342 +Kaffeefabrik | Favoritenstraße 4, 1040 Wien, Österreich | | +43 660 1789 092 | Mo-Fr 8-18, Sa 11-17 | 48.1958824 | 16.3684117 +Kaffeemodul | Josefstädter Str. 35, 1080 Wien, Österreich | | +43 680 2228039 | Mo-Fr 7:30-17:30, Sa 10-14 | 48.2095175 | 16.348442 +Kaffemik | Zollergasse 5/2, 1070 Wien, Österreich | | +43 650 4141 535 | Mo-Fr 8-18, Sa 10-18, Su-Ft 12-18 | 48.1991733 | 16.350998 +Kartonage Kitchen | Burggasse 6-8, 1070 Wien, Österreich | | +43 660 6387346 | Mo-Fr 8-18:30 | 48.2043708 | 16.3554785 +Kauf Dich Glücklich | Kirchengasse 9, 1070 Wien, Österreich | | +43 1 9247755 | Mo-Fr 10-20, Sa 10-18 | 48.200681 | 16.3522777 +Kunsthalle Wien | Museumsplatz 1, 1070 Wien, Österreich | | +43 1 521890 | Mo-We 11-19, Th 11-21, Fr-Su 11-19 | 48.2026499 | 16.3591421 +Kunst Haus Wien Museum Hundertwasser | Untere Weißgerberstraße 13, 1030 Wien, Österreich | | +43 1 7120491 | Mo-Su 10-18 | 48.211074 | 16.393286 +Leones Gelato | Lange Gasse 78, 1080 Wien, Österreich | ciao@leones.at | +43 1 352 5252 | | 48.21468309999999 | 16.3511642 +Leones Gelato | Praterstr. 16, 1020 Wien, Österreich | ciao@leones.at | +43 1 352 5252 | | 48.2133617 | 16.3822807 +Lingenhel | Landstraßer Hauptstr. 74, 1030 Wien, Österreich | | +43 1 710 1566 | Mo-Sa 8-22 | 48.2009278 | 16.391988 +Lunzers Mass-Greisslerei | Heinestr. 35, 1020 Wien, Österreich | | +43 1 212 1387 | Mo-Fr 9-19, Sa 9-17 | 48.2196131 | 16.3880717 +Macaroom | Landstraßer Hauptstr. 86, 1030 Wien, Österreich | | | Tu-Fr 12-18, Sa 10-14 | 48.2000992 | 16.3930174 +Mani | Yppenplatz 153-155, 1160 Wien, Österreich | welcome@mani-wien.at | +43 1 402 4317 | Tu-Fr 15-23 Jul-Aug, Sa 9-23 Jul-Aug | 48.21344010000001 | 16.3358906 +Metro Kinokulturhaus | Johannesgasse 4, 1010 Wien, Österreich | | +43 1 5121803 | Mo-Su 15-21 | 48.2049288 | 16.3719383 +Mumok | Museumsplatz 1, 1070 Wien, Österreich | | +43 1 525000 | Mo 14-19, Tu 10-19, We 10-19, Th 10-21, Fr-Su 10-19 | 48.2037823 | 16.3578331 +Nam Nam - Der Inder | Webgasse 3, 1060 Wien, Österreich | office@nam-nam.at | +43 1 595 6127 | Tu-Su 11-14:30, Tu-Su 18-23 | 48.19239340000001 | 16.3481239 +Österreichisches Filmmuseum | Augustinerstraße 1, 1010 Wien, Österreich | | +43 1 5337054 | | 48.20487989999999 | 16.3684482 +Ottakringer Brauerei | Ottakringer Pl. 1, 1160 Wien, Österreich | | +43 1 491000 | Mo-Fr 9-17:30 | 48.2125515 | 16.3243735 +Phil | Gumpendorfer Str. 10-12, 1060 Wien, Österreich | | +43 1 581 0489 | Mo 17-1, Tu-Su 9-1 | 48.2002824 | 16.3611614 +Pitawerk | Mariahilferstr. 147, 1150 Wien, Österreich | | +43 1 941 8190 | Mo-Fr 9-21, Sa-Ft 10-21 | 48.1943475 | 16.3359463 +Polkadot Vienna | Albertgasse 12, 1080 Wien, Österreich | | +43 1 407 4125 | Mo-Th 19-2, Fr-Sa 20-4, Su 19-0 | 48.2085441 | 16.3438194 +Pygmalion Theater Wien | Alser Str. 43, 1080 Wien, Österreich | | +43 681 20754496 | | 48.2149702 | 16.3471596 +Radio The Label Bar | Schottenfeldgasse 17, 1070 Wien, Österreich | | +43 676 9363129 | Mo-Th 17-2, Fr 17-4, Sa 17-4 | 48.19837099999999 | 16.343331 +RE:TREAT - Home for Yoga, Meditation & Vegan | Nelkengasse 6, Tür 14, 3. Stock, 1060 Wien, Österreich | | | | 48.19864399999999 | 16.351989 +Sägewerk Wien | Währinger Str. 21, 1090 Wien, Österreich | | +43 1 942 2489 | Mo-Su 11-2 | 48.218772 | 16.357538 +Sanfish | Margaretenstraße 67, 1050 Wien, Österreich | | +43 664 4138090 | | 48.1920145 | 16.3602976 +Schachtelw!rt | Judengasse 5, 1010 Wien, Österreich | | +43 1 432 0707 | Tu-Fr 11:30-21, Sa 12-22 | 48.2114641 | 16.3735361 +Schelato | Lerchenfelderstr. 34, 1070 Wien, Österreich | info@schelato.at | | Mo-Su 12-22 Jul-Sep | 48.20646190000001 | 16.3502846 +Schelato | Schleifmühlgasse 11, 1040 Wien, Österreich | info@schelato.at | | Mo-Su 12-22 Jul-Sep | 48.1971518 | 16.3652054 +Schikaneder Kino | Margaretenstraße 22-24, 1040 Wien, Österreich | | +43 1 5852867 | Mo-Su 18-0 | 48.1967562 | 16.3651328 +Schokomuseum | Willendorfer G. 2, 1230 Wien, Österreich | | +43 1 667211019 | Mo-Sa 9-16 | 48.1510802 | 16.3326236 +Schrittesser Speck&Bar | Reichsratstr. 11, 1010 Wien, Österreich | | +43 1 890 6146 | Mo-Fr 12-0, Sa 18-0 | 48.2127553 | 16.3586973 +S'Frische - Das fitte Bistro | Alser Str. 19, 1080 Wien, Österreich | | +43 676 905 3520 | Mo-Fr 7-18, Sa 9-13 | 48.2147544 | 16.3518641 +Shoyu Ramen | Seilerstätte 10, 1010 Wien, Österreich | | +43 1 941 5018 | Mo-Sa 11:30-22 | 48.2058159 | 16.3757767 +Simply Raw Bakery | Drahtgasse 2, 1010 Wien, Österreich | | +43 677 62469124 | Mo-Sa 9-18 | 48.2114497 | 16.3689125 +Sophort | Türkenstraße 23, 1090 Wien, Österreich | | +43 676 7735872 | | 48.218007 | 16.364246 +Stadtkino im Künstlerhaus | Akademiestr. 13, 1010 Wien, Österreich | | +43 1 7126276 | | 48.2009524 | 16.3712387 +Tag | Gumpendorfer Str. 67, 1060 Wien, Österreich | | +43 1 5865222 | | 48.1954436 | 16.351336 +Tapete | Zentagasse 14, 1050 Wien, Österreich | office@tapete.bar | +43 19664 346 | Tu-Th 18-0, Su 18-0, Fr-Sa 18-2 | 48.1869603 | 16.3601802 +Theater Akzent | Theresianumgasse 18, 1040 Wien, Österreich | | +43 1 5016513306 | Mo-Sa 13-18 | 48.1923268 | 16.3749978 +Theater im Zentrum | Liliengasse 3, 1010 Wien, Österreich | | +43 1 521100 | | 48.2071025 | 16.3729654 +The Loft | Lerchenfelder Gürtel 37, 1160 Wien, Österreich | | +43 1 9477619 | Fr-Sa 20-4 | 48.2088385 | 16.3381239 +The Room - Sofiensäle | Marxergasse 17, 1030 Wien, Österreich | | +43 1 710 5577 | Mo-Fr 11:30-0, Sa 10-0, Su 10-16 | 48.20667299999999 | 16.3910562 +Toma Tu Tiempo | Zieglergasse 44, 1070 Wien, Österreich | | +43 660 447 7069 | Mo 17-23, Tu-We 12-23, Th-Fr 12-0, Sa 11-0, Su 11-22 | 48.2023445 | 16.3452483 +Top Kino | Rahlgasse 1, 1060 Wien, Österreich | office@topkino.at | +43 1 208 3000 | Mo-We 11-2, Th-Sa 11-4, Su 10:30-0 | 48.2012244 | 16.361548 +Viadukt Screen Prints | Gumpendorfer Str. 132, 1060 Wien, Österreich | | +43 664 9595714 | | 48.1906295 | 16.340484 +Volkskundemuseum Wien | Laudongasse 15–19, 1080 Wien, Österreich | | +43 1 4068905 | Tu-Su 10-17, Th 10-20 | 48.213209 | 16.350919 +Volkstheater | Arthur-Schnitzler-Platz 1, 1070 Wien, Österreich | | +43 1 521110 | | 48.2052266 | 16.357045 +Weingut & Heuriger Christ | Amtsstr. 10-14, 1210 Wien, Österreich | | +43 1 292 5152 | Mo-Su 15-0 Jan, Mo-Su 15-0 Mar, Mo-Su 15-0 May, Mo-Su 15-0 Jul, Mo-Su 15-0 Sep, Mo-Su 15-0 Nov | 48.2780059 | 16.4070448 +Werk | Spittelauer Lände 12, 1090 Wien, Österreich | | | | 48.233785 | 16.360638 +Werk X | Oswaldgasse 35A, 1120 Wien, Österreich | | +43 1 9626110 | | 48.1660499 | 16.3250717 +Wiener Riesenrad | Riesenradplatz 1, 1022 Wien, Österreich | | +43 1 7295430 | | 48.2175095 | 16.3966444 +Wienführung | Reisnerstraße 59, 1030 Wien, Österreich | | +43 676 9227773 | | 48.1959889 | 16.383793 +Wrapstars | Popovweg 8, 1100 Wien, Österreich | | +43 664 247 3263 | | 48.154027 | 16.4028146 +WUK Werkstätten und Kulturhaus | Währinger Str. 59, 1090 Wien, Österreich | | +43 1 401210 | | 48.2232158 | 16.3514935 +Zuppa | Schwarszpanierstr. 22, 1090 Wien, Österreich | | +43 1 405 8500 | Mo-Th 8-17, Fr 8-16 | 48.2170263 | 16.3590272 +Zur Herknerin | Wiedner Hauptstr. 36, 1040 Wien, Österreich | | +43 699 1522 0522 | Tu-Fr 17-23 | 48.19506819999999 | 16.3668034 diff --git a/data/locations_old.csv b/data/locations_old.csv deleted file mode 100644 index c409c06..0000000 --- a/data/locations_old.csv +++ /dev/null @@ -1,115 +0,0 @@ -name | address | latitude | longitude -12 - Karma Food | Ausstellungsstr. 63, 1020 Wien, Österreich | 48.2181676 | 16.4078821 -12 - Karma Food | Laurenzerberg 3, 1010 Wien, Österreich | 48.2109296 | 16.3778659 -1683 Handmade Bagels & Farm Coffee | Währinger Str. 12, 1090 Wien, Österreich | 48.2167778 | 16.3600738 -Allergiker Café | Wiedner Hauptstr. 35, 1040 Wien, Österreich | 48.1944784 | 16.3673893 -Avalon Kultur | Pfeilgasse 27, 1080 Wie, Österreichn | 48.2089484 | 16.3434956 -Bao Bar | Zollergasse 2, 1070 Wien, Österreich | 48.1992751 | 16.3512409 -Baschly 1090 | Schwarzspanierstr. 22, 1090 Wien, Österreich | 48.2170063 | 16.3589762 -Bio-Pizzeria Vero | Währinger Gürtel 162, 1090 Wien, Österreich | 48.230901 | 16.3525584 -Biodeli | Gumpendorfer Str. 36, 1060 Wien, Österreich | 48.1987291 | 16.3575349 -Biodeli | Otto-Bauer-Gasse 11, 1060 Wien, Österreich | 48.1957689 | 16.3487646 -Brasseria De La Marie | Amerlingstr. 15, 1060 Wien, Österreich | 48.1974896 | 16.3505101 -Buchcafè Melange | Reindorfgasse 42, 1150 Wien, Österreich | 48.1908533 | 16.3301859 -Bäckerei Felzl | Lerchenfelderstr. 99-101, 1070 Wien, Österreich | 48.2074219 | 16.3435234 -Café Caspar | Grillparzerstr. 6, 1010 Wien, Österreich | 48.2126564 | 16.3581608 -Café Comet / Fürth Kaffee | Kirchengasse 44, 1070 Wien, Austria | 48.2044242 | 16.351686 -Café Z | Meiselstr. 2, 1150 Wien, Österreich | 48.1974825 | 16.3232923 -Corns N'Pops | Gumpendorfer Str. 37, 1060 Wien, Österreich | 48.1986689 | 16.3580129 -Das Lokal Im Hof | Weyringerg. 36, 1040 Wien, Österreich | 48.1883846 | 16.3742402 -Die Süsse | Phorusgassse 8, 1040 Wien, Österreich | 48.1901246 | 16.3638214 -Donuteria | Landstraßer Hauptstr. 71/G01, 1030 Wien, Österreich | 48.2012309 | 16.3934624 -Elefant & Castle | Neubaugasse 45, 1070 Wien, Österreich | 48.20258640000001 | 16.3489424 -Hase und Igel | Theobaldgasse 16, 1060 Wien, Österreich | 48.2002855 | 16.3595533 -Iko | Wipplingerstr. 6, 1010 Wien, Österreich | 48.2117344 | 16.3713102 -J. Hornig Kaffebar | Siebensterngasse 29, 1070 Wien, Österreich | 48.20203919999999 | 16.3522683 -Kartonage Kitchen | Burggasse 6-8, 1070 Wien, Österreich | 48.2043708 | 16.3554785 -Leones Gelato | Lange Gasse 78, 1080 Wien, Österreich | 48.21468309999999 | 16.3511642 -Leones Gelato | Praterstr. 16, 1020 Wien, Österreich | 48.2133617 | 16.3822807 -Lingenhel | Landstraßer Hauptstr. 74, 1030 Wien, Österreich | 48.2009278 | 16.391988 -Lunzers Mass-Greisslerei | Heinestr. 35, 1020 Wien, Österreich | 48.2196131 | 16.3880717 -Macaroom | Landstraßer Hauptstr. 86, 1030 Wien, Österreich | 48.2000992 | 16.3930174 -Mani | Yppenplatz 153-155, 1160 Wien, Österreich | 48.21344010000001 | 16.3358906 -Nam Nam - Der Inder | Webgasse 3, 1060 Wien, Österreich | 48.19239340000001 | 16.3481239 -Phil | Gumpendorfer Str. 10-12, 1060 Wien, Österreich | 48.2002824 | 16.3611614 -Pitawerk | Mariahilferstr. 147, 1150 Wien, Österreich | 48.1943475 | 16.3359463 -S'Frische - Das fitte Bistro | Alser Str. 19, 1080 Wien, Österreich | 48.2147544 | 16.3518641 -Sanfish | Margaretenstraße 67, 1050 Wien, Österreich | 48.1920145 | 16.3602976 -Schachtelw!rt | Judengasse 5, 1010 Wien, Österreich | 48.2114641 | 16.3735361 -Schelato | Lerchenfelderstr. 34, 1070 Wien, Österreich | 48.20646190000001 | 16.3502846 -Schelato | Schleifmühlgasse 11, 1040 Wien, Österreich | 48.1971518 | 16.3652054 -Schrittesser Speck&Bar | Reichsratstr. 11, 1010 Wien, Österreich | 48.2127553 | 16.3586973 -Shoyu Ramen | Seilerstätte 10, 1010 Wien, Österreich | 48.2058159 | 16.3757767 -Simply Raw Bakery | Drahtgasse 2, 1010 Wien, Österreich | 48.2114497 | 16.3689125 -Sägewerk Wien | Währinger Str. 21, 1090 Wien, Österreich | 48.218772 | 16.357538 -The Room - Sofiensäle | Marxergasse 17, 1030 Wien, Österreich | 48.20667299999999 | 16.3910562 -Toma Tu Tiempo | Zieglergasse 44, 1070 Wien, Österreich | 48.2023445 | 16.3452483 -Weingut & Heuriger Christ | Amtsstr. 10-14, 1210 Wien, Österreich | 48.2780059 | 16.4070448 -Wrapstars | Popovweg 8, 1100 Wien, Österreich | 48.154027 | 16.4028146 -Zuppa | Schwarszpanierstr. 22, 1090 Wien, Österreich | 48.2170263 | 16.3590272 -Zur Herknerin | Wiedner Hauptstr. 36, 1040 Wien, Österreich | 48.19506819999999 | 16.3668034 -Achtundzwanzig | Schlösselgasse 28, 1080 Wien, Österreich | 48.2145523 | 16.3532458 -Au - Raum für Kunst und Kultur | Brunnengasse 76, 1160 Wien, Österreich | 48.2143648 | 16.3371565 -Beaver Brewing Company | Liechtensteinstraße 69, 1090 Wien, Österreich | 48.2245931 | 16.3569675 -Bits and Bites | Webgasse 27, 1060 Wien, Österreich | 48.1945438 | 16.3458842 -Black Dogs Coffee | Wallgasse 27, 1060 Wien, Österreich | 48.1922288 | 16.3393213 -Botanical Garden | Kolingasse 1, 1090 Wien, Österreich | 48.21553429999999 | 16.361423 -Brauwerk | Ottakringer Pl. 1, 1160 Wien, Österreich | 48.212931 | 16.3248221 -Buschenschank & Bioweinbau Obermann | Cobenzlgasse 102, 1190 Wien, Österreich | 48.2620386 | 16.332417 -Café Klitzeklein | Ausstellungsstraße 2, 1020 Wien, Österreich | 48.2179008 | 16.3952356 -Café Zeitgenossin | Billrothstraße 18, 1190 Wien, Austria | 48.23639249999999 | 16.3494354 -Creme de la Creme | 76, Lange G., 1080 Wien, Österreich | 48.2144043 | 16.3511785 -Deli Bluem | Hamerlingpl. 2, 1080 Wien, Österreich | 48.211378 | 16.344551 -Deli Bluem | Laudongasse 17, 1080 Wien, Österreich | 48.213209 | 16.350719 -Jetzt Bar & Entertainment | Parhamerpl. 16, 1170 Wien, Österreich | 48.2165786 | 16.3276148 -Jonas Reindl Coffee | Währinger Str. 2-4, 1090 Wien, Österreich | 48.2149624 | 16.3617342 -Kaffeefabrik | Favoritenstraße 4, 1040 Wien, Österreich | 48.1958824 | 16.3684117 -Kaffeemodul | Josefstädter Str. 35, 1080 Wien, Österreich | 48.2095175 | 16.348442 -Kaffemik | Zollergasse 5/2, 1070 Wien, Österreich | 48.1991733 | 16.350998 -Polkadot Vienna | Albertgasse 12, 1080 Wien, Österreich | 48.2085441 | 16.3438194 -Tapete | Zentagasse 14, 1050 Wien, Österreich | 48.1869603 | 16.3601802 -Adamah Biohof | Glinzendorf 7, 2282 Glinzendorf, Österreich | 48.2466401 | 16.6427625 -Archäonow | Weihburggasse 21, 1010 Wien, Österreich | 48.2059213 | 16.37517 -Arnulf Rainer Museum | Josefspl. 5, 2500 Baden, Österreich | 48.0063245 | 16.2337794 -Bank Austria Kunstforum Wien | Freyung 8, 1010 Wien, Österreich | 48.211486 | 16.3662557 -Blün | Schafflerhofstraße 156, 1220 Wien, Österreich | 48.2281848 | 16.5364981 -Brut | Karlsplatz 5, 1010 Wien, Österreich | 48.2006328 | 16.3720546 -Burg Kino | Opernring 19, 1010 Wien, Österreich | 48.2030519 | 16.3650429 -Cyberlab | Schottenfeldgasse 51, 1070 Wien, Österreich | 48.202167 | 16.3430246 -Dialog im Dunkeln | Freyung 6, 1010 Wien, Österreich | 48.21231 | 16.3643799 -Die Buntique | Kirchengasse 26, 1070 Wien, Österreich | 48.20167 | 16.3520428 -Dritte Mann Museum | Preßgasse 25, 1040 Wien, Österreich | 48.1960993 | 16.3624638 -Fesch'Markt Wien | Ottakringer Str. 83, 1160 Wien, Österreich | 48.21303169999999 | 16.3250144 -Gartenbaukino | Parkring 12, 1010 Wien, Österreich | 48.2056486 | 16.3783174 -Hands Up | Freyung 6, 1010 Wien, Österreich | 48.2123992 | 16.3644022 -Hands Up | Nußdorfer Str. 9, 1090 Wien, Österreich | 48.2235 | 16.35361 -Haus der Musik | Seilerstätte 30, 1010 Wien, Österreich | 48.2038401 | 16.3730192 -Kauf Dich Glücklich | Kirchengasse 9, 1070 Wien, Österreich | 48.200681 | 16.3522777 -Kunst Haus Wien Museum Hundertwasser | Untere Weißgerberstraße 13, 1030 Wien, Österreich | 48.211074 | 16.393286 -Kunsthalle Wien | Museumsplatz 1, 1070 Wien, Österreich | 48.2026499 | 16.3591421 -Kunsthalle Wien | Treitlstraße 2, 1040 Wien, Österreich | 48.200284 | 16.367714 -Metro Kinokulturhaus | Johannesgasse 4, 1010 Wien, Österreich | 48.2049288 | 16.3719383 -Mumok | Museumsplatz 1, 1070 Wien, Österreich | 48.2037823 | 16.3578331 -Ottakringer Brauerei | Ottakringer Pl. 1, 1160 Wien, Österreich | 48.2125515 | 16.3243735 -Pygmalion Theater Wien | Alser Str. 43, 1080 Wien, Österreich | 48.2149702 | 16.3471596 -RE:TREAT - Home for Yoga, Meditation & Vegan | Nelkengasse 6, Tür 14, 3. Stock, 1060 Wien, Österreich | 48.19864399999999 | 16.351989 -Radio The Label Bar | Schottenfeldgasse 17, 1070 Wien, Österreich | 48.19837099999999 | 16.343331 -Schikaneder Kino | Margaretenstraße 22-24, 1040 Wien, Österreich | 48.1967562 | 16.3651328 -Schokomuseum | Willendorfer G. 2, 1230 Wien, Österreich | 48.1510802 | 16.3326236 -Sophort | Türkenstraße 23, 1090 Wien, Österreich | 48.218007 | 16.364246 -Stadtkino im Künstlerhaus | Akademiestr. 13, 1010 Wien, Österreich | 48.2009524 | 16.3712387 -Tag | Gumpendorfer Str. 67, 1060 Wien, Österreich | 48.1954436 | 16.351336 -The Loft | Lerchenfelder Gürtel 37, 1160 Wien, Österreich | 48.2088385 | 16.3381239 -Theater Akzent | Theresianumgasse 18, 1040 Wien, Österreich | 48.1923268 | 16.3749978 -Theater im Zentrum | Liliengasse 3, 1010 Wien, Österreich | 48.2071025 | 16.3729654 -Top Kino | Rahlgasse 1, 1060 Wien, Österreich | 48.2012244 | 16.361548 -Viadukt Screen Prints | Gumpendorfer Str. 132, 1060 Wien, Österreich | 48.1906295 | 16.340484 -Volkskundemuseum Wien | Laudongasse 15–19, 1080 Wien, Österreich | 48.213209 | 16.350919 -Volkstheater | Arthur-Schnitzler-Platz 1, 1070 Wien, Österreich | 48.2052266 | 16.357045 -WUK Werkstätten und Kulturhaus | Währinger Str. 59, 1090 Wien, Österreich | 48.2232158 | 16.3514935 -Werk | Spittelauer Lände 12, 1090 Wien, Österreich | 48.233785 | 16.360638 -Werk X | Oswaldgasse 35A, 1120 Wien, Österreich | 48.1660499 | 16.3250717 -Werk X | Peterspl. 1, 1010 Wien, Österreich | 48.2091048 | 16.3701238 -Wiener Riesenrad | Riesenradplatz 1, 1022 Wien, Österreich | 48.2175095 | 16.3966444 -Wienführung | Reisnerstraße 59, 1030 Wien, Österreich | 48.1959889 | 16.383793 -Österreichisches Filmmuseum | Augustinerstraße 1, 1010 Wien, Österreich | 48.20487989999999 | 16.3684482 diff --git a/data/venues_old.csv b/data/venues_old.csv deleted file mode 100644 index ce34ff0..0000000 --- a/data/venues_old.csv +++ /dev/null @@ -1,108 +0,0 @@ -name | homepage | email | phone | opening_hours | description -12 - Karma Food | www.12.co.at | hallo@12.co.at | +43 676 914 8012 | Mo-Fr 9-17, Sa. 9-15 | "Alle Entscheidungen im 12 Karma Food basieren auf dem Motto: ""Do good and good will come to you"". Diese und alle anderen Regeln des ""Good Karma"" leben Simone und Adi im gemütlichen Laden am Laurenzerberg und im neueröffneten Lokal in der Ausstellungsstraße." -1683 Handmade Bagels & Farm Coffee | www.bagel1683.at | | +43 664 2639936 | Mo-Fr. 7:30-18:30, Sa + So + Feiertags 9:30-18:30 | "Oft als amerikanische Erfindung bgetan, vergisst man die wahren Wurzeln des runden Gebäcks mit dem Loch in der Mitte. ""1683"" nennt sich die Bageldiele am Alsergrund." -Allergiker Café | www.allergikercafe.at | kontakt@allergikercafe.at | +43 660 5371007 | Di-Fr. 9:30-19,Sa. 10-16 | """Süßes für Alle"" lautet das Motto des Allergiker Cafés. Hier wird nach Lust und Laune geschlemmt, auch wenn man das eine oder andere nicht verträgt." -Avalon Kultur | www.avalonkultur.at | | +43 1 942 5365 | Mo-Fr. 17-20, Sa 18-20 | Das sehr ansehnliche Biersortiment im alternativen Kultur-Beisl ist nur einer der Gründe, weswegen man auch nach dem Essen gern mal etwas länger bleibt. -Bao Bar | www.baobar.at | | | Mo-Sa. 10-20h | Baos sind die taiwanesische Antwort auf den Burgerboom. Die hellen Brötchen werden über Wasserdampf gegart, ohne mit Wasser in Berührung zu kommen. -Baschly 1090 | www.baschly.com | | +43 1 729 7900 | Mo-Do. 11-19 | Das Wort Baschly ist eine Eigenkreation und bedeutet soviel wie Freund. Der Name ist kein Zufall, denn hier werden die Gäste wie Freunde behandelt. In der Küche werden traditionelle israelische Gerichte und beste Zutaten aus Österreich vereint. -Bio-Pizzeria Vero | www.vero.co.at | | +43 1 310 1179 | Mo-Fr. 11:30-14:30 & 17:30-22:30, Sa + So + Feiertag 11:30-22:30 | Pizzaessen muss nicht ungesund sein! Die Pizzeria Vero hat sich auf hausgemacht Bio-Pizzen spezialisisert. -Biodeli | www.biodeli.at | servus@biodeli.at | +43 1 347 3335 | Mo-Fr 9-16 | Das Biodeli ist ein modernes Deli mit Bio- und Fairtrade-Zertifizierung. Je nach Lust und Laune wird vegetarisch gekocht, vegan und mit Fleisch, mal österreichisch, mal asiatisch oder italienisch. -Brasseria De La Marie | www.brasseriedelamarie.com | | +43 664 154 7737 | Mo-Fr. 11-23, Sa 10-23:45 | Spieglein, Spieglein an der Wand. Ganz oft begegnet man sich selbst, sobald man die Brasserie de la Marie betritt. Carmen Mihalcea ist gebürtige Rumänin und hat zum Hobby alte Möbel aufzupeppen und zu restaurieren. -Buchcafè Melange | www.buchcafe-melange.com | | +43 677 619 705 28 | Di – Fr. 10-13 & 13:30-18:30, Sa. 10-15 | Bücher, Kaffee und Kuchen - Was will man mehr? Das Buchcafé Melange ist eine kleine Buchhandlung mit Café in der Reindorfgasse. -Bäckerei Felzl | www.felzl.at | | +43 1 522 3809 | Mo-Fr. 6-19, Sa. 6-17, So + Feiertag 7-12:30 | Beim Felzl schmecken die Baguettes genauso wie in Frankreich. Kein Wunder, werden diese doch nach original französischem Rezept hergestellt. -Café Caspar | www.cafecaspar.com | hallo@cafecaspar.com | +43 1 957 6793 | Mo-Fr. 11-23 | Ein bisschen versteckt, aber in bester Lage, zwischen Hauptuni und Rathaus, hat sich seit Oktober 2015 das Café Caspar eingerichtet. Und zwar mit den gemütlichsten Sofas und viel Liebe zum Design. -Café Comet / Fürth Kaffee | www.fuerthkaffee.eu | | +43 664 1303066 | | Im Café Comet wird Kaffee der eigenen Rösterei Fürth Kaffee, je nach Wunsch, als Bohne oder in der Tasse verabreicht. -Café Z | www.facebook.com/cafeZwien | office@cafe-z.at | +43 680 308 7234 | Di-Sa. 10-22, So. 10-17 | Rudolfscrime wird der 15. Wiener Gemeindebezirk liebevoll genannt. Ganz so schlimm ist es nicht mehr und viele nette Lokale und Geschäfte öffnen hier ihre Pforten. Allen voran das Café Z. -Corns N'Pops | www.cornsnpops.com | | +43 664 131 2005 | Mo-Fr. 8-17, Sa. Siehe Homepage | Das Corns n'Pops bewährt sich seit fast zehn Jahren als beliebtes Frühstücks-Lokal und Mittags-Deli im 6. Bezirk. -Das Lokal Im Hof | www.daslokal.at | | +43 1 971 1914 | Mo-Fr. 11-23 | "Etwas versteckt im Innenhof der alten Alpenmilchzentrale in Wieden liegt ""Das Lokal im Hof"". Hier bewirten euch Robert Haider und Barbara Fegerl mit modern interpretierter österreichischer Gastlichkeit." -Die Süsse | www.diesuesse.at | | +43 699 11419453 | Mo-Fr. 10-18 | Karin kocht und bäckt schon immer mit Leidenschaft und hat sich in Wieden ihre Backstube in einer ehemaligen Bäckerei eingerichtet. -Donuteria | www.donuteria.com | | +43 660 738 7264 | Mo-Fr. 9-19, Sa 9-18 | In der Donuteria wurde nicht das Rad neu erfunden, sondern der Donut. Alle, die das besondere Geschmackserlebnis suchen, sind hier richtig. -Elefant & Castle | www.elefantcastle.at | | +43 699 192 08 459 | Mo-Fr. 11:30-19:30 | Mitten im Shopping-District für junge, kreative Mode, abseits des Mainstreams in der Mariahilfer Straße, lädt das entzückende Elefant & Castle auf der Neubaugasse - nicht zu Unrecht benannt nach einer U-Bahnstation in London - zu einer kleinen Stärkung. -Hase und Igel | www.haseundigel.at | | +43 1 971 6029 | Mo-Fr. 11-20, Sa. 11-18 | Die Hase und Igel Feinkost liegt zentrumsnah in der schönen Theobaldgasse im 6. Bezirk. Hier werden ausgewählte Bio Produkte für den täglichen Bedarf angeboten. -Iko | www.iko.wien | | +43 1 890 4200 | Mo-Fr. 11-22, Sa 12-22 | Im Gassengewirr der Inneren Stadt kann man sich schon mal verlieren. Auf der Suche nach dem Alten Rathaus findet man auch das IKO, in verstaubten Außenmauern ein kleines schickes, modernes Restaurant mit asiatischer Fusionsküche. -J. Hornig Kaffebar | www.jhornig.at | kaffeebar@jhornig.at | +43 1 522 2251 | Mo-Fr 7:30-19, Sa+So+Feiertage 9-19h | Im Herzen des Szeneviertels Neubau hat sich die Spezialitätenrösterei J. Hornig niedergelassen. -Kartonage Kitchen | www.kartonage.kitchen | | +43 660 6387346 | Mo-Fr 8-18:30 | Frühstück verschlafen? Auf zur Kartonage Kitschen - einem kleinen Café in der Burggasse. Hier bekommt ihr Kaffee, Frühstück und Mittagessen To Go. -Kimbo Dogs - Old Idea New Hot Dogs | www.kimbodogs.com | | | | Hot Dogs - lecker! Besonders schmackhaft sind die Gourmet Brötchen mit Würstel und köstlichen Toppings von Kimbo Dogs. -Leones Gelato | www.leones.at | ciao@leones.at | +43 1 352 5252 | Siehe Homepage | In Süditalien haben Giorgio und Lisa ihre Leidenschaft für traditionelles italienisches Eis entdeckt und ihr Handwerk erlernt. -Lingenhel | www.lingenhel.com | | +43 1 710 1566 | Mo-Sa 8-22 | Die erste und einzige Stadtkäserei Wiens hat ihre Türen geöffnet. Lingenhel ist Genuss-Oase, Feinschmecker-Treffpunkt, Käse-Erlebniswelt und urbane Lebensmittel-Werkstätte in einem. -Lunzers Mass-Greisslerei | www.mass-greisslerei.at | | +43 1 212 1387 | Mo-Fr 9-19, Sa 9-17 | Verpackungsfrei und nach eigenem Bedarf regionale Lebensmittel erwerben und der Umwelt etwas Gutes tun, das ist die Idee von Lunzers Maß-Greißlerei. -Macaroom | www.macaroom.at | | | Di-Fr 12-18, Sa 10-14 | Es darf niemand behaupten Macarons gegessen zu haben, ehe er nicht die von Melinda gekostet hat. -Mani | www.mani-wien.at | welcome@mani-wien.at | +43 1 402 4317 | Sommer: Di-Fr 15-23, Sa 9-23 | Mid East reicht Wien die Hand - ein absolutes Muss für Hummus-FeinschmeckerInnen. -Nam Nam - Der Inder | www.nam-nam.at | office@nam-nam.at | +43 1 595 6127 | Di-So 11-14:30 & 18-23 | Das Nam Nam ist Wiens bekanntester und Gerüchten zu Folge auch bester Inder. Weit weg vom klischeehaften Kitsch ist es hier nicht nur modern und gemütlich, sondern ein Palast der vielseitigen Küche Indiens. -Phil | www.phil.info | | +43 1 581 0489 | Mo 17-1, Di-So 9-1 | Auf der hippen Gumpendorfer Straße lässdt sich im phil viel erledigen: Bücher lesen, über Gott und die Welt reden, Leute schauen, gerettete Designerstücke kaufen, Musik hören... -Pitawerk | www.pitawerk.at | | +43 1 941 8190 | Mo-Fr 9-21, Sa+So+Feiertag 10-21h | Auf der äußeren Mariahilfer Straße werden Pitas nach einer traditionellen Rezeptur täglich frisch hergestellt. Erlernt von einem der besten Bäckermeister in Bosnien-Herzegowina mit ausschließlich frischen und regionalen Zutaten. -S'Frische - Das fitte Bistro | www.sfrische.at | | +43 676 905 3520 | Mo-Fr 7-18, Sa 9-13 | Im s'frische - das fitte bistro dreht sich alles um eine gesunde Ernährung und einen freshen Lifestyle. -Sanfish | www.sanfisch.at | | +43 664 4138090 | Siehe Homepage | Dominik hat eine Vorliebe für gutes Essen, kocht gerne und liebt Meeresfrüchte. Auf Reisen nach Südostasien und in die USA entstand die Idee Fisch in verschiedenen Variationen auf die Teller der Wiener zu zaubern. -Schachtelw!rt | www.schachtelwirt.at | | +43 1 432 0707 | Di-Fr 11:30-21, Sa 12-22 | Beim Schachtelw!rt wird in der Box gedacht. Schnelle Hausmannskost in einem kleinen Lokal in der Partymeile Bermudadreieck. -Schelato | www.schelato.at | info@schelato.at | | im Sommer: Mo-So. 12-22 | Carrot Cake, Waldbeer-Zweigelt, Mohn-Maracuja, Granatapfel-Basilikum, Lakritz - die Sorten von Schelato klingen wie ein süßer Traum, aus dem man erwacht und nur eines will: Eis, Eis, Eis. -Schrittesser Speck&Bar | www.schrittesser.at | | +43 1 890 6146 | Mo-Fr. 12-0, Sa. 18-0 | Direkt hinter der Hauptuni, zwischen Rathaus und Votivkirche, liegt Klein-Kärnten. Unter einem wunderschönen Stilhaus mit Arkadengang sitzend könnt ihr hier hochwertige Bioprodukte aus Kärnten verputzen. -Shoyu Ramen | www.shoyu.at | | +43 1 941 5018 | Mo-Sa. 11:30-22 | Hilft garantiert bei Heim- oder Fernweh: die japanische Hausmannskost im SHOYU. Mitten in Wien serviert das SHOYU hausgemachte Ramen, das beliebteste Soulfood Asiens, das mit seinen vielen Varianten unterschiedlicher, aufwändig hergestellter Suppenfonds, Nudelsorten und vielfältigen Toppings jeden Geschmack trifft. -Simply Raw Bakery | www.simplyrawbakery.at | | +43 677 62469124 | Mo-Sa. 9-18 | Gabriele und Shanna sagen den Vorurteilen gegenüber Rohkost den Kampf an. Also weg mit Sellerie und Karottenstangerl und her mit Sacherwürfeln, Trüffeln und anderen Leckerbissen. -Sägewerk Wien | www.diebausatzlokale.at | | +43 1 942 2489 | Mo-So. 11-2 | Viele typische Stuendtenlokale gibt es in Wien nicht. Das Sägewerk ist allerdings eines, das diesen Titel tragen darf. Es gehört zu der bekannten Bausatzlokalgruppe, die insgesamt 12 Lokale in Graz und Wien betreiben. -The Room - Sofiensäle | www.theroom.at | | +43 1 710 5577 | Mo-Fr. 11:30-0, Sa. 10-0, So. 10-16 | Wo lässt es sich besser ein Gläschen Champagner schlürfen als in den historischen Räumen der geschichtsträchtigen Sofiensäle? Ein Erlebnis zu jeder Tages- und Nachtzeit. -Toma Tu Tiempo | www.tomatutiempo.at | | +43 660 447 7069 | Mo. 17-23, Di-Mi. 12-23, Do-Fr. 12-0, Sa. 11-0, So, 11-22 | Auftrag: Zeit nehmen im Toma Tu Tiempo. Zeit zum Essen, Zeit zum Genießen, Zeit, um das südländische Flair auf sich wirken zu lassen. Neben spanischem Wein bringt Lucia - die eigentlich Juristin ist - mit ihrem spanischsprachigen Team leckere Tapas auf den Tisch. -Weingut & Heuriger Christ | www.weingut-christ.at | | +43 1 292 5152 | Ungerade Monate: Mo-So. 15-0 | Unzählige Male besungen, gilt der Heurige als Treffpunkt und Kulturgut der Wiener. Schon an den Grenzen zu Wiens Weinbergen befindet sich in Jedlersdorf einer der modernsten Wiener Heurigen, Teil des Weinguts Christ. -Wrapstars | www.wrapstars.at | | +43 664 247 3263 | Siehe Homepage | Ein Wiener Food Truck hat sich vorgenommen, die Welt durch besseres Essen zu retten, Wrap für Wrap. -Zuppa | www.zuppa.at | | +43 1 405 8500 | Mo-Do. 8-17, Fr. 8-16 | Täglich frisch gekocht wird im Zuppa. Neben abwechslungsreichen Suppen und Eintöpfen, gibt es bei Heidi Mayrhofer täglich mindestens zwei wechselnde Hauptsachen (veggie und nicht veggie) und verschiedene köstliche Desserts. -Zur Herknerin | www.zurherknerin.at | | +43 699 1522 0522 | Di-Fr. 17-23 | Im ehemaligen Installateurgeschäft an der Wiedner Hauptstraße findet sich heute Hausmannskost deluxe. -Achtundzwanzig | www.achtundzwanzig.at | | +43 664 562 3786 | Mo-Fr. 16-1, Sa. 18-2 | "Direkt vis-à-vis der ""people on caffeine"" dreht sich alles um den Wein. An der großen Kreidetafel über der Bar im Achtundzwanzig lässt sich ausfindig machen, welche Weine hier offen ausgeschenkt werden. Allesamt österreichischer Herkunnft, allesamt abseits der üblichen Verdächtigen." -Au - Raum für Kunst und Kultur | www.viennau.com | | | So-Do+Feiertag 18-2h, Fr-Sa. 18-4 | Das AU gleich beim Yppenplatz ist das beste Lokal für ein gemütliches Bier. Lauter kann's mal werden, wenn im hinteren Clubraum die Anlage angeworfen wird und Partys stattfinden, auf die man eher zufällig stößt. -Beaver Brewing Company | www.beaverbrewing.at | office@beaverbrewing.at | +43 677 610 12253 | Mo-Do 16-0, Fr. 16-1, Sa. 10-1, So. 10-22 | Hochqualitatives Bier und Essen zu fairen Preisen: Bei der Beaver Brewing Company bekommt ihr genau das. -Bits and Bites | www.bitsandbites.at | office@bitsandbites.at | +43 660 8372509 | Mi-Sa. 18-22h, Mi-So. 10-15h | Im Bits and Bites erwartet die Gäste ein ansprechendes Ambiente mit toller Speisekarte. Die Brasserie ist berühmt für leckeren Brunch und Dinnerkreationen auf Haubenniveau zum leistbaren Preis. -Black Dogs Coffee | www.blackdogs.at | | +43 664 536 6088 | Di-Sa. 9-12:30 + 17-22 | Cimbali, M39, Greaf Contessa: Begriffe, die gerne im Black Dogs Coffee fallen. Ein Kleinod für Third Wave Coffee schufen zwei Freunde aus Leidenschaft für Kaffee. -Botanical Garden | www.botanicalgarden.at | | +43 676 4422553 | Mo-Do 17-2, Fr+Sa 17-3 | Den botanischen Garten findet man hier auf den Beeten der Bar und im Cocktailglas. Kräuter aus Eigenanbau geben den Drinkkreationen die besondere Würze. -Brauwerk | www.brauwerk.wien | | +43 1 491 005 480 | Siehe Homepage | Das Brauwerk ist Kreativwerkstatt rund um das Thema Gerstensaft. Braumeister und Brauwerkler experimentieren im modernen Glasbau, während die Gäste an der Bar gegenüber der Kesssel die ersten Kreationen verkosten. -Buschenschank & Bioweinbau Obermann | www.weinbauobermann.at | | +43 664 451 9927 | Mi-So, ab Okt: Fr-So | Ein kurzer Fußmarsch durch die Weingärten von Wien wird nicht nur mit einer herrlichen Aussicht belohnt, sondern auch mit einem guten Glas Wein im Weingut Obermann. In fünfter Generation werden hier die lokalen Rebsorten wie Grüner Veltliner oder Riesling zu Wein gemacht. -Café Klitzeklein | www.cafeklitzeklein.at | | | Siehe Homepage | Ein wahres Kleinod: Wer den Massen am Eingang zum Prater aus dem Weg gehen möchte, ohne auf seinen schmackhaften Cappuccino mit Ausblick auf das Riesenrad zu verzichten, ist hier genau richtig. -Café Zeitgenossin | . | | +43 699 1073 8865 | Mo. 12-17:30, Di-Fr. 9:30-17:30 | Kerstin hat sich einen Traum erfüllt und ihr Café Zeitgenossin in Döbling eröffnet. Als Sozialpädagogin hat sie auf einem Segelschiff gearbeitet und an der Küste Englands ihre Liebe für Scones entdeckt. Heute steht sie als Zeitgenossin gerne für einen Plausch zur Verfügung. -Creme de la Creme | www.cremedelacreme.at | hallo@cremedelacreme.at | +43 660 283 3769 | Di-Fr. 9-18, Sa-So 10-17 | Zum Anbeißen ist Julias mintfarbene Patisserie unweit des Alten AKH in der Wiener Josefstadt. Bugholzstühle, modern interpretierte Luster und hoher Plafond. -Deli Bluem | www.delibluem.at | | +43 1 8900 449 | Mo-Fr. 8-19h, Sa+So 9-18 | Im deli bluem gibt es frisches, lokal-saisonales, pflanzliches Slow-Food. In den Räumlichkeiten des Volkskundemuseums und am Hamerlingplatz wird auf eine moderne und gemütliche Atmosphäre gesetzt. -Jetzt Bar & Entertainment | www.dasjetzt.at | info@dasjetzt.at | +43 1 4857 680 | Mo-Do 1803, Fr+Sa 18-4, So 18-2 | Zweites Wohnzimmer, Zufluchtsort, Bierkneipe und noch vieles mehr vereint das Jetzt unter einem Dach. Besonders beliebt sind die wechselnden Biersorten vom Fass und in den heißen Wiener Sommernächten der grüne Innenhof. -Jonas Reindl Coffee | www.jonasreindl.at | | | Mo-Fr. 7:30-22, Sa. 10-22 | Jonas Reindl Coffee bietet ein außergewöhnliches Kaffee-Erlebnis. Dazu sind viele Partner notwendig, die einen wichtigen Teil beitragen, bis der Kaffee in den Tassen oder im Becher der Gäste landet. -Kaffeefabrik | www.kaffeefabrik.at | | +43 660 1789 092 | Mo-Fr.8-18, Sa. 11-17 | Die kaffeefabrik ist eine kleine Kaffeebar und eine Kaffeerösterei, in der man hervorragenden, direkt importierten Bio-Kaffee aus Äthiopien, Brasilien oder Ecuador genießen kann - klassisch aus der Espressomaschine oder sorgfältig von Hand aufgegossen als Filterkaffee. -Kaffeemodul | www.kaffeemodul.at | | +43 680 2228039 | Mo-Fr 7:30-17:30, Sa. 10-14 | Munter wird man im Kaffeemodul in der Josefstadt. Im Paradies für Kaffeeliebhaber bekommt ihr neben Kaffee-Klassikern wie cremigem Cappuccino und starkem Ristretto auch fruchtigen Espresso, Flat White und Milchkaffee, heiß oder geeist und dazu süße Kleinigkeiten von der Tortenmanufaktur Zola Auböck. -Kaffemik | www.kaffemik.at | | +43 650 4141 535 | Mo-Fr 8-18, Sa 10-18, So+Feiertags 12-18 | Klein, hübsch und puristisch kommt das Kaffemik daher. Third Wave Coffee kommt hier in die Tassen. -Polkadot Vienna | www.polkadot.at | | +43 1 407 4125 | Mo-Do 19-2h, Fr +Sa 20-4, So 19-0 | Auf der Suche nach einem richtig gemütlichen Studentenbeisl? Hier stimmt nicht nur das Ambiente, auch das Bierangebot darf sich sehen lassen. -Tapete | www.tapete.bar, www.fb.com/tapete.bar | office@tapete.bar | +43 19664 346 | Di-Do + So 18-0, Fr+Sa. 18-2 | "Der Name ist Programm: Tapetenmuster vergangener Tage bilden einen Fleckerlteppich an Teilen der Wand, das restliche Gemäuer blitzt in Baustellenoptik zwischen Blümchenmuster hervor. ""Eine Mischung aus Lagerhalle und Oma's Wohnzimmer"" nennen es die Betreiber selbst." -Adamah Biohof | www.adamah.at | | +43 2248 2224 | | Im nahegelegenen Mrchfeld gedeiht bestes BioGemüse am ADAMAH BioHof, dem Vorreiter der BioKistln. In verschiedenen Varianten kann man sich die Vitaminbomben bequem online nach Hause bestellen. -Archäonow | www.archaeo-now.com | | +43 2248 2224 | | Alte Römerwege, gruselige Schauergeschichten, Wiener Kaffeehaus-Flair - die Rätselrallyes von Archäo Now sind ein interaktives Erlebnisspiel durch die Innenstadt. -Arnulf Rainer Museum | www.arnulf-rainer-museum.at | office@arnulf-rainer-museum.at | +43 2252 209196 | | Dem 1929 in Baden geborenen österreichischen Künstler widmet das Arnulf Rainer museum ebendort einen gebührenden Schauraum. Bekannt für seine Übermalungen zeigt man in den regelmäßig wechselnden Ausstellungen aber auch andere Schaffensperioden. -Bank Austria Kunstforum Wien | www.kunstforumwien.at | | +43 1 5373326 | | Das Bank Austria Kunstforum Wien ist für Kulturinteressierte die erste Anlaufstelle, wenn es um die VIP's der Malerei geht. In international aufsehenerregenden Wechselausstellungen präsentiert das Ausstellungshaus in der Wiener Innenstadt die Geschichte der klassischen Moderne und ihre Folgen in Kooperationen mit den berühmtesten Sammlungen der Welt. -Blün | www.bluen.at | | +43 1 7741333 | | BLÜN ist die erste kommerzielle Aquaponikanlage Wiens. Es werden nachhaltige Technologie, Fischzucht und Gemüseanbau in einem geschlossenem Kreislauf vereint. -Brut | www.brut-wien.at | | +43 1 5878774 | | brut ist eine Produktions- und Spielstätte für Performative Künste in Wien und gehört zu den renommiertesten Häusern der freien Performance-, Tanz- und Theaterszene im deutschsprachigen Raum. -Burg Kino | www.burgkino.at | | +43 1 5878406 | | Familiäres Oldschool-Kinoflair mitten in der Stadt. Im Burg Kino, einem der ältesten Kinos der Welt und dem ersten Originalfassungskino in Wien, wird seit über hundert Jahren Wert auf ein qualitätsvolles und abwechslungsreiches Programm gelegt. -Cyberlab | www.cyberlab.at | | +43 1 522400400 | | "Im inoffiziellen ""photo-district"" Wiens befindet sich unweit der Galerie WestLicht das beliebteste Fotolabor der Stadt, das cyberlab. Hier trifft man professionelle Fotografen, Amateure und auch Lomographen." -Dialog im Dunkeln | www.imdunkeln.at, www.facebook.com/dialogimdunkeln | | +43 1 8906060 | | Dialog im Dunkeln - kein Augenblick Alltag. In der lichtlosen Erlebnisinstallation führen sehbehinderte Guides durch eine blinde Wirklichkeit, die dennoch voll von verschiedensten bunten Eindrücken ist. -Die Buntique | https://www.diebuntique.at | | +43 664 75110469 | | Von Hand mit Herz: Liebevoll Genähtes, knallbunt Designtes, fröhlich Gezeichnetes für Klein und Groß. Das fröhliche und kunterbunte Sortiment wird von drei kreativen Frauen hergestellt und durch DIY-Workshops ergänzt, in denen Interessenten selbst kreativ werden können. -Dritte Mann Museum | www.3mpc.net | | +43 1 5864872 | | "Wer hat ihn nicht gesehen? Der 1948 in Wien gedrehte Filmklassiker ""Der dritte Mann"" erfreut sich nach wie vor internationaler Beliebtheit. Das Dritte Mann Museum ist Anlaufstelle für Filminteressierte und Türöffner zur Wiener Zeitgeschichte." -Fesch'Markt Wien | www.feschmarkt.info | | | | Der FESCH'MARKT ist Österreichs größtes Marktfestival für Kunst und Design. Zweimal im Jahr ist er der Place-to-be für alle die gerne individuell shoppen, sich gerne inspirieren oder einfach die Seele baumeln lassen möchten. -Gartenbaukino | www.gartenbaukino.at | | +43 1 5122354 | | Mit 736 Sitzplätzen ist es das letzte Einsaalkino in der Wiener Innenstadt. Neben regulären Filmvorführungen dient das Gartenbaukino auch als Schauplatz für zahlreiche Filmpremieren und unter anderem als Spielstätte der Viennale. -Hands Up | www.handsup.wien | | +43 1 3193701 | | Mittern in Wien kann man Stille neu erleben. In einer Erlebnisausstellung taucht man für eine Stunde in die Welt der Gehörlosen ein. Ohne zu hören und ohne zu sprechen gehen Besucher_innen durch die Ausstellung - alleine oder geführt von gehörlosen Guides. -Haus der Musik | www.hausdermusik.at | | +43 1 5134850 | | Wien ruht sich auf seiner Tradition als Musik- und Hochkulturstadt aus? Das Wiener Klangmuseum nicht. Es geht vielmehr einen Schritt weiter und wirft einen Blick in die Zukunft. Weltweit einzigartig lädt es in den Räumen des ehemaligen Palais Erzherzog Karls zur Interaktion ein. -Kauf Dich Glücklich | www.kaufdichgluecklich.de | | +43 1 9247755 | | "Bei Kauf Dich Glücklich findet ihr eine gelungene Auswahl an Damen- und Herrenmode, Wohnaccessoires, Naturkosmetik, Schmuck und Geschenkartikeln. Das Sortiment mit Schwerpunkt auf skandinavischem Design reicht von kleineren Labels über etablierte Modemarken bis hin zu Designer Brands und einer eigenen ""Kauf Dich Glücklich""-Kolektion." -Kunst Haus Wien Museum Hundertwasser | www.kunsthauswien.com | | +43 1 7120491 | | Das lustige, bunte Haus im driten Bezirk beherbergt heute das KUNST HAUS WIEN und wurde vom berühmten Künstler Hundertwasser gestaltet. Keine Wand, keine Decke und kein Boden sind eben, die Malereien farbenfroh und pointiert. -Kunsthalle Wien | www.kunsthallewien.at | | +43 1 521890 | | Die Kunsthalle Wien ist das Ausstellungshaus für zeitgenössische Kunst und Diskurs der Stadt Wien. Ein Ort der Auseinandersetzung, des Experiments, der permanenten Aktion und Veränderung - ein Haus, in dem aktuelle gesellschaftliche Fragestellungen verhandelt werden. -Metro Kinokulturhaus | www.filmarchiv.at | | +43 1 5121803 | | Ein Kino mit viel Geschichte ist das Metro Kino Kulturhaus unweit des Stephansdoms. Noch heute ist die ursprüngliche Profession des Gebäudes nicht zu verschweigen. 1840 wurde es als Theater gegründet und er historische Saal verfügt über samterne Sitze in Rot und schwere Holzlogen im Hochparterre. -Mumok | www.mumok.at | | +43 1 525000 | | Das mumok ist das wichtigste österreichische Museum für moderne und zeitgenössische Kunst. -Ottakringer Brauerei | www.ottakringer.at | | +43 1 491000 | | Die Ottakringer Brauerei ist ein Stück Wien, das ihr euch nicht entgehen lassen solltet: Seit 180 Jahren werden hier nicht nur Biere von höchster Qualität gebraut, die denkmalgeschützten Räumlichkeiten werden zudem für die Stadt geöffnet. -Pygmalion Theater Wien | www.pygmaliontheater.at | | +43 681 20754496 | | Das Pygmalion Theater Wien ist ein Laboratorium moderne Theaterideen, in welchem auf der Grundlage fundierter Kenntnisse des tradierten Theaters des Abendlandes neue Wege gesucht werden. -Radio The Label Bar | www.radiobar.berlin | | +43 676 9363129 | | Viele Farben, Tapeten, Flohmarkt-Equipment und ein Einhorn - das Lieblingstier der Besitzerin begrüßt die Besucher in der Berliner Bar in Wien. Hier kann man immer kommen, ob zum Techno-Trödel, zum Barsitzen, Tanzen oder Klamottenstöbern. -RE:TREAT - Home for Yoga, Meditation & Vegan | www.retreat-vienna.com | | | | Das Yogastudio RE:TREAT achtet auf die Tradition des Yogas, nimmt sich aber auch die Freiheit diese neu zu deuten. Ob Anfänger oder Profi, jeder ist willkommen. -Schikaneder Kino | www.schikaneder.at | | +43 1 5852867 | | Das Schikaneder ist eines der ältesten Kinos Wien und feierte kürzlich sein 110-jähriges Jubiläum. -Schokomuseum | www.schokomuseum.at | | +43 1 667211019 | | Woher kommt eigentlich Kakao? Wie wird weiße Schokolade hergestellt? Und wofür steht Fairtrade? Dall das und vieles mehr erfahren alle wissbegierigen Naschkatzen und Schokotiger im süßesten Museum Wiens! -Sophort | www.sophort.com | | +43 676 7735872 | | Geht mit auf Entdeckungsreise! Bei der Polaroid-Fototour bekommt ihr eine originale Polaroid-Leihkamera, einen Film und die Erklärung, wie es funktioniert. -Stadtkino im Künstlerhaus | www.stadtkinowien.at | | +43 1 7126276 | | Vor mehr als hundert Jahren als Schwarzenbergkino eröffnet, ist es eines der ältesten Programmkinos Wiens und hat im Künstlerhaus seit 2013 eine neue Heimat gefunden. Der Saal gilt als einer der schönsten der Stadt. -Tag | www.dastag.at, www.facebook.com/TAGtheater | | +43 1 5865222 | | Das TAG - Theater an der Gumpendorfer Straße - produziert mit viel Leidenschaft und Anspruch kurzweiliges, zeitgemäßes Sprechtheater. -The Loft | www.theloft.at | | +43 1 9477619 | | The Loft ist ein Fixpunkt in der Wiener Clubszene. Das vielfältige Publikum plaudert im Café gleich beim Eingang und feiert auf bis zu drei Floors zu cooler Musik. -Theater Akzent | www.akzent.at | | +43 1 5016513306 | | Das Theater Akzent ist mit drei Theatersälen ein Ort der kulturellen Begegnung. Ein vielfältiger und abwechslungsreicher Spielplan bietet ZuschauerInnen aktuelle und künstlerisch ansprechende Produktionen aus dem In- und Ausland. -Theater im Zentrum | www.tdj.at | | +43 1 521100 | | Das Theater im Zentrum ist eine intime Wiener Innenstadtbühne, die auf eine bedeutende Geschichte bis in das Jahr 1913 zurückblicken darf. Heute fungiert es als zweite Spielstätte des Theaters der Jugend. -Top Kino | www.topkino.at | office@topkino.at | +43 1 208 3000 | Mo-Mi. 11-2, Do-Sa. 11-4, So. 10:30-0 | Im Top Kino werden Cinephile nicht nur durch bewegte Bilder verwöhnt. Es bietet Spielfilme und Dokumentationen und ein reichhaltiges Angebot an Speisen und Getränken in gemütlicher Wohnzimmeratmosphäre. -Viadukt Screen Prints | www.viadukt.at | | +43 664 9595714 | | Viadukt Screen Prints ist eine von wenigen Siebdruckwerkstätten in Wien. Neben der künstlerischen Produktion verschiedenster lokaler KünstlerInnen und DesignerInnen werden in regelmäßigen Abständen internationale KünstlerInnen und DesignerInnen in die Studios eingeladen. -Volkskundemuseum Wien | www.volkskundemuseum.at | | +43 1 4068905 | | Das Volkskundemuseum Wien ist ein kulturwissenschaftliches Museum und versteht sich als offener Ort für NutzerInnen. Es beherbergt umfangreiche Sammlungen aus der ehemaligen Habsburgermonarchie. -Volkstheater | www.volkstheater.at | | +43 1 521110 | | Das Volkstheater ist das zweitgrößte Sprechtheater Wiens und nimmt die Hauptstadt Österreichs im Austausch mit der Welt in den Blick. -Werk | https://www.daswerk.org | | | | Kunst lebt, Kunst leibt, Kunst WERKt! Im 2006 eröffneten WERK eröffnet sich eine Vielzahl an unbeschreibbaren Möglichkeiten: Der schönste Club Wien, Ateliers, Sound- und Fotostudio, WERKstatt, Konzertlocation, Kunstcafé, Gastgarten direkt am Donaukanalufer und Hafen verändern bereits jetzt und in Zukunft den Donaukanal an der Spittelau. -Werk X | www.werk-x.at | | +43 1 9626110 | | Das WERK X in Wien Meidling ist ein Verhandlungsraum zentraler gesellschaftlicher Gegenwartsfragen für progressive und politisch engagierte KünstlerInnen. Im Zentrum des Programms stehen alle zeitgenössischen Formen des Sprechtheaters. -Wiener Riesenrad | www.wienerriesenrad.com | | +43 1 7295430 | | Wer nicht einmal eine Runde mit dem Riesenrad gedreht hat und die tolle Aussicht über die Dächer der Stadt genossen hat, war nicht wirklich in Wien. 1897 anlässlich des 50. Thronjubiläums von Kaiser Franz Joseph I. erbaut, ist das Wiener Riesenrad seit jeher ein beliebter Treffpunkt für jedermann. -Wienführung | www.wienfuehrung.com | | +43 676 9227773 | | Wer sich tiefer in das Innere Wiens vorwagen will, dem seien die vielfältigen Führungen von Wienfuehrung nahegelegt: Spaziergänge zu den schönsten Winkeln der Altstadt mit einem Blick auf die Wiener Seele. -WUK Werkstätten und Kulturhaus | www.wuk.at | | +43 1 401210 | | Die ehemalige Lokomotivfabrik ist ein alternativer Veranstaltungsort für Musik, Performance, Tanz, Theater, Kunst und Kinderkultur. Als offener Kulturraum vereint das WUK Kreativität, politisches Engagement und Party. -Österreichisches Filmmuseum | www.filmmuseum.at | | +43 1 5337054 | | "Das Österreichische Filmmuseum, im Erdgeschoß der Albertina beheimatet, ist mer als ""nur"" ein Kino: Cinémathèque, Archiv, Forschungsstätte, Vermittler von Film in all seinen Dimensionen und ein Ort des fachkundigen AUstausches." diff --git a/luupsmap/cli/commands/seed.py b/luupsmap/cli/commands/seed.py index 3abe3a5..e93472e 100644 --- a/luupsmap/cli/commands/seed.py +++ b/luupsmap/cli/commands/seed.py @@ -1,11 +1,13 @@ from luupsmap import db from luupsmap.cli.util import CsvFile -from luupsmap.model import Venue, Location, Voucher, VoucherType, VoucherTag, Type, Tag +from luupsmap.cli.util.interval_parser import IntervalParser +from luupsmap.model import Venue, Location, Voucher, VoucherType, VoucherTag, Type, Tag, Interval class SeedCommand: venues = [] locations = {} + intervals = {} vouchers = {} def __init__(self, venues_file, locations_file, vouchers_file): @@ -18,62 +20,76 @@ def __init__(self, venues_file, locations_file, vouchers_file): def run(self): print('Start seeding tables...'.ljust(25), end=' ') self.venues = self.venues_file.load() - self._load_locations() - self._load_vouchers() - self._create_and_save_models() + self.__load_locations() + self.__load_vouchers() + self.__create_and_save_models() print('Done') - def _load_locations(self): + def __load_locations(self): locations = self.locations_file.load() for location in locations: name = location['name'] self.locations.setdefault(name, []).append(location) - def _load_vouchers(self): + def __load_vouchers(self): vouchers = self.vouchers_file.load() for voucher in vouchers: name = voucher['name'] self.vouchers.setdefault(name, []).append(voucher) - def _create_and_save_models(self): + def __create_and_save_models(self): venues = [] locations = [] + intervals = [] vouchers = [] for venue in self.venues: venue['vouchers'] = [] venue['locations'] = [] venue = Venue(venue) - self._create_locations(venue, locations) - self._create_vouchers(venue, vouchers) + self.__create_locations(venue, locations, intervals) + self.__create_vouchers(venue, vouchers) db.session.add_all(venues) db.session.add_all(locations) + db.session.add_all(intervals) db.session.add_all(vouchers) db.session.commit() - def _create_locations(self, venue, locations): + def __create_locations(self, venue, locations, intervals): name = venue.name if name not in self.locations: return for location in self.locations[name]: location['venue'] = venue + opening_hours = location['opening_hours'] + location['opening_hours'] = [] location = Location(location) + + self.__create__intervals(location, opening_hours, intervals) venue.locations.append(location) locations.append(location) - def _create_vouchers(self, venue, vouchers): + def __create__intervals(self, location, opening_hours, intervals): + parsed_intervals = IntervalParser().parse(opening_hours) + for interval in parsed_intervals: + interval['location'] = location + model = Interval(interval) + intervals.append(model) + location.opening_hours.append(model) + + def __create_vouchers(self, venue, vouchers): name = venue.name if name not in self.vouchers: return for voucher in self.vouchers[name]: - self._convert_voucher_types(voucher) - self._convert_voucher_tags(voucher) + self.__convert_voucher_types(voucher) + self.__convert_voucher_tags(voucher) voucher['venue'] = venue voucher = Voucher(voucher) venue.vouchers.append(voucher) vouchers.append(voucher) @staticmethod - def _convert_voucher_types(voucher): + def __convert_voucher_types(voucher): # In case we already converted string to proper types if type(voucher['voucher_types']) == list: return @@ -81,7 +97,7 @@ def _convert_voucher_types(voucher): voucher['voucher_types'] = [VoucherType(Type[voucher_type]) for voucher_type in voucher_types] @staticmethod - def _convert_voucher_tags(voucher): + def __convert_voucher_tags(voucher): # In case we already converted string to proper types if type(voucher['voucher_tags']) == list: return diff --git a/luupsmap/cli/commands/update.py b/luupsmap/cli/commands/update.py index 7927a3a..11392d7 100644 --- a/luupsmap/cli/commands/update.py +++ b/luupsmap/cli/commands/update.py @@ -14,12 +14,12 @@ def __init__(self, locations_file, venues_file): self.place_cache = PlaceCache() def run(self): - # self._load_venues() + self._load_venues() self._load_locations() - # self._update_venues() + self._update_venues() self._update_locations() - # self._create_locations() - # self._write_files() + self._create_locations() + self._write_files() def _load_venues(self): print('Loading venues file...'.ljust(LINE_LENGTH), end=' ') diff --git a/luupsmap/cli/data.py b/luupsmap/cli/data.py index ac22f79..9da176c 100644 --- a/luupsmap/cli/data.py +++ b/luupsmap/cli/data.py @@ -14,6 +14,13 @@ @data.command('seed') @with_appcontext def seed(): + """ + Resets the database, and seeds new data from CSV files + + Usage: + + flask data seed + """ remove_data() path = os.path.abspath(os.path.dirname(__file__)) venues_file = os.path.join(path, '..', '..', 'data', 'venues.csv') @@ -21,35 +28,56 @@ def seed(): vouchers_file = os.path.join(path, '..', '..', 'data', 'vouchers.csv') SeedCommand(venues_file, locations_file, vouchers_file).run() + @data.command('reset') @with_appcontext def reset(): + """ + Removes all data from the database, but leaves columns as is. + + Usage: + + flask data reset + """ remove_data() @data.command('nuke') @with_appcontext def reset(): + """ + Resets the database, dropping all tables. + + Usage: + + flask data nuke + """ nuke() @data.command('update') @with_appcontext def update(): + """ + Updates the CSV documents available for data seeding by fetching data from the Google API. + + Usage: + + flask data nuke + """ path = os.path.abspath(os.path.dirname(__file__)) locations = os.path.join(path, '..', '..', 'data', 'locations.csv') venues = os.path.join(path, '..', '..', 'data', 'venues.csv') UpdateCommand(locations_file=locations, venues_file=venues).run() -# noinspection SqlWithoutWhere def remove_data(): - # TODO: Use logger print('Removing existing data...'.ljust(LINE_LENGTH), end=' ') session = db.session session.query(VoucherTag).delete() session.query(VoucherType).delete() session.query(Voucher).delete() + session.query(Interval).delete() session.query(Location).delete() session.query(Venue).delete() session.commit() diff --git a/luupsmap/cli/util/interval_parser.py b/luupsmap/cli/util/interval_parser.py new file mode 100644 index 0000000..8f483ed --- /dev/null +++ b/luupsmap/cli/util/interval_parser.py @@ -0,0 +1,85 @@ +import calendar +import time + + +class IntervalParser: + DAY_MAPPING = {v[:2]: k for k, v in enumerate(calendar.day_abbr)} + + MONTH_MAPPING = {v: k for k, v in enumerate(calendar.month_abbr)} + + def __init__(self): + self.DAY_MAPPING.update({'Ft': 7}) + self.parsed = [] + + def parse(self, string): + if not string.strip(): + return [] + + blocks = [x.strip() for x in string.split(',')] + for block in blocks: + parts = [x.strip() for x in block.split(' ')] + self.__parse_parts(parts) + return self.parsed + + def __parse_parts(self, parts): + start_day, end_day = self.__parse_days(parts) + + start_hour, end_hour = self.__parse_hours(parts) + + start_month, end_month = self.__parse_months(parts) + + arguments = { + 'start_day': start_day, + 'end_day': end_day, + 'start_hour': start_hour, + 'end_hour': end_hour, + 'start_month': start_month, + 'end_month': end_month, + } + + if start_hour > end_hour: + self.__split_hours(arguments) + else: + self.parsed.append(arguments) + + def __parse_days(self, parts): + days = parts[0] + day_split = days.split('-') + start_day = self.DAY_MAPPING[day_split[0]] + end_day = self.DAY_MAPPING[day_split[1]] if len(day_split) == 2 else start_day + return start_day, end_day + + def __parse_hours(self, parts): + hours = parts[1] + hours_split = hours.split('-') + start_hour = time.strftime('%H:%M', self.__try_parse_hour(hours_split[0])) + end_hour = time.strftime('%H:%M', self.__try_parse_hour(hours_split[1])) + + return start_hour, end_hour + + def __parse_months(self, parts): + start_month = 1 + end_month = 12 + if len(parts) == 3: + months = parts[2] + month_split = months.split('-') + start_month = self.MONTH_MAPPING[month_split[0]] + end_month = self.MONTH_MAPPING[month_split[1]] if len(month_split) > 1 else start_month + return start_month, end_month + + @staticmethod + def __try_parse_hour(hour): + try: + start_hour = time.strptime(hour, "%H") + except ValueError: + start_hour = time.strptime(hour, "%H:%M") + return start_hour + + def __split_hours(self, arguments): + additional_args = arguments.copy() + arguments['end_hour'] = '24:00' + self.parsed.append(arguments) + additional_args['start_hour'] = '00:00' + additional_args['start_day'] = arguments['start_day'] + 1 + additional_args['end_day'] = arguments['end_day'] + 1 + self.parsed.append(additional_args) diff --git a/luupsmap/cli/util/place_cache.py b/luupsmap/cli/util/place_cache.py index 39dc4ee..82d18d2 100644 --- a/luupsmap/cli/util/place_cache.py +++ b/luupsmap/cli/util/place_cache.py @@ -3,6 +3,7 @@ from luupsmap import app +# Store place details, which are used multiple times to save on requests to the Google API class PlaceCache: places = {} diff --git a/luupsmap/dto/__init__.py b/luupsmap/dto/__init__.py index 370bd6e..9c8ac36 100644 --- a/luupsmap/dto/__init__.py +++ b/luupsmap/dto/__init__.py @@ -1,3 +1,4 @@ +from luupsmap.dto.opening_hours_dto import * from luupsmap.dto.location_dto import * from luupsmap.dto.voucher_dto import * from luupsmap.dto.venue_dto import * diff --git a/luupsmap/dto/location_dto.py b/luupsmap/dto/location_dto.py index 55a5f87..a030459 100644 --- a/luupsmap/dto/location_dto.py +++ b/luupsmap/dto/location_dto.py @@ -1,5 +1,7 @@ """Location DTOs.""" +from luupsmap.dto import OpeningHoursDto + class LocationDto(object): __slots__ = 'id', 'id_venue', 'address', 'latitude', 'longitude', 'email', 'phone', 'opening_hours' @@ -12,4 +14,12 @@ def __init__(self, data): self.longitude = data.longitude self.email = data.email self.phone = data.phone - self.opening_hours = data.opening_hours + self.opening_hours = OpeningHoursDto(data) + + +class LocationSearchDto(object): + __slots__ = 'opening_time', 'voucher_tags', 'voucher_types' + + def __init__(self, data): + for key in data: + setattr(self, key, data[key]) diff --git a/luupsmap/dto/opening_hours_dto.py b/luupsmap/dto/opening_hours_dto.py new file mode 100644 index 0000000..f3e5b50 --- /dev/null +++ b/luupsmap/dto/opening_hours_dto.py @@ -0,0 +1,10 @@ +"""DTOs related to intervals.""" +from luupsmap.service.opening_hour_service import OpeningHourService + + +class OpeningHoursDto(object): + __slots__ = 'id', 'id_location', 'entries' + + def __init__(self, location): + self.id_location = location.id + self.entries = OpeningHourService(location.opening_hours).get_readable() diff --git a/luupsmap/dto/venue_dto.py b/luupsmap/dto/venue_dto.py index fbe420e..842a56b 100644 --- a/luupsmap/dto/venue_dto.py +++ b/luupsmap/dto/venue_dto.py @@ -25,3 +25,4 @@ class VenueDetailDto(VenueDto): def __init__(self, data): super().__init__(data) self.description = data.description + self.opening_hours = "new" diff --git a/luupsmap/model/interval.py b/luupsmap/model/interval.py index cefa39f..61dc8fe 100644 --- a/luupsmap/model/interval.py +++ b/luupsmap/model/interval.py @@ -7,7 +7,7 @@ class Interval(db.Model): __tablename__ = 'interval' id = db.Column(db.Integer, primary_key=True) - id_venue = db.Column(db.Integer, db.ForeignKey('venue.id'), nullable=False) + id_location = db.Column(db.Integer, db.ForeignKey('location.id'), nullable=False) start_hour = db.Column(db.Time, nullable=False) end_hour = db.Column(db.Time, nullable=False) start_day = db.Column(db.Integer, nullable=False) @@ -16,15 +16,8 @@ class Interval(db.Model): end_month = db.Column(db.Integer, nullable=False) def __init__(self, data): - self.venue = data['venue'] - self.start_hour = data['start_hour'] - self.end_hour = data['end_hour'] - self.start_day = data['start_day'] - self.end_day = data['end_day'] - self.start_month = data['start_month'] - self.end_month = data['end_month'] + for key in data: + setattr(self, key, data[key]) def __repr__(self): return ''.format(self.id) - - diff --git a/luupsmap/model/location.py b/luupsmap/model/location.py index f46a1db..8ca27f3 100644 --- a/luupsmap/model/location.py +++ b/luupsmap/model/location.py @@ -12,18 +12,13 @@ class Location(db.Model): address = db.Column(db.String(256), nullable=False) email = db.Column(db.String(64)) phone = db.Column(db.String(32)) - opening_hours = db.Column(db.Text) + opening_hours = db.relationship("Interval", backref="location") latitude = db.Column(db.Float, nullable=False) longitude = db.Column(db.Float, nullable=False) def __init__(self, data): - self.address = data['address'] - self.email = data['email'] - self.phone = data['phone'] - self.opening_hours = data['opening_hours'] - self.latitude = data['latitude'] - self.longitude = data['longitude'] - self.venue = data['venue'] + for key in data: + setattr(self, key, data[key]) def __repr__(self): return ''.format(self.address) diff --git a/luupsmap/service/__init__.py b/luupsmap/service/__init__.py index 8678ce1..5544533 100644 --- a/luupsmap/service/__init__.py +++ b/luupsmap/service/__init__.py @@ -19,3 +19,4 @@ def convert_to_dto(*args, **kwargs): # IMPORTS from luupsmap.service.venue_service import * from luupsmap.service.location_service import * +from luupsmap.service.opening_hour_service import * diff --git a/luupsmap/service/location_service.py b/luupsmap/service/location_service.py index 055af92..572197e 100644 --- a/luupsmap/service/location_service.py +++ b/luupsmap/service/location_service.py @@ -8,13 +8,6 @@ class LocationService: def __init__(self): self.db_session = db.session - def find_all(self): - return self.__find_all() - - @as_dto(LocationDto) - def __find_all(self): - return self.db_session.query(Location).all() - @as_dto(LocationDto) def get(self, location_id): return self.db_session.query(Location).get(location_id) diff --git a/luupsmap/service/opening_hour_service.py b/luupsmap/service/opening_hour_service.py new file mode 100644 index 0000000..76a5b64 --- /dev/null +++ b/luupsmap/service/opening_hour_service.py @@ -0,0 +1,91 @@ +from calendar import different_locale, day_name, day_abbr, month_abbr, month_name + + +class OpeningHourService: + def __init__(self, opening_hours): + self.opening_hours = opening_hours + + def get_readable(self): + self.__merge_intervals() + entries = [] + for interval in self.opening_hours: + entries.append( + { + 'hours': self.__get_hours(interval), + 'days': self.__get_days(interval), + 'months': self.__get_months(interval), + }) + return entries + + def __merge_intervals(self): + merged = [] + intervals = self.opening_hours + i = 0 + while i < (len(intervals) - 1): + current_interval = intervals[i] + next_interval = intervals[i + 1] + days_match = (current_interval.start_day + 1 == next_interval.start_day) and ( + current_interval.end_day + 1 == next_interval.end_day) + if days_match: + current_interval.end_hour = next_interval.end_hour + merged.append(current_interval) + i = i + 2 + else: + merged.append(intervals[i]) + i = i + 1 + if i < (len(intervals)): + merged.append(intervals[i]) + merged.sort(key=(lambda x: x.start_day)) + self.opening_hours = merged + + def __get_days(self, interval): + start_day = self.__get_day_name(interval.start_day) + end_day = self.__get_day_name(interval.end_day) + if start_day == end_day: + return start_day + else: + return '{}-{}'.format(start_day, end_day) + + @staticmethod + def __get_day_name(day_no, short=False): + if day_no >= 7: + return 'Feiertag' + with different_locale('de_AT.UTF8') as encoding: + if short: + s = day_abbr[day_no] + else: + s = day_name[day_no] + if encoding is not None: + s = s.decode(encoding) + return s + + @staticmethod + def __get_hours(interval): + start_time = interval.start_hour.strftime("%H:%M") + end_time = interval.end_hour.strftime("%H:%M") + + if end_time == '00:00': + end_time = '24:00' + + return '{}-{}'.format(start_time, end_time) + + def __get_months(self, interval): + if interval.start_month == 1 and interval.end_month == 12: + return None + start_month = self.__get_month_name(interval.start_month) + end_month = self.__get_month_name(interval.end_month) + if start_month == end_month: + return '{}'.format(start_month) + else: + return '{} - {}'.format(start_month, end_month) + + @staticmethod + def __get_month_name(month_no, short=False): + with different_locale('de_AT.UTF8') as encoding: + if short: + s = month_abbr[month_no] + else: + s = month_name[month_no] + if encoding is not None: + s = s.decode(encoding) + return s diff --git a/luupsmap/service/venue_service.py b/luupsmap/service/venue_service.py index db9741e..72bddec 100644 --- a/luupsmap/service/venue_service.py +++ b/luupsmap/service/venue_service.py @@ -1,6 +1,6 @@ from luupsmap import db from luupsmap.dto.venue_dto import VenueDto, VenueDetailDto -from luupsmap.model import Venue, Voucher, VoucherType, VoucherTag +from luupsmap.model import Venue, Voucher, VoucherType, VoucherTag, Location, Interval from luupsmap.service import as_dto @@ -17,8 +17,8 @@ def find_all(self, include_type=True): return venues - def filter_by(self, types, tags): - venues = self.__filter_by(types, tags) + def filter_by(self, types, tags, time): + venues = self.__filter_by(types, tags, time) # TODO: Improve conversion to DTO so we dont' have to patch this afterwards for venue in venues: @@ -26,30 +26,46 @@ def filter_by(self, types, tags): return venues + @as_dto(VenueDetailDto) + def get(self, venue_id): + return self.db_session.query(Venue).get(venue_id) + + @as_dto(VenueDto) + def create(self, venue): + self.db_session.add(venue) + return self.db_session.commit() + @as_dto(VenueDto) def __find_all(self): return self.db_session.query(Venue).all() @as_dto(VenueDto) - def __filter_by(self, types, tags): + def __filter_by(self, types, tags, time): venues = self.db_session.query(Venue) \ .join(Voucher) \ - .join(VoucherType) \ - .outerjoin(VoucherTag) + .join(Location) if types: - venues = venues.filter(VoucherType.type.in_(types)) + venues = venues \ + .join(VoucherType) \ + .filter(VoucherType.type.in_(types)) if tags: - venues = venues.filter(VoucherTag.tag.in_(tags)) - return venues.all() + venues = venues \ + .join(VoucherTag) \ + .filter(VoucherTag.tag.in_(tags)) + if time: + weekday = time.weekday() + month = time.month + time = time.time() + venues = venues \ + .outerjoin(Interval) \ + .filter(time >= Interval.start_hour, + time <= Interval.end_hour, + weekday >= Interval.start_day, + weekday <= Interval.end_day, + month >= Interval.start_month, + month <= Interval.end_month) - @as_dto(VenueDetailDto) - def get(self, venue_id): - return self.db_session.query(Venue).get(venue_id) - - @as_dto(VenueDto) - def create(self, venue): - self.db_session.add(venue) - return self.db_session.commit() + return venues.all() @staticmethod def __determine_type(vouchers): diff --git a/luupsmap/static/css/custom.css b/luupsmap/static/css/custom.css index ec4ff45..8fc5dfc 100644 --- a/luupsmap/static/css/custom.css +++ b/luupsmap/static/css/custom.css @@ -1,6 +1,19 @@ /* FONTS **************************************************************************************************************/ @import url('https://fonts.googleapis.com/css?family=Amaranth:400,700|Open+Sans:300,400,700'); +/*CSS Variables? */ +:root { + --yellow: #ffab00; + --red: #c2185b; + --green: #24b37f; + --light-green: #25ba84; + --gris: #607d8b; + --gray: #484848; + --light-gray: #414141; + --color: #333; + --background: #fff; +} + body { font-family: 'Open Sans', sans-serif; } @@ -389,21 +402,25 @@ h2 + .voucher-container { } .icon-container.food { - background: #ffab00; + background: var(--yellow); } .icon-container.drink { - background: #607d8b; + background: var(--gris); } .icon-container.ticket { - background: #c2185b; + background: var(--red); } .icon-container svg { - fill: #fff; + fill: var(--background); } + +/* OPENING HOURS *****************************************************************************************************/ + + /* SVG ****************************************************************************************************************/ svg { diff --git a/luupsmap/static/css/picker.css b/luupsmap/static/css/picker.css new file mode 100755 index 0000000..ad60fa7 --- /dev/null +++ b/luupsmap/static/css/picker.css @@ -0,0 +1,305 @@ +/*! + * Picker.js v1.2.1 + * https://fengyuanchen.github.io/pickerjs + * + * Copyright 2016-present Chen Fengyuan + * Released under the MIT license + * + * Date: 2019-02-18T13:08:09.658Z + */ + +.picker { + color: #333; + color: var(--color); + direction: ltr; + display: none; + font-size: 1rem; + line-height: 1.5; + overflow: hidden; + -ms-touch-action: none; + touch-action: none; + -webkit-transition: opacity 0.15s; + transition: opacity 0.15s; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + border-radius: 5px; + border: 1px solid var(--green); +} + +.picker-fixed { + bottom: 0; + left: 0; + position: fixed; + right: 0; + top: 0; + z-index: 1986; +} + +.picker-fixed > .picker-dialog { + bottom: -100%; + left: 0; + max-height: 100%; + position: absolute; + right: 0; + -webkit-transition: bottom 0.3s; + transition: bottom 0.3s; +} + +.picker-fixed .picker-header { + display: block; +} + +.picker-fixed .picker-footer { + display: table; +} + +.picker-open { + display: block; + opacity: 0; +} + +.picker-opened { + opacity: 1; +} + +.picker-opened > .picker-dialog { + bottom: 0; +} + +.picker-dialog { + background-color: #fff; + border: 1px solid #eee; +} + +.picker-header { + border-bottom: 1px solid #eee; + display: none; + padding: 0.875rem 1.25rem; + position: relative; +} + +.picker-title { + font-size: 1.125rem; + font-weight: 500; + line-height: 1.25rem; + margin: 0; +} + +.picker-close { + background-color: transparent; + border-width: 0; + color: #999; + color: var(--gray); + cursor: pointer; + font-size: 1.75rem; + height: 3rem; + opacity: 0.75; + padding: 0; + position: absolute; + right: 0; + top: 0; + width: 3rem; +} + +.picker-close:focus, +.picker-close:hover { + opacity: 1; + outline: none; +} + +.picker-body { + overflow: hidden; +} + +.picker-grid { + display: table; + table-layout: fixed; + width: 100%; +} + +.picker-cell { + display: table-cell; + position: relative; +} + +.picker-cell::before, +.picker-cell::after { + content: ''; + display: block; + left: 0; + position: absolute; + right: 0; + z-index: 3; +} + +.picker-cell::before { + background-image: -webkit-gradient(linear, left bottom, left top, from(rgba(0, 0, 0, 0)), to(rgba(0, 0, 0, 0.05))); + background-image: linear-gradient(to top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.05)); + bottom: 50%; + margin-bottom: 1rem; + top: 0; +} + +.picker-cell::after { + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0)), to(rgba(0, 0, 0, 0.05))); + background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.05)); + bottom: 0; + margin-top: 1rem; + top: 50%; +} + +.picker-cell + .picker-cell { + border-left: 1px solid #eee; + border-left: var(--border); +} + +.picker-headers .picker-cell::before { + margin-bottom: 0; +} + +.picker-headers .picker-cell::after { + margin-top: 2rem; +} + +.picker-single:not(.picker-controls):not(.picker-headers) .picker-cell::before, +.picker-single:not(.picker-controls):not(.picker-headers) .picker-cell::after { + display: none; +} + +.picker-cell__header { + color: #999; + color: var(--gray); + font-size: 0.875rem; + font-weight: 500; + line-height: 1.5rem; + margin: 0; + overflow: hidden; + padding: 0.25rem 0.5rem; + text-align: center; + text-overflow: ellipsis; + white-space: nowrap; +} + +.picker-cell__control { + cursor: pointer; + height: 2rem; + padding: 0.25rem 0.5rem; + position: relative; + z-index: 4; +} + +.picker-cell__control::before { + border: 0 solid #ccc; + content: ''; + display: block; + height: 0.5rem; + left: 50%; + position: absolute; + top: 50%; + -webkit-transform: translate(-50%, -50%) rotate(-45deg); + -ms-transform: translate(-50%, -50%) rotate(-45deg); + transform: translate(-50%, -50%) rotate(-45deg); + width: 0.5rem; +} + +.picker-cell__control:hover::before { + border-color: var(--primary); +} + +.picker-cell__control--prev::before { + border-right-width: 1px; + border-top-width: 1px; + margin-top: 2px; +} + +.picker-cell__control--next::before { + border-bottom-width: 1px; + border-left-width: 1px; + margin-bottom: 2px; +} + +.picker-cell__body { + overflow: hidden; + position: relative; +} + +.picker-cell__body::before, +.picker-cell__body::after { + content: ''; + height: 2rem; + left: 0; + position: absolute; + right: 0; + z-index: 1; +} + +.picker-cell__body::before { + background-image: -webkit-gradient(linear, left bottom, left top, from(rgba(255, 255, 255, 0)), to(rgba(255, 255, 255, 1))); + background-image: linear-gradient(to top, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1)); + top: 0; +} + +.picker-cell__body::after { + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(255, 255, 255, 0)), to(rgba(255, 255, 255, 1))); + background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1)); + bottom: 0; +} + +.picker-single .picker-cell__body::before, +.picker-single .picker-cell__body::after { + display: none; +} + +.picker-list { + list-style: none; + margin: -2rem 0; + padding: 0; + position: relative; +} + +.picker-item { + color: #999; + color: var(--gray); + padding: 0.25rem 0.5rem; + text-align: center; + white-space: nowrap; +} + +.picker-picked { + color: var(--green); + font-size: 1.125em; + line-height: 1.5rem; +} + +.picker-footer { + border-top: 1px solid #eee; + border-top: var(--border); + display: none; + width: 100%; +} + +.picker-cancel, +.picker-confirm { + background-color: transparent; + border-width: 0; + cursor: pointer; + display: table-cell; + font-size: 1rem; + padding: 0.75rem 1rem; + width: 50%; +} + +.picker-cancel:focus, +.picker-cancel:hover, +.picker-confirm:focus, +.picker-confirm:hover { + background-color: #fcfcfc; + outline: none; +} + +.picker-confirm { + color: #0074d9; + color: var(--blue); +} diff --git a/luupsmap/static/js/modal.js b/luupsmap/static/js/modal.js index 61927a2..d228bd4 100644 --- a/luupsmap/static/js/modal.js +++ b/luupsmap/static/js/modal.js @@ -9,10 +9,10 @@ const showModal = (value) => { if (value) { modal.classList.add('visible'); overlay.classList.add('visible') - } - else { + } else { modal.classList.remove('visible'); overlay.classList.remove('visible') } }; + diff --git a/luupsmap/static/js/picker.de-DE.js b/luupsmap/static/js/picker.de-DE.js new file mode 100755 index 0000000..a73cd03 --- /dev/null +++ b/luupsmap/static/js/picker.de-DE.js @@ -0,0 +1,50 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : factory(global.Picker); +}(typeof self !== 'undefined' ? self : this, function (Picker) { + 'use strict'; + + Picker.languages['de-DE'] = { + format: 'DD.MM.YYYY HH:mm', + months: [ + 'Januar', + 'Februar', + 'März', + 'April', + 'Mai', + 'Juni', + 'Juli', + 'August', + 'September', + 'Oktober', + 'November', + 'Dezember' + ], + monthsShort: [ + 'Jan', + 'Feb', + 'Mrz', + 'Apr', + 'Mai', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Okt', + 'Nov', + 'Dez', + ], + text: { + title: 'Setze ein Datum oder Zeit', + cancel: 'Abbrechen', + confirm: 'OK', + year: 'Jahr', + month: 'Monat', + day: 'Tag', + hour: 'Stunde', + minute: 'Minute', + second: 'Sekunde', + millisecond: 'Millisekunden' + } + }; +})); diff --git a/luupsmap/static/js/picker.js b/luupsmap/static/js/picker.js new file mode 100755 index 0000000..58a8939 --- /dev/null +++ b/luupsmap/static/js/picker.js @@ -0,0 +1,1685 @@ +/*! + * Picker.js v1.2.1 + * https://fengyuanchen.github.io/pickerjs + * + * Copyright 2016-present Chen Fengyuan + * Released under the MIT license + * + * Date: 2019-02-18T13:08:12.801Z + */ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.Picker = factory()); +}(this, function () { 'use strict'; + + function _typeof(obj) { + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function (obj) { + return typeof obj; + }; + } else { + _typeof = function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); + } + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); + } + + function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } + } + + function _iterableToArray(iter) { + if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); + } + + function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance"); + } + + var DEFAULTS = { + // Define the container for putting the picker. + container: null, + // Indicate whether show the prev and next arrow controls on each column. + controls: false, + // The initial date. If not present, use the current date. + date: null, + // The date string format, also as the sorting order for columns. + format: 'YYYY-MM-DD HH:mm', + // Indicate whether show the column headers. + headers: false, + // Define the increment for each date / time part. + increment: 1, + // Enable inline mode. + inline: false, + // Define the language. (An ISO language code). + language: '', + // Months' name. + months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + // Shorter months' name. + monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + // Define the number of rows for showing. + rows: 5, + // Define the text of the picker. + text: { + title: 'Pick a date and time', + cancel: 'Cancel', + confirm: 'OK', + year: 'Year', + month: 'Month', + day: 'Day', + hour: 'Hour', + minute: 'Minute', + second: 'Second', + millisecond: 'Millisecond' + }, + // Translate date / time text. + translate: function translate(type, text) { + return text; + }, + // Shortcuts of custom events. + show: null, + shown: null, + hide: null, + hidden: null, + pick: null + }; + + var TEMPLATE = ''; + + var IS_BROWSER = typeof window !== 'undefined'; + var WINDOW = IS_BROWSER ? window : {}; + var IS_TOUCH_DEVICE = IS_BROWSER ? 'ontouchstart' in WINDOW.document.documentElement : false; + var HAS_POINTER_EVENT = IS_BROWSER ? 'PointerEvent' in WINDOW : false; + var NAMESPACE = 'picker'; + var LANGUAGES = {}; // Actions + + var ACTION_HIDE = 'hide'; + var ACTION_NEXT = 'next'; + var ACTION_PICK = 'pick'; + var ACTION_PREV = 'prev'; // Classes + + var CLASS_OPEN = "".concat(NAMESPACE, "-open"); + var CLASS_OPENED = "".concat(NAMESPACE, "-opened"); + var CLASS_PICKED = "".concat(NAMESPACE, "-picked"); // Data keys + // Add namespace to avoid to conflict to some other libraries. + + var DATA_ACTION = "".concat(NAMESPACE, "Action"); + var DATA_TOKEN = 'token'; + var DATA_TYPE = 'type'; + var DATA_NAME = 'name'; + var DATA_VALUE = 'value'; // Events + + var EVENT_CLICK = 'click'; + var EVENT_FOCUS = 'focus'; + var EVENT_HIDDEN = 'hidden'; + var EVENT_HIDE = 'hide'; + var EVENT_KEY_DOWN = 'keydown'; + var EVENT_PICK = 'pick'; + var EVENT_TOUCH_START = IS_TOUCH_DEVICE ? 'touchstart' : 'mousedown'; + var EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'touchmove' : 'mousemove'; + var EVENT_TOUCH_END = IS_TOUCH_DEVICE ? 'touchend touchcancel' : 'mouseup'; + var EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? 'pointerdown' : EVENT_TOUCH_START; + var EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? 'pointermove' : EVENT_TOUCH_MOVE; + var EVENT_POINTER_UP = HAS_POINTER_EVENT ? 'pointerup pointercancel' : EVENT_TOUCH_END; + var EVENT_SHOW = 'show'; + var EVENT_SHOWN = 'shown'; + var EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll'; + + var _Object$prototype = Object.prototype, + hasOwnProperty = _Object$prototype.hasOwnProperty, + toString = _Object$prototype.toString; + /** + * Detect the type of the given value. + * @param {*} value - The value to detect. + * @returns {string} Returns the type. + */ + + function typeOf(value) { + return toString.call(value).slice(8, -1).toLowerCase(); + } + /** + * Check if the given value is a string. + * @param {*} value - The value to check. + * @returns {boolean} Returns `true` if the given value is a string, else `false`. + */ + + function isString(value) { + return typeof value === 'string'; + } + /** + * Check if the given value is finite. + */ + + var isFinite = Number.isFinite || WINDOW.isFinite; + /** + * Check if the given value is not a number. + */ + + var isNaN = Number.isNaN || WINDOW.isNaN; + /** + * Check if the given value is a number. + * @param {*} value - The value to check. + * @returns {boolean} Returns `true` if the given value is a number, else `false`. + */ + + function isNumber(value) { + return typeof value === 'number' && !isNaN(value); + } + /** + * Check if the given value is an object. + * @param {*} value - The value to check. + * @returns {boolean} Returns `true` if the given value is an object, else `false`. + */ + + function isObject(value) { + return _typeof(value) === 'object' && value !== null; + } + /** + * Check if the given value is a plain object. + * @param {*} value - The value to check. + * @returns {boolean} Returns `true` if the given value is a plain object, else `false`. + */ + + function isPlainObject(value) { + if (!isObject(value)) { + return false; + } + + try { + var _constructor = value.constructor; + var prototype = _constructor.prototype; + return _constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf'); + } catch (error) { + return false; + } + } + /** + * Check if the given value is a function. + * @param {*} value - The value to check. + * @returns {boolean} Returns `true` if the given value is a function, else `false`. + */ + + function isFunction(value) { + return typeof value === 'function'; + } + /** + * Check if the given value is a date. + * @param {*} value - The value to check. + * @returns {boolean} Returns `true` if the given value is a date, else `false`. + */ + + function isDate(value) { + return typeOf(value) === 'date'; + } + /** + * Check if the given value is a valid date. + * @param {*} value - The value to check. + * @returns {boolean} Returns `true` if the given value is a valid date, else `false`. + */ + + function isValidDate(value) { + return isDate(value) && value.toString() !== 'Invalid Date'; + } + /** + * Iterate the given data. + * @param {*} data - The data to iterate. + * @param {Function} callback - The process function for each element. + * @returns {*} The original data. + */ + + function forEach(data, callback) { + if (data && isFunction(callback)) { + if (Array.isArray(data) || isNumber(data.length) + /* array-like */ + ) { + var length = data.length; + var i; + + for (i = 0; i < length; i += 1) { + if (callback.call(data, data[i], i, data) === false) { + break; + } + } + } else if (isObject(data)) { + Object.keys(data).forEach(function (key) { + callback.call(data, data[key], key, data); + }); + } + } + + return data; + } + /** + * Recursively assigns own enumerable properties of source objects to the target object. + * @param {Object} target - The target object. + * @param {Object[]} sources - The source objects. + * @returns {Object} The target object. + */ + + function deepAssign(target) { + for (var _len = arguments.length, sources = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + sources[_key - 1] = arguments[_key]; + } + + if (isObject(target) && sources.length > 0) { + sources.forEach(function (source) { + if (isObject(source)) { + Object.keys(source).forEach(function (key) { + if (isPlainObject(target[key]) && isPlainObject(source[key])) { + target[key] = deepAssign({}, target[key], source[key]); + } else { + target[key] = source[key]; + } + }); + } + }); + } + + return target; + } + /** + * Add classes to the given element. + * @param {Element} element - The target element. + * @param {string} value - The classes to be added. + */ + + function addClass(element, value) { + if (!value) { + return; + } + + if (isNumber(element.length)) { + forEach(element, function (elem) { + addClass(elem, value); + }); + return; + } + + if (element.classList) { + element.classList.add(value); + return; + } + + var className = element.className.trim(); + + if (!className) { + element.className = value; + } else if (className.indexOf(value) < 0) { + element.className = "".concat(className, " ").concat(value); + } + } + /** + * Remove classes from the given element. + * @param {Element} element - The target element. + * @param {string} value - The classes to be removed. + */ + + function removeClass(element, value) { + if (!value) { + return; + } + + if (isNumber(element.length)) { + forEach(element, function (elem) { + removeClass(elem, value); + }); + return; + } + + if (element.classList) { + element.classList.remove(value); + return; + } + + if (element.className.indexOf(value) >= 0) { + element.className = element.className.replace(value, ''); + } + } + var REGEXP_HYPHENATE = /([a-z\d])([A-Z])/g; + /** + * Transform the given string from camelCase to kebab-case + * @param {string} value - The value to transform. + * @returns {string} The transformed value. + */ + + function hyphenate(value) { + return value.replace(REGEXP_HYPHENATE, '$1-$2').toLowerCase(); + } + /** + * Get data from the given element. + * @param {Element} element - The target element. + * @param {string} name - The data key to get. + * @returns {string} The data value. + */ + + function getData(element, name) { + if (isObject(element[name])) { + return element[name]; + } + + if (element.dataset) { + return element.dataset[name]; + } + + return element.getAttribute("data-".concat(hyphenate(name))); + } + /** + * Set data to the given element. + * @param {Element} element - The target element. + * @param {string} name - The data key to set. + * @param {string} data - The data value. + */ + + function setData(element, name, data) { + if (isObject(data)) { + element[name] = data; + } else if (element.dataset) { + element.dataset[name] = data; + } else { + element.setAttribute("data-".concat(hyphenate(name)), data); + } + } + /** + * Remove data from the given element. + * @param {Element} element - The target element. + * @param {string} name - The data key to remove. + */ + + function removeData(element, name) { + if (isObject(element[name])) { + try { + delete element[name]; + } catch (error) { + element[name] = undefined; + } + } else if (element.dataset) { + // #128 Safari not allows to delete dataset property + try { + delete element.dataset[name]; + } catch (error) { + element.dataset[name] = undefined; + } + } else { + element.removeAttribute("data-".concat(hyphenate(name))); + } + } + var REGEXP_SPACES = /\s\s*/; + + var onceSupported = function () { + var supported = false; + + if (IS_BROWSER) { + var once = false; + + var listener = function listener() {}; + + var options = Object.defineProperty({}, 'once', { + get: function get() { + supported = true; + return once; + }, + + /** + * This setter can fix a `TypeError` in strict mode + * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only} + * @param {boolean} value - The value to set + */ + set: function set(value) { + once = value; + } + }); + WINDOW.addEventListener('test', listener, options); + WINDOW.removeEventListener('test', listener, options); + } + + return supported; + }(); + /** + * Remove event listener from the target element. + * @param {Element} element - The event target. + * @param {string} type - The event type(s). + * @param {Function} listener - The event listener. + * @param {Object} options - The event options. + */ + + + function removeListener(element, type, listener) { + var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + var handler = listener; + type.trim().split(REGEXP_SPACES).forEach(function (event) { + if (!onceSupported) { + var listeners = element.listeners; + + if (listeners && listeners[event] && listeners[event][listener]) { + handler = listeners[event][listener]; + delete listeners[event][listener]; + + if (Object.keys(listeners[event]).length === 0) { + delete listeners[event]; + } + + if (Object.keys(listeners).length === 0) { + delete element.listeners; + } + } + } + + element.removeEventListener(event, handler, options); + }); + } + /** + * Add event listener to the target element. + * @param {Element} element - The event target. + * @param {string} type - The event type(s). + * @param {Function} listener - The event listener. + * @param {Object} options - The event options. + */ + + function addListener(element, type, listener) { + var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + var _handler = listener; + type.trim().split(REGEXP_SPACES).forEach(function (event) { + if (options.once && !onceSupported) { + var _element$listeners = element.listeners, + listeners = _element$listeners === void 0 ? {} : _element$listeners; + + _handler = function handler() { + delete listeners[event][listener]; + element.removeEventListener(event, _handler, options); + + for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + listener.apply(element, args); + }; + + if (!listeners[event]) { + listeners[event] = {}; + } + + if (listeners[event][listener]) { + element.removeEventListener(event, listeners[event][listener], options); + } + + listeners[event][listener] = _handler; + element.listeners = listeners; + } + + element.addEventListener(event, _handler, options); + }); + } + /** + * Dispatch event on the target element. + * @param {Element} element - The event target. + * @param {string} type - The event type(s). + * @param {Object} data - The additional event data. + * @returns {boolean} Indicate if the event is default prevented or not. + */ + + function dispatchEvent(element, type, data) { + var event; // Event and CustomEvent on IE9-11 are global objects, not constructors + + if (isFunction(Event) && isFunction(CustomEvent)) { + event = new CustomEvent(type, { + detail: data, + bubbles: true, + cancelable: true + }); + } else { + event = document.createEvent('CustomEvent'); + event.initCustomEvent(type, true, true, data); + } + + return element.dispatchEvent(event); + } + /** + * Check if the given year is a leap year. + * @param {number} year - The year to check. + * @returns {boolean} Returns `true` if the given year is a leap year, else `false`. + */ + + function isLeapYear(year) { + return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0; + } + /** + * Get days number of the given month. + * @param {number} year - The target year. + * @param {number} month - The target month. + * @returns {number} Returns days number. + */ + + function getDaysInMonth(year, month) { + return [31, isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; + } + /** + * Add leading zeroes to the given value + * @param {number} value - The value to add. + * @param {number} [length=1] - The number of the leading zeroes. + * @returns {string} Returns converted value. + */ + + function addLeadingZero(value) { + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; + var str = String(Math.abs(value)); + var i = str.length; + var result = ''; + + if (value < 0) { + result += '-'; + } + + while (i < length) { + i += 1; + result += '0'; + } + + return result + str; + } + /** + * Map token to type name + * @param {string} token - The token to map. + * @returns {string} Returns mapped type name. + */ + + function tokenToType(token) { + return { + Y: 'year', + M: 'month', + D: 'day', + H: 'hour', + m: 'minute', + s: 'second', + S: 'millisecond' + }[token.charAt(0)]; + } + var REGEXP_TOKENS = /(Y|M|D|H|m|s|S)\1*/g; + /** + * Parse date format. + * @param {string} format - The format to parse. + * @returns {Object} Returns parsed format data. + */ + + function parseFormat(format) { + var tokens = format.match(REGEXP_TOKENS); + + if (!tokens) { + throw new Error('Invalid format.'); + } // Remove duplicate tokens (#22) + + + tokens = tokens.filter(function (token, index) { + return tokens.indexOf(token) === index; + }); + var result = { + tokens: tokens + }; + tokens.forEach(function (token) { + result[tokenToType(token)] = token; + }); + return result; + } + + var events = { + bind: function bind() { + var element = this.element, + options = this.options, + grid = this.grid; + + if (isFunction(options.show)) { + addListener(element, EVENT_SHOW, options.show); + } + + if (isFunction(options.shown)) { + addListener(element, EVENT_SHOWN, options.shown); + } + + if (isFunction(options.hide)) { + addListener(element, EVENT_HIDE, options.hide); + } + + if (isFunction(options.hidden)) { + addListener(element, EVENT_HIDDEN, options.hidden); + } + + if (isFunction(options.pick)) { + addListener(element, EVENT_PICK, options.pick); + } + + addListener(element, EVENT_FOCUS, this.onFocus = this.focus.bind(this)); + addListener(element, EVENT_CLICK, this.onFocus); + addListener(this.picker, EVENT_CLICK, this.onClick = this.click.bind(this)); + addListener(grid, EVENT_WHEEL, this.onWheel = this.wheel.bind(this)); + addListener(grid, EVENT_POINTER_DOWN, this.onPointerDown = this.pointerdown.bind(this)); + addListener(document, EVENT_POINTER_MOVE, this.onPointerMove = this.pointermove.bind(this)); + addListener(document, EVENT_POINTER_UP, this.onPointerUp = this.pointerup.bind(this)); + addListener(document, EVENT_KEY_DOWN, this.onKeyDown = this.keydown.bind(this)); + }, + unbind: function unbind() { + var element = this.element, + options = this.options, + grid = this.grid; + + if (isFunction(options.show)) { + removeListener(element, EVENT_SHOW, options.show); + } + + if (isFunction(options.shown)) { + removeListener(element, EVENT_SHOWN, options.shown); + } + + if (isFunction(options.hide)) { + removeListener(element, EVENT_HIDE, options.hide); + } + + if (isFunction(options.hidden)) { + removeListener(element, EVENT_HIDDEN, options.hidden); + } + + if (isFunction(options.pick)) { + removeListener(element, EVENT_PICK, options.pick); + } + + removeListener(element, EVENT_FOCUS, this.onFocus); + removeListener(element, EVENT_CLICK, this.onFocus); + removeListener(this.picker, EVENT_CLICK, this.onClick); + removeListener(grid, EVENT_WHEEL, this.onWheel); + removeListener(grid, EVENT_POINTER_DOWN, this.onPointerDown); + removeListener(document, EVENT_POINTER_MOVE, this.onPointerMove); + removeListener(document, EVENT_POINTER_UP, this.onPointerUp); + removeListener(document, EVENT_KEY_DOWN, this.onKeyDown); + } + }; + + var handlers = { + focus: function focus(event) { + event.target.blur(); + this.show(); + }, + click: function click(event) { + var target = event.target; + var action = getData(target, DATA_ACTION); + + switch (action) { + case ACTION_HIDE: + this.hide(); + break; + + case ACTION_PICK: + this.pick(); + break; + + case ACTION_PREV: + case ACTION_NEXT: + this[action](getData(target.parentElement, DATA_TYPE)); + break; + + default: + } + }, + wheel: function wheel(event) { + var target = event.target; + + if (target === this.grid) { + return; + } + + event.preventDefault(); + + while (target.parentElement && target.parentElement !== this.grid) { + target = target.parentElement; + } + + var type = getData(target, DATA_TYPE); + + if (event.deltaY < 0) { + this.prev(type); + } else { + this.next(type); + } + }, + pointerdown: function pointerdown(event) { + var target = event.target; + + if (target === this.grid || getData(target, DATA_ACTION)) { + return; + } // This line is required for preventing page scrolling in iOS browsers + + + event.preventDefault(); + + while (target.parentElement && target.parentElement !== this.grid) { + target = target.parentElement; + } + + var list = target.querySelector(".".concat(NAMESPACE, "-list")); + var itemHeight = list.firstElementChild.offsetHeight; + this.cell = { + elem: target, + list: list, + moveY: 0, + maxMoveY: itemHeight, + minMoveY: itemHeight / 2, + startY: event.changedTouches ? event.changedTouches[0].pageY : event.pageY, + type: getData(target, DATA_TYPE) + }; + }, + pointermove: function pointermove(event) { + var cell = this.cell; + + if (!cell) { + return; + } + + event.preventDefault(); + var endY = event.changedTouches ? event.changedTouches[0].pageY : event.pageY; + var moveY = cell.moveY + (endY - cell.startY); + cell.startY = endY; + cell.moveY = moveY; + + if (Math.abs(moveY) < cell.maxMoveY) { + cell.list.style.top = "".concat(moveY, "px"); + return; + } + + cell.list.style.top = 0; + cell.moveY = 0; + + if (moveY >= cell.maxMoveY) { + this.prev(cell.type); + } else if (moveY <= -cell.maxMoveY) { + this.next(cell.type); + } + }, + pointerup: function pointerup(event) { + var cell = this.cell; + + if (!cell) { + return; + } + + event.preventDefault(); + cell.list.style.top = 0; + + if (cell.moveY >= cell.minMoveY) { + this.prev(cell.type); + } else if (cell.moveY <= -cell.minMoveY) { + this.next(cell.type); + } + + this.cell = null; + }, + keydown: function keydown(event) { + if (this.shown && (event.key === 'Escape' || event.keyCode === 27)) { + this.hide(); + } + } + }; + + var helpers = { + render: function render(type) { + var _this = this; + + if (!type) { + this.format.tokens.forEach(function (token) { + return _this.render(tokenToType(token)); + }); + return; + } + + var options = this.options; + var data = this.data[type]; + var current = this.current(type); + var max = isFunction(data.max) ? data.max() : data.max; + var min = isFunction(data.min) ? data.min() : data.min; + var base = 0; + + if (isFinite(max)) { + base = min > 0 ? max : max + 1; + } + + data.list.innerHTML = ''; + data.current = current; + + for (var i = 0; i < options.rows + 2; i += 1) { + var item = document.createElement('li'); + var position = i - data.index; + var newValue = current + position * data.increment; + + if (base) { + newValue %= base; + + if (newValue < min) { + newValue += base; + } + } + + item.textContent = options.translate(type, data.aliases ? data.aliases[newValue] : addLeadingZero(newValue + data.offset, data.digit)); + setData(item, DATA_NAME, type); + setData(item, DATA_VALUE, newValue); + addClass(item, "".concat(NAMESPACE, "-item")); + + if (position === 0) { + addClass(item, CLASS_PICKED); + data.item = item; + } + + data.list.appendChild(item); + } + }, + current: function current(type, value) { + var date = this.date; + var format = this.format; + var token = format[type]; + + switch (token.charAt(0)) { + case 'Y': + if (isNumber(value)) { + date.setFullYear(token.length === 2 ? 2000 + value : value); + + if (format.month) { + this.render(tokenToType(format.month)); + } + + if (format.day) { + this.render(tokenToType(format.day)); + } + } + + return date.getFullYear(); + + case 'M': + if (isNumber(value)) { + date.setMonth(value, // The current day should not exceed its maximum day in current month + Math.min(date.getDate(), getDaysInMonth(date.getFullYear(), value))); + + if (format.day) { + this.render(tokenToType(format.day)); + } + } + + return date.getMonth(); + + case 'D': + if (isNumber(value)) { + date.setDate(value); + } + + return date.getDate(); + + case 'H': + if (isNumber(value)) { + date.setHours(value); + } + + return date.getHours(); + + case 'm': + if (isNumber(value)) { + date.setMinutes(value); + } + + return date.getMinutes(); + + case 's': + if (isNumber(value)) { + date.setSeconds(value); + } + + return date.getSeconds(); + + case 'S': + if (isNumber(value)) { + date.setMilliseconds(value); + } + + return date.getMilliseconds(); + + default: + } + + return date; + }, + getValue: function getValue() { + var element = this.element; + return this.isInput ? element.value : element.textContent; + }, + setValue: function setValue(value) { + var element = this.element; + + if (this.isInput) { + element.value = value; + } else if (this.options.container) { + element.textContent = value; + } + }, + open: function open() { + var body = this.body; + body.style.overflow = 'hidden'; + body.style.paddingRight = "".concat(this.scrollBarWidth + (parseFloat(this.initialBodyPaddingRight) || 0), "px"); + }, + close: function close() { + var body = this.body; + body.style.overflow = ''; + body.style.paddingRight = this.initialBodyPaddingRight; + } + }; + + var methods = { + /** + * Show the picker. + * @param {boolean} [immediate=false] - Indicate if show the picker immediately or not. + * @returns {Picker} this + */ + show: function show() { + var immediate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + var element = this.element, + picker = this.picker; + + if (this.inline || this.shown) { + return this; + } + + if (dispatchEvent(element, EVENT_SHOW) === false) { + return this; + } + + this.shown = true; + this.open(); + addClass(picker, CLASS_OPEN); + + var done = function done() { + dispatchEvent(element, EVENT_SHOWN); + }; + + if (!immediate) { + // Reflow to enable transition + // eslint-disable-next-line + picker.offsetWidth; + } + + addClass(picker, CLASS_OPENED); + + if (immediate) { + done(); + } else { + setTimeout(done, 300); + } + + return this; + }, + + /** + * Hide the picker. + * @param {boolean} [immediate=false] - Indicate if hide the picker immediately or not. + * @returns {Picker} this + */ + hide: function hide() { + var _this = this; + + var immediate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + var element = this.element, + picker = this.picker; + + if (this.inline || !this.shown) { + return this; + } + + if (dispatchEvent(element, EVENT_HIDE) === false) { + return this; + } + + this.shown = false; + removeClass(picker, CLASS_OPENED); + + var done = function done() { + _this.close(); + + removeClass(picker, CLASS_OPEN); + dispatchEvent(element, EVENT_HIDDEN); + }; + + if (immediate) { + done(); + } else { + setTimeout(done, 300); + } + + return this; + }, + + /** + * Pick to the previous item. + * @param {string} type - The column type. + * @returns {Picker} this + */ + prev: function prev(type) { + var options = this.options; + var token = this.format[type]; + var data = this.data[type]; + var list = data.list; + var item = list.lastElementChild; + var max = isFunction(data.max) ? data.max() : data.max; + var min = isFunction(data.min) ? data.min() : data.min; + var prev = data.item.previousElementSibling; + var value = Number(getData(list.firstElementChild, DATA_VALUE)) - data.increment; + + if (value < min) { + value += max - min + 1; + } + + item.textContent = options.translate(type, data.aliases ? data.aliases[value] : addLeadingZero(value + data.offset, token.length)); + setData(item, DATA_VALUE, value); + + if (prev) { + removeClass(data.item, CLASS_PICKED); + addClass(prev, CLASS_PICKED); + data.item = prev; + } + + list.insertBefore(item, list.firstElementChild); + data.current = Number(getData(data.item, DATA_VALUE)); + this.current(type, data.current); + + if (this.inline && options.container) { + this.pick(); + } + + return this; + }, + + /** + * Pick to the next item. + * @param {String} type - The column type. + * @returns {Picker} this + */ + next: function next(type) { + var options = this.options; + var token = this.format[type]; + var data = this.data[type]; + var list = data.list; + var item = list.firstElementChild; + var max = isFunction(data.max) ? data.max() : data.max; + var min = isFunction(data.min) ? data.min() : data.min; + var next = data.item.nextElementSibling; + var value = Number(getData(list.lastElementChild, DATA_VALUE)) + data.increment; + + if (value > max) { + value -= max - min + 1; + } + + item.textContent = options.translate(type, data.aliases ? data.aliases[value] : addLeadingZero(value + data.offset, token.length)); + setData(item, DATA_VALUE, value); + list.appendChild(item); + + if (next) { + removeClass(data.item, CLASS_PICKED); + addClass(next, CLASS_PICKED); + data.item = next; + } + + data.current = Number(getData(data.item, DATA_VALUE)); + this.current(type, data.current); + + if (this.inline && options.container) { + this.pick(); + } + + return this; + }, + // Pick the current date to the target element. + pick: function pick() { + var element = this.element; + + if (dispatchEvent(element, EVENT_PICK) === false) { + return this; + } + + var value = this.formatDate(this.date); + this.setValue(value); + + if (this.isInput && dispatchEvent(element, 'change') === false) { + this.reset(); + } + + this.hide(); + return this; + }, + + /** + * Get the current date. + * @param {boolean} [formatted=false] - Indicate if format the date or not. + * @return {Date|string} The output date. + */ + getDate: function getDate() { + var formatted = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + var date = this.date; + return formatted ? this.formatDate(date) : new Date(date); + }, + + /** + * Override the current date with a new date. + * @param {Date|string} date - The date to set. + * @returns {Picker} this + */ + setDate: function setDate(date) { + if (date) { + this.date = this.parseDate(date); + this.render(); + } + + return this; + }, + // Update the picker with the current element value / text. + update: function update() { + this.date = this.parseDate(this.getValue()); + this.render(); + return this; + }, + // Reset the picker and element value / text. + reset: function reset() { + this.setValue(this.initialValue); + this.date = new Date(this.initialDate); + this.render(); + return this; + }, + + /** + * Parse a date with the set date format. + * @param {Date|string} date - The date to parse. + * @returns {Date} The parsed date object. + */ + parseDate: function parseDate(date) { + var options = this.options, + format = this.format; + var digits = []; + + if (isDate(date)) { + return new Date(date); + } + + if (isString(date)) { + var groups = [].concat(_toConsumableArray(options.months), _toConsumableArray(options.monthsShort), ['\\d+']); + digits = date.match(new RegExp("(".concat(groups.join('|'), ")"), 'g')); // Parse `11111111` (YYYYMMDD) to ['1111', '11', '11'] + + if (digits && date.length === options.format.length && digits.length !== format.tokens.length) { + digits = format.tokens.map(function (token) { + return date.substr(options.format.indexOf(token), token.length); + }); + } + + if (!digits || digits.length !== format.tokens.length) { + return new Date(); + } + } + + var parsedDate = new Date(); + digits.forEach(function (digit, i) { + var token = format.tokens[i]; + var n = Number(digit); + + switch (token) { + case 'YYYY': + case 'YYY': + case 'Y': + { + var index = date.indexOf(digit); + var isHyphen = date.substr(index - 1, 1) === '-'; + var isBC = index > 1 && isHyphen && /\S/.test(date.substr(index - 2, 1)) || index === 1 && isHyphen; + parsedDate.setFullYear(isBC ? -n : n); + break; + } + + case 'YY': + parsedDate.setFullYear(2000 + n); + break; + + case 'MMMM': + parsedDate.setMonth(options.months.indexOf(digit)); + break; + + case 'MMM': + parsedDate.setMonth(options.monthsShort.indexOf(digit)); + break; + + case 'MM': + case 'M': + parsedDate.setMonth(n - 1); + break; + + case 'DD': + case 'D': + parsedDate.setDate(n); + break; + + case 'HH': + case 'H': + parsedDate.setHours(n); + break; + + case 'mm': + case 'm': + parsedDate.setMinutes(n); + break; + + case 'ss': + case 's': + parsedDate.setSeconds(n); + break; + + case 'SSS': + case 'SS': + case 'S': + parsedDate.setMilliseconds(n); + break; + + default: + } + }); + return parsedDate; + }, + + /** + * Format a date object to a string with the set date format. + * @param {Date} date - The date to format. + * @return {string} THe formatted date. + */ + formatDate: function formatDate(date) { + var options = this.options, + format = this.format; + var formatted = ''; + + if (isValidDate(date)) { + var year = date.getFullYear(); + var month = date.getMonth(); + var day = date.getDate(); + var hours = date.getHours(); + var minutes = date.getMinutes(); + var seconds = date.getSeconds(); + var milliseconds = date.getMilliseconds(); + formatted = options.format; + format.tokens.forEach(function (token) { + var replacement = ''; + + switch (token) { + case 'YYYY': + case 'YYY': + case 'Y': + replacement = addLeadingZero(year, token.length); + break; + + case 'YY': + replacement = addLeadingZero(year % 100, 2); + break; + + case 'MMMM': + replacement = options.months[month]; + break; + + case 'MMM': + replacement = options.monthsShort[month]; + break; + + case 'MM': + case 'M': + replacement = addLeadingZero(month + 1, token.length); + break; + + case 'DD': + case 'D': + replacement = addLeadingZero(day, token.length); + break; + + case 'HH': + case 'H': + replacement = addLeadingZero(hours, token.length); + break; + + case 'mm': + case 'm': + replacement = addLeadingZero(minutes, token.length); + break; + + case 'ss': + case 's': + replacement = addLeadingZero(seconds, token.length); + break; + + case 'SSS': + case 'SS': + case 'S': + replacement = addLeadingZero(milliseconds, token.length); + break; + + default: + } + + formatted = formatted.replace(token, replacement); + }); + } + + return formatted; + }, + // Destroy the picker and remove the instance from the target element. + destroy: function destroy() { + var element = this.element, + picker = this.picker; + + if (!getData(element, NAMESPACE)) { + return this; + } + + this.hide(true); + this.unbind(); + removeData(element, NAMESPACE); + picker.parentNode.removeChild(picker); + return this; + } + }; + + var REGEXP_DELIMITER = /\{\{\s*(\w+)\s*\}\}/g; + var REGEXP_INPUTS = /input|textarea/i; + var AnotherPicker = WINDOW.Picker; + + var Picker = + /*#__PURE__*/ + function () { + /** + * Create a new Picker. + * @param {Element} element - The target element for picking. + * @param {Object} [options={}] - The configuration options. + */ + function Picker(element) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + _classCallCheck(this, Picker); + + if (!element || element.nodeType !== 1) { + throw new Error('The first argument is required and must be an element.'); + } + + this.element = element; + this.options = deepAssign({}, DEFAULTS, LANGUAGES[options.language], isPlainObject(options) && options); + this.shown = false; + this.init(); + } + + _createClass(Picker, [{ + key: "init", + value: function init() { + var _this = this; + + var element = this.element; + + if (getData(element, NAMESPACE)) { + return; + } + + setData(element, NAMESPACE, this); + var options = this.options; + var isInput = REGEXP_INPUTS.test(element.tagName); + var inline = options.inline && (options.container || !isInput); + var template = document.createElement('div'); + template.insertAdjacentHTML('afterbegin', TEMPLATE.replace(REGEXP_DELIMITER, function () { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return options.text[args[1]]; + })); + var picker = template.getElementsByClassName(NAMESPACE)[0]; + var grid = picker.getElementsByClassName("".concat(NAMESPACE, "-grid"))[0]; + var container = options.container; + + if (isString(container)) { + container = document.querySelector(container); + } + + if (inline) { + addClass(picker, CLASS_OPEN); + addClass(picker, CLASS_OPENED); + + if (!container) { + container = element; + } + } else { + var ownerDocument = element.ownerDocument; + var body = ownerDocument.body || ownerDocument.documentElement; + this.body = body; + this.scrollBarWidth = WINDOW.innerWidth - ownerDocument.documentElement.clientWidth; + this.initialBodyPaddingRight = WINDOW.getComputedStyle(body).paddingRight; + addClass(picker, "".concat(NAMESPACE, "-fixed")); + + if (!container) { + container = document.body; + } + } + + this.isInput = isInput; + this.inline = inline; + this.container = container; + this.picker = picker; + this.grid = grid; + this.cell = null; + this.format = parseFormat(options.format); + var initialValue = this.getValue(); + var date = this.parseDate(options.date || initialValue); + this.date = date; + this.initialDate = new Date(date); + this.initialValue = initialValue; + this.data = {}; + var rows = Number(options.rows); + + if (!(rows % 2)) { + rows += 1; + } + + options.rows = rows || 5; + addClass(grid, "".concat(NAMESPACE, "-").concat(options.rows > 1 ? 'multiple' : 'single')); + + if (options.controls) { + addClass(grid, "".concat(NAMESPACE, "-controls")); + } + + var headers = options.headers, + increment = options.increment; + + if (headers) { + addClass(grid, "".concat(NAMESPACE, "-headers")); // TODO: Drop the `headers` option's object support in v2. + + headers = isPlainObject(headers) ? headers : options.text; + } + + if (!isPlainObject(increment)) { + increment = { + year: increment, + month: increment, + day: increment, + hour: increment, + minute: increment, + second: increment, + millisecond: increment + }; + } + + this.format.tokens.forEach(function (token) { + var type = tokenToType(token); + var cell = document.createElement('div'); + var cellBody = document.createElement('div'); + var list = document.createElement('ul'); + var data = { + digit: token.length, + increment: Math.abs(Number(increment[type])) || 1, + list: list, + max: Infinity, + min: -Infinity, + index: Math.floor((options.rows + 2) / 2), + offset: 0 + }; + + switch (token.charAt(0)) { + case 'Y': + if (data.digit === 2) { + data.max = 99; + data.min = 0; + } + + break; + + case 'M': + data.max = 11; + data.min = 0; + data.offset = 1; + + if (data.digit === 3) { + data.aliases = options.monthsShort; + } else if (data.digit === 4) { + data.aliases = options.months; + } + + break; + + case 'D': + // XXX: Use the latest date to calculate the max day (#23) + data.max = function () { + return getDaysInMonth(_this.date.getFullYear(), _this.date.getMonth()); + }; + + data.min = 1; + break; + + case 'H': + data.max = 23; + data.min = 0; + break; + + case 'm': + data.max = 59; + data.min = 0; + break; + + case 's': + data.max = 59; + data.min = 0; + break; + + case 'S': + data.max = 999; + data.min = 0; + break; + + default: + } + + setData(cell, DATA_TYPE, type); + setData(cell, DATA_TOKEN, token); + + if (headers) { + var cellHeader = document.createElement('div'); + addClass(cellHeader, "".concat(NAMESPACE, "-cell__header")); + cellHeader.textContent = headers[type] || type[0].toUpperCase() + type.substr(1); + cell.appendChild(cellHeader); + } + + if (options.controls) { + var prev = document.createElement('div'); + addClass(prev, "".concat(NAMESPACE, "-cell__control")); + addClass(prev, "".concat(NAMESPACE, "-cell__control--prev")); + setData(prev, DATA_ACTION, ACTION_PREV); + cell.appendChild(prev); + } + + addClass(list, "".concat(NAMESPACE, "-list")); + addClass(cellBody, "".concat(NAMESPACE, "-cell__body")); + addClass(cell, "".concat(NAMESPACE, "-cell")); + addClass(cell, "".concat(NAMESPACE, "-").concat(type, "s")); + cellBody.appendChild(list); + cell.appendChild(cellBody); + + if (options.controls) { + var next = document.createElement('div'); + addClass(next, "".concat(NAMESPACE, "-cell__control")); + addClass(next, "".concat(NAMESPACE, "-cell__control--next")); + setData(next, DATA_ACTION, ACTION_NEXT); + cell.appendChild(next); + } + + grid.appendChild(cell); + _this.data[type] = data; + + _this.render(type); + }); + + if (inline) { + container.innerHTML = ''; + } + + container.appendChild(picker); + this.bind(); + } + /** + * Get the no conflict picker class. + * @returns {Picker} The picker class. + */ + + }], [{ + key: "noConflict", + value: function noConflict() { + WINDOW.Picker = AnotherPicker; + return Picker; + } + /** + * Change the default options. + * @param {Object} options - The new default options. + */ + + }, { + key: "setDefaults", + value: function setDefaults(options) { + deepAssign(DEFAULTS, LANGUAGES[options.language], isPlainObject(options) && options); + } + }]); + + return Picker; + }(); + + deepAssign(Picker.prototype, events, handlers, helpers, methods); + Picker.languages = LANGUAGES; + + return Picker; + +})); diff --git a/luupsmap/templates/details.html b/luupsmap/templates/details.html index d703a49..18c3ea8 100644 --- a/luupsmap/templates/details.html +++ b/luupsmap/templates/details.html @@ -35,10 +35,22 @@

{{ venue.name }}

{{ voucher.description }}
{{ voucher.limitations }} - {% endfor %}

{{ venue.description }}

+ + {% if location.opening_hours.entries %} +

Öffnungszeiten

+ {% for entry in location.opening_hours.entries %} +
+
{{ entry['days'] }}
+
{{ entry['hours'] }}
+ {% if entry['months'] %} +
{{ entry['months'] }}
+ {% endif%} +
+ {% endfor %} + {% endif %} {# TODO: Fix dots when no phone or email exist#} {% set homepages = venue.homepage.replace(' ', '').split(',') %} diff --git a/luupsmap/templates/index.html b/luupsmap/templates/index.html index 1b8a556..4dffce5 100644 --- a/luupsmap/templates/index.html +++ b/luupsmap/templates/index.html @@ -7,10 +7,13 @@ + + + LUUPSMAP - Die Wienkarte diff --git a/luupsmap/templates/main.html b/luupsmap/templates/main.html index 4720bef..81da62f 100644 --- a/luupsmap/templates/main.html +++ b/luupsmap/templates/main.html @@ -27,13 +27,21 @@

Gutscheinart

Tags

{% for tag in tags %} - {% set tag_name = tag[0] %} - {% set tag_pretty = tag[1] %} + {% set tag_name = tag[0] %} + {% set tag_pretty = tag[1] %} - + {% endfor %}
+

Zeit

+
+ + +
+
+