From 4bf46da31afec77758402aa1fe1373f5f1df8d50 Mon Sep 17 00:00:00 2001 From: hschne Date: Sat, 6 Jul 2019 11:14:20 +0200 Subject: [PATCH 01/21] Readme fixes and clean up --- README.md | 2 + data/locations_old.csv | 115 ----------------------------------------- data/venues_old.csv | 108 -------------------------------------- 3 files changed, 2 insertions(+), 223 deletions(-) delete mode 100644 data/locations_old.csv delete mode 100644 data/venues_old.csv diff --git a/README.md b/README.md index 2d976c9..31fcc18 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 = 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." From 6045d5cfd1cffd1f9cbf6751303a476bc012b622 Mon Sep 17 00:00:00 2001 From: hschne Date: Sat, 6 Jul 2019 13:09:40 +0200 Subject: [PATCH 02/21] Add initial interval parser --- luupsmap/cli/util/opening_hours_parser.py | 61 +++++++++++++++++++++++ luupsmap/model/interval.py | 11 +--- requirements.txt | 1 + tests/__init__.py | 0 tests/interval_test.py | 41 +++++++++++++++ 5 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 luupsmap/cli/util/opening_hours_parser.py create mode 100644 tests/__init__.py create mode 100644 tests/interval_test.py diff --git a/luupsmap/cli/util/opening_hours_parser.py b/luupsmap/cli/util/opening_hours_parser.py new file mode 100644 index 0000000..0864329 --- /dev/null +++ b/luupsmap/cli/util/opening_hours_parser.py @@ -0,0 +1,61 @@ +import calendar +import time + +from luupsmap import Interval + + +class OpeningHoursParser: + 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': 8}) + self.parsed = [] + + def parse(self, string): + blocks = [x.strip() for x in string.split(',')] + for block in blocks: + parts = [x.strip() for x in block.split(' ')] + self.parsed.append(self.__parse_parts(parts)) + return self.parsed + + def __parse_parts(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 + + hours = parts[1] + start_hour, end_hour = self.__parse_hours(hours) + + start_month = 1 + end_month = 12 + + # TODO: Add variable months, e.g. if opening hours vary during the year + + 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, + + } + return Interval(arguments) + + def __parse_hours(self, hours): + # TODO: Consider hours spilling over to next day + 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 __try_parse_hour(self, hour): + try: + start_hour = time.strptime(hour, "%H") + except ValueError: + start_hour = time.strptime(hour, "%H:%M") + return start_hour diff --git a/luupsmap/model/interval.py b/luupsmap/model/interval.py index cefa39f..021cadf 100644 --- a/luupsmap/model/interval.py +++ b/luupsmap/model/interval.py @@ -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/requirements.txt b/requirements.txt index 5389446..1aa0bc2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ gunicorn==19.9.0 psycopg2==2.7.7 python-dotenv==0.10.1 SQLAlchemy==1.2.17 +pytest==5.0.1 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/interval_test.py b/tests/interval_test.py new file mode 100644 index 0000000..ed141b6 --- /dev/null +++ b/tests/interval_test.py @@ -0,0 +1,41 @@ +import pytest + +from luupsmap.cli.util.opening_hours_parser import OpeningHoursParser + + +@pytest.fixture() +def parser(): + return OpeningHoursParser() + + +def test_single_entry_with_simple_hours_should_parse(parser): + interval = parser.parse("Mo-Fr 10-12")[0] + + assert interval.start_day == 0 + assert interval.end_day == 4 + assert interval.start_hour == "10:00" + assert interval.end_hour == "12:00" + + +def test_single_entry_with_single_day_should_parse(parser): + interval = parser.parse("Tu 0-0")[0] + + assert interval.start_day == 1 + assert interval.end_day == 1 + assert interval.start_hour == "10:00" + assert interval.end_hour == "12:00" + + +def test_single_entry_with_detailed_hours_should_parse(parser): + interval = parser.parse("Mo-Fr 10:30-12:45")[0] + + assert interval.start_day == 0 + assert interval.end_day == 4 + assert interval.start_hour == "10:30" + assert interval.end_hour == "12:45" + + +def test_multiple_entries_should_parse(parser): + interval = parser.parse("Mo-Fr 10-12, Sa-Su 7-9") + + assert len(interval) == 2 From 9593b3f95ca623538e07870285f1c9db4d9d97d4 Mon Sep 17 00:00:00 2001 From: hschne Date: Sat, 6 Jul 2019 17:03:57 +0200 Subject: [PATCH 03/21] Start implementing import --- luupsmap/cli/commands/seed.py | 40 +++++++----- ...ing_hours_parser.py => interval_parser.py} | 61 +++++++++++++------ luupsmap/cli/util/place_cache.py | 1 + luupsmap/model/interval.py | 2 +- luupsmap/model/location.py | 2 +- tests/cli/util/interval_parser_test.py | 59 ++++++++++++++++++ tests/interval_test.py | 41 ------------- 7 files changed, 131 insertions(+), 75 deletions(-) rename luupsmap/cli/util/{opening_hours_parser.py => interval_parser.py} (50%) create mode 100644 tests/cli/util/interval_parser_test.py delete mode 100644 tests/interval_test.py diff --git a/luupsmap/cli/commands/seed.py b/luupsmap/cli/commands/seed.py index 3abe3a5..f9497ed 100644 --- a/luupsmap/cli/commands/seed.py +++ b/luupsmap/cli/commands/seed.py @@ -1,3 +1,4 @@ +from cli.util.interval_parser import IntervalParser from luupsmap import db from luupsmap.cli.util import CsvFile from luupsmap.model import Venue, Location, Voucher, VoucherType, VoucherTag, Type, Tag @@ -18,39 +19,42 @@ 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) + for location in locations: + self.__create__intervals(location, intervals) + self.__create_vouchers(venue, vouchers) db.session.add_all(venues) db.session.add_all(locations) db.session.add_all(vouchers) db.session.commit() - def _create_locations(self, venue, locations): + def __create_locations(self, venue, locations): name = venue.name if name not in self.locations: return @@ -60,20 +64,28 @@ def _create_locations(self, venue, locations): venue.locations.append(location) locations.append(location) - def _create_vouchers(self, venue, vouchers): + def __create__intervals(self, location, intervals): + string = location['opening_hours'] + new_intervals = IntervalParser().parse(string) + for interval in new_intervals: + interval.id_location = location.id + location.opening_hours.append(new_intervals) + intervals.append(new_intervals) + + 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 +93,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/util/opening_hours_parser.py b/luupsmap/cli/util/interval_parser.py similarity index 50% rename from luupsmap/cli/util/opening_hours_parser.py rename to luupsmap/cli/util/interval_parser.py index 0864329..15a31e2 100644 --- a/luupsmap/cli/util/opening_hours_parser.py +++ b/luupsmap/cli/util/interval_parser.py @@ -4,7 +4,7 @@ from luupsmap import Interval -class OpeningHoursParser: +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)} @@ -17,23 +17,16 @@ def parse(self, string): blocks = [x.strip() for x in string.split(',')] for block in blocks: parts = [x.strip() for x in block.split(' ')] - self.parsed.append(self.__parse_parts(parts)) + self.__parse_parts(parts) return self.parsed def __parse_parts(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 + start_day, end_day = self.__parse_days(parts) - hours = parts[1] - start_hour, end_hour = self.__parse_hours(hours) - - start_month = 1 - end_month = 12 + start_hour, end_hour = self.__parse_hours(parts) # TODO: Add variable months, e.g. if opening hours vary during the year + start_month, end_month = self.__parse_months(parts) arguments = { 'start_day': start_day, @@ -42,18 +35,50 @@ def __parse_parts(self, parts): 'end_hour': end_hour, 'start_month': start_month, 'end_month': end_month, - } - return Interval(arguments) - def __parse_hours(self, hours): - # TODO: Consider hours spilling over to next day + if start_hour > end_hour: + self.__split_hours(arguments) + # Split into two args in case we have opening hours like Mo 22:00 - 04:00. This should result in two + # intervals, one being Mo 22:00-00:00 and one being Tu 00:00-04:00. + else: + self.parsed.append(Interval(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_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]] + return start_month, end_month + + def __split_hours(self, arguments): + additional_args = arguments.copy() + arguments['end_hour'] = '24:00' + self.parsed.append(Interval(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(Interval(additional_args)) + + 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] + return start_hour, end_hour - def __try_parse_hour(self, hour): + @staticmethod + def __try_parse_hour(hour): try: start_hour = time.strptime(hour, "%H") except ValueError: 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/model/interval.py b/luupsmap/model/interval.py index 021cadf..231f388 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('venue.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) diff --git a/luupsmap/model/location.py b/luupsmap/model/location.py index f46a1db..9771b40 100644 --- a/luupsmap/model/location.py +++ b/luupsmap/model/location.py @@ -12,7 +12,7 @@ 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) diff --git a/tests/cli/util/interval_parser_test.py b/tests/cli/util/interval_parser_test.py new file mode 100644 index 0000000..e646ed0 --- /dev/null +++ b/tests/cli/util/interval_parser_test.py @@ -0,0 +1,59 @@ +import pytest + +from luupsmap.cli.util.interval_parser import IntervalParser + + +@pytest.fixture() +def parser(): + return IntervalParser() + + +def test_single_entry_with_simple_hours_should_parse(parser): + interval = parser.parse('Mo-Fr 10-12')[0] + + assert interval.start_day == 0 + assert interval.end_day == 4 + assert interval.start_hour == '10:00' + assert interval.end_hour == '12:00' + + +def test_single_entry_with_single_day_should_parse(parser): + interval = parser.parse('Tu 10-12')[0] + + assert interval.start_day == 1 + assert interval.end_day == 1 + assert interval.start_hour == '10:00' + assert interval.end_hour == '12:00' + + +def test_single_entry_with_detailed_hours_should_parse(parser): + interval = parser.parse('Mo-Fr 10:30-12:45')[0] + + assert interval.start_day == 0 + assert interval.end_day == 4 + assert interval.start_hour == '10:30' + assert interval.end_hour == '12:45' + + +def test_roll_over_entries_should_parse(parser): + interval = parser.parse('Mo-Fr 21-04') + + assert len(interval) == 2 + first, second = interval[0], interval[1] + assert first.end_hour == '24:00' + assert second.start_hour == '00:00' + assert first.start_day + 1 == second.start_day + assert first.end_day + 1 == second.end_day + + +def test_multiple_entries_should_parse(parser): + interval = parser.parse('Mo 10-12, Sa 10-12') + + assert len(interval) == 2 + + +def test_single_entry_with_months_should_parse(parser): + interval = parser.parse('Mo 10-12 Jan-Jul')[0] + + assert interval.start_month == 1 + assert interval.end_month == 7 diff --git a/tests/interval_test.py b/tests/interval_test.py deleted file mode 100644 index ed141b6..0000000 --- a/tests/interval_test.py +++ /dev/null @@ -1,41 +0,0 @@ -import pytest - -from luupsmap.cli.util.opening_hours_parser import OpeningHoursParser - - -@pytest.fixture() -def parser(): - return OpeningHoursParser() - - -def test_single_entry_with_simple_hours_should_parse(parser): - interval = parser.parse("Mo-Fr 10-12")[0] - - assert interval.start_day == 0 - assert interval.end_day == 4 - assert interval.start_hour == "10:00" - assert interval.end_hour == "12:00" - - -def test_single_entry_with_single_day_should_parse(parser): - interval = parser.parse("Tu 0-0")[0] - - assert interval.start_day == 1 - assert interval.end_day == 1 - assert interval.start_hour == "10:00" - assert interval.end_hour == "12:00" - - -def test_single_entry_with_detailed_hours_should_parse(parser): - interval = parser.parse("Mo-Fr 10:30-12:45")[0] - - assert interval.start_day == 0 - assert interval.end_day == 4 - assert interval.start_hour == "10:30" - assert interval.end_hour == "12:45" - - -def test_multiple_entries_should_parse(parser): - interval = parser.parse("Mo-Fr 10-12, Sa-Su 7-9") - - assert len(interval) == 2 From 770ad68f575074cd700ea419d4d0c8ccb2974418 Mon Sep 17 00:00:00 2001 From: hschne Date: Sat, 6 Jul 2019 17:18:01 +0200 Subject: [PATCH 04/21] Fix tests and continue with importer --- luupsmap/cli/commands/seed.py | 10 +++--- luupsmap/cli/util/interval_parser.py | 10 ++---- migrations/alembic.ini | 8 +---- ...c6a3db53f67_fix_references_on_intervals.py | 36 +++++++++++++++++++ tests/cli/util/interval_parser_test.py | 36 +++++++++---------- 5 files changed, 64 insertions(+), 36 deletions(-) create mode 100644 migrations/versions/ac6a3db53f67_fix_references_on_intervals.py diff --git a/luupsmap/cli/commands/seed.py b/luupsmap/cli/commands/seed.py index f9497ed..837d2f2 100644 --- a/luupsmap/cli/commands/seed.py +++ b/luupsmap/cli/commands/seed.py @@ -1,7 +1,7 @@ -from cli.util.interval_parser import IntervalParser 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: @@ -51,6 +51,7 @@ def __create_and_save_models(self): 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() @@ -67,10 +68,11 @@ def __create_locations(self, venue, locations): def __create__intervals(self, location, intervals): string = location['opening_hours'] new_intervals = IntervalParser().parse(string) + interval_models = [] for interval in new_intervals: - interval.id_location = location.id + interval['id_location'] = location.id + interval_models.append(Interval(interval)) location.opening_hours.append(new_intervals) - intervals.append(new_intervals) def __create_vouchers(self, venue, vouchers): name = venue.name diff --git a/luupsmap/cli/util/interval_parser.py b/luupsmap/cli/util/interval_parser.py index 15a31e2..3f907ff 100644 --- a/luupsmap/cli/util/interval_parser.py +++ b/luupsmap/cli/util/interval_parser.py @@ -1,8 +1,6 @@ import calendar import time -from luupsmap import Interval - class IntervalParser: DAY_MAPPING = {v[:2]: k for k, v in enumerate(calendar.day_abbr)} @@ -39,10 +37,8 @@ def __parse_parts(self, parts): if start_hour > end_hour: self.__split_hours(arguments) - # Split into two args in case we have opening hours like Mo 22:00 - 04:00. This should result in two - # intervals, one being Mo 22:00-00:00 and one being Tu 00:00-04:00. else: - self.parsed.append(Interval(arguments)) + self.parsed.append(arguments) def __parse_days(self, parts): days = parts[0] @@ -64,11 +60,11 @@ def __parse_months(self, parts): def __split_hours(self, arguments): additional_args = arguments.copy() arguments['end_hour'] = '24:00' - self.parsed.append(Interval(arguments)) + 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(Interval(additional_args)) + self.parsed.append(additional_args) def __parse_hours(self, parts): hours = parts[1] diff --git a/migrations/alembic.ini b/migrations/alembic.ini index f8ed480..78e76fe 100644 --- a/migrations/alembic.ini +++ b/migrations/alembic.ini @@ -1,13 +1,7 @@ # A generic, single database configuration. [alembic] -# template used to generate migration files -# file_template = %%(rev)s_%%(slug)s - -# set to 'true' to run the environment during -# the 'revision' command, regardless of autogenerate -# revision_environment = false - +file_template = %%(year)d_%%(month).2d%%(day).2d%%(hour).2d%%(minute).2d%%(second).2d_%%(rev)s_%%(slug)s # Logging configuration [loggers] diff --git a/migrations/versions/ac6a3db53f67_fix_references_on_intervals.py b/migrations/versions/ac6a3db53f67_fix_references_on_intervals.py new file mode 100644 index 0000000..7ebb794 --- /dev/null +++ b/migrations/versions/ac6a3db53f67_fix_references_on_intervals.py @@ -0,0 +1,36 @@ +"""Fix references on intervals + +Revision ID: ac6a3db53f67 +Revises: 8dfac21ded04 +Create Date: 2019-07-06 17:05:27.574809 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'ac6a3db53f67' +down_revision = '8dfac21ded04' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('interval', sa.Column('id_location', sa.Integer(), nullable=False)) + op.drop_constraint('interval_id_venue_fkey', 'interval', type_='foreignkey') + op.create_foreign_key(None, 'interval', 'venue', ['id_location'], ['id']) + op.drop_column('interval', 'id_venue') + op.drop_column('location', 'opening_hours') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('location', sa.Column('opening_hours', sa.TEXT(), autoincrement=False, nullable=True)) + op.add_column('interval', sa.Column('id_venue', sa.INTEGER(), autoincrement=False, nullable=False)) + op.drop_constraint(None, 'interval', type_='foreignkey') + op.create_foreign_key('interval_id_venue_fkey', 'interval', 'venue', ['id_venue'], ['id']) + op.drop_column('interval', 'id_location') + # ### end Alembic commands ### diff --git a/tests/cli/util/interval_parser_test.py b/tests/cli/util/interval_parser_test.py index e646ed0..c5a02f2 100644 --- a/tests/cli/util/interval_parser_test.py +++ b/tests/cli/util/interval_parser_test.py @@ -11,28 +11,28 @@ def parser(): def test_single_entry_with_simple_hours_should_parse(parser): interval = parser.parse('Mo-Fr 10-12')[0] - assert interval.start_day == 0 - assert interval.end_day == 4 - assert interval.start_hour == '10:00' - assert interval.end_hour == '12:00' + assert interval['start_day'] == 0 + assert interval['end_day'] == 4 + assert interval['start_hour'] == '10:00' + assert interval['end_hour'] == '12:00' def test_single_entry_with_single_day_should_parse(parser): interval = parser.parse('Tu 10-12')[0] - assert interval.start_day == 1 - assert interval.end_day == 1 - assert interval.start_hour == '10:00' - assert interval.end_hour == '12:00' + assert interval['start_day'] == 1 + assert interval['end_day'] == 1 + assert interval['start_hour'] == '10:00' + assert interval['end_hour'] == '12:00' def test_single_entry_with_detailed_hours_should_parse(parser): interval = parser.parse('Mo-Fr 10:30-12:45')[0] - assert interval.start_day == 0 - assert interval.end_day == 4 - assert interval.start_hour == '10:30' - assert interval.end_hour == '12:45' + assert interval['start_day'] == 0 + assert interval['end_day'] == 4 + assert interval['start_hour'] == '10:30' + assert interval['end_hour'] == '12:45' def test_roll_over_entries_should_parse(parser): @@ -40,10 +40,10 @@ def test_roll_over_entries_should_parse(parser): assert len(interval) == 2 first, second = interval[0], interval[1] - assert first.end_hour == '24:00' - assert second.start_hour == '00:00' - assert first.start_day + 1 == second.start_day - assert first.end_day + 1 == second.end_day + assert first['end_hour'] == '24:00' + assert second['start_hour'] == '00:00' + assert first['start_day'] + 1 == second['start_day'] + assert first['end_day'] + 1 == second['end_day'] def test_multiple_entries_should_parse(parser): @@ -55,5 +55,5 @@ def test_multiple_entries_should_parse(parser): def test_single_entry_with_months_should_parse(parser): interval = parser.parse('Mo 10-12 Jan-Jul')[0] - assert interval.start_month == 1 - assert interval.end_month == 7 + assert interval['start_month'] == 1 + assert interval['end_month'] == 7 From b6269f78e486d56ff5458a06a0a7c29840e079f1 Mon Sep 17 00:00:00 2001 From: hschne Date: Sat, 6 Jul 2019 17:39:11 +0200 Subject: [PATCH 05/21] Clean up csv --- data/locations.csv | 224 ++++++++++++++++++++++----------------------- 1 file changed, 112 insertions(+), 112 deletions(-) diff --git a/data/locations.csv b/data/locations.csv index efc3d70..08385e6 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 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-Su 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 | | 48.0063245 | 16.2337794 +Au - Raum für Kunst und Kultur | Brunnengasse 76, 1160 Wien, Österreich | | | Mo-Do 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-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, Su-Ft 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-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 | Mi-Sa 18-22, Mi-Su 10-15 | 48.1945438 | 16.3458842 +Black Dogs Coffee | Wallgasse 27, 1060 Wien, Österreich | | +43 664 536 6088 | Di-Sa 9-12:30, Di-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 | | 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 | | | | 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 8-17 | 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-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-Su 9-18 | 48.211378 | 16.344551 +Deli Bluem | Laudongasse 17, 1080 Wien, Österreich | | +43 1 8900 449 | Mo-Fr 8-19h, Sa-Su 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-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-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-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 | | 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 | | 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, Sa 9-23, Jul-Aug | 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 | 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 | | 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 | | 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-So 12-22, Jul-Sep | 48.20646190000001 | 16.3502846 +Schelato | Schleifmühlgasse 11, 1040 Wien, Österreich | info@schelato.at | | Mo-So 12-22, Jun-Sep | 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 | 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 | | 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, 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 | | 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-So 15-0 Jan, Mo-So 15-0 Mar, Mo-So 15-0 May, Mo-So 15-0 Jul, Mo-So 15-0 Sep, Mo-So 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 From bae5fb629da9468299518432e6cbfa808033de13 Mon Sep 17 00:00:00 2001 From: hschne Date: Sat, 6 Jul 2019 18:31:01 +0200 Subject: [PATCH 06/21] Fix seed command --- data/locations.csv | 30 +++++++++---------- luupsmap/cli/commands/seed.py | 26 ++++++++-------- luupsmap/cli/commands/update.py | 8 ++--- luupsmap/cli/util/factories/__init__.py | 0 .../cli/util/factories/location_factory.py | 0 luupsmap/cli/util/interval_parser.py | 7 +++-- luupsmap/model/interval.py | 2 +- luupsmap/model/location.py | 9 ++---- ...74453_badd7e9a5689_fix_references_again.py | 30 +++++++++++++++++++ tests/cli/util/interval_parser_test.py | 9 ++++++ 10 files changed, 80 insertions(+), 41 deletions(-) create mode 100644 luupsmap/cli/util/factories/__init__.py create mode 100644 luupsmap/cli/util/factories/location_factory.py create mode 100644 migrations/versions/2019_0706174453_badd7e9a5689_fix_references_again.py diff --git a/data/locations.csv b/data/locations.csv index 08385e6..b0b8781 100644 --- a/data/locations.csv +++ b/data/locations.csv @@ -7,7 +7,7 @@ Adamah Biohof | Glinzendorf 7, 2282 Glinzendorf, 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 | | 48.0063245 | 16.2337794 -Au - Raum für Kunst und Kultur | Brunnengasse 76, 1160 Wien, Österreich | | | Mo-Do 18-2, Su-Ft 18-2, Fr-Sa 18-4 | 48.2143648 | 16.3371565 +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-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, Su-Ft 7-12:30 | 48.2074219 | 16.3435234 Bank Austria Kunstforum Wien | Freyung 8, 1010 Wien, Österreich | | +43 1 5373326 | | 48.211486 | 16.3662557 @@ -17,14 +17,14 @@ Beaver Brewing Company | Liechtensteinstraße 69, 1090 Wie 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 | Mi-Sa 18-22, Mi-Su 10-15 | 48.1945438 | 16.3458842 -Black Dogs Coffee | Wallgasse 27, 1060 Wien, Österreich | | +43 664 536 6088 | Di-Sa 9-12:30, Di-Sa 17-22 | 48.1922288 | 16.3393213 +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 +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 | | 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 @@ -33,11 +33,11 @@ Café Klitzeklein | Ausstellungsstraße 2, 1020 Wien 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 8-17 | 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-So 10-17 | 48.2144043 | 16.3511785 +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 | | 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-Su 9-18 | 48.211378 | 16.344551 -Deli Bluem | Laudongasse 17, 1080 Wien, Österreich | | +43 1 8900 449 | Mo-Fr 8-19h, Sa-Su 9-18 | 48.213209 | 16.350719 +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 | | 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 @@ -51,11 +51,11 @@ Hase und Igel | Theobaldgasse 16, 1060 Wien, Öst 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-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-19h | 48.20203919999999 | 16.3522683 +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, So-Ft 12-18 | 48.1991733 | 16.350998 +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 | | 48.200681 | 16.3522777 Kunsthalle Wien | Museumsplatz 1, 1070 Wien, Österreich | | +43 1 521890 | | 48.2026499 | 16.3591421 @@ -65,7 +65,7 @@ Leones Gelato | Praterstr. 16, 1020 Wien, Österr 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, Sa 9-23, Jul-Aug | 48.21344010000001 | 16.3358906 +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 | | 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 | Tu-Su 11-14:30, Tu-Su 18-23 | 48.19239340000001 | 16.3481239 @@ -80,8 +80,8 @@ RE:TREAT - Home for Yoga, Meditation & Vegan | Nelkengasse 6, Tür 14, 3. Stock, 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-So 12-22, Jul-Sep | 48.20646190000001 | 16.3502846 -Schelato | Schleifmühlgasse 11, 1040 Wien, Österreich | info@schelato.at | | Mo-So 12-22, Jun-Sep | 48.1971518 | 16.3652054 +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 | | 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 @@ -101,12 +101,12 @@ Top Kino | Rahlgasse 1, 1060 Wien, Österrei 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 | Mo-So 15-0 Jan, Mo-So 15-0 Mar, Mo-So 15-0 May, Mo-So 15-0 Jul, Mo-So 15-0 Sep, Mo-So 15-0 Nov | 48.2780059 | 16.4070448 +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 +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/luupsmap/cli/commands/seed.py b/luupsmap/cli/commands/seed.py index 837d2f2..e93472e 100644 --- a/luupsmap/cli/commands/seed.py +++ b/luupsmap/cli/commands/seed.py @@ -7,6 +7,7 @@ class SeedCommand: venues = [] locations = {} + intervals = {} vouchers = {} def __init__(self, venues_file, locations_file, vouchers_file): @@ -45,9 +46,7 @@ def __create_and_save_models(self): venue['vouchers'] = [] venue['locations'] = [] venue = Venue(venue) - self.__create_locations(venue, locations) - for location in locations: - self.__create__intervals(location, intervals) + self.__create_locations(venue, locations, intervals) self.__create_vouchers(venue, vouchers) db.session.add_all(venues) db.session.add_all(locations) @@ -55,24 +54,27 @@ def __create_and_save_models(self): 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__intervals(self, location, intervals): - string = location['opening_hours'] - new_intervals = IntervalParser().parse(string) - interval_models = [] - for interval in new_intervals: - interval['id_location'] = location.id - interval_models.append(Interval(interval)) - location.opening_hours.append(new_intervals) + 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 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/util/factories/__init__.py b/luupsmap/cli/util/factories/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/luupsmap/cli/util/factories/location_factory.py b/luupsmap/cli/util/factories/location_factory.py new file mode 100644 index 0000000..e69de29 diff --git a/luupsmap/cli/util/interval_parser.py b/luupsmap/cli/util/interval_parser.py index 3f907ff..6b4beff 100644 --- a/luupsmap/cli/util/interval_parser.py +++ b/luupsmap/cli/util/interval_parser.py @@ -12,6 +12,9 @@ def __init__(self): 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(' ')] @@ -23,7 +26,6 @@ def __parse_parts(self, parts): start_hour, end_hour = self.__parse_hours(parts) - # TODO: Add variable months, e.g. if opening hours vary during the year start_month, end_month = self.__parse_months(parts) arguments = { @@ -43,6 +45,7 @@ def __parse_parts(self, parts): def __parse_days(self, parts): days = parts[0] day_split = days.split('-') + print(parts) 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 @@ -54,7 +57,7 @@ def __parse_months(self, parts): months = parts[2] month_split = months.split('-') start_month = self.MONTH_MAPPING[month_split[0]] - end_month = self.MONTH_MAPPING[month_split[1]] + end_month = self.MONTH_MAPPING[month_split[1]] if len(month_split) > 1 else start_month return start_month, end_month def __split_hours(self, arguments): diff --git a/luupsmap/model/interval.py b/luupsmap/model/interval.py index 231f388..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_location = 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) diff --git a/luupsmap/model/location.py b/luupsmap/model/location.py index 9771b40..8ca27f3 100644 --- a/luupsmap/model/location.py +++ b/luupsmap/model/location.py @@ -17,13 +17,8 @@ class Location(db.Model): 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/migrations/versions/2019_0706174453_badd7e9a5689_fix_references_again.py b/migrations/versions/2019_0706174453_badd7e9a5689_fix_references_again.py new file mode 100644 index 0000000..502aa11 --- /dev/null +++ b/migrations/versions/2019_0706174453_badd7e9a5689_fix_references_again.py @@ -0,0 +1,30 @@ +"""Fix references again + +Revision ID: badd7e9a5689 +Revises: ac6a3db53f67 +Create Date: 2019-07-06 17:44:53.493533 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'badd7e9a5689' +down_revision = 'ac6a3db53f67' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint('interval_id_location_fkey', 'interval', type_='foreignkey') + op.create_foreign_key(None, 'interval', 'location', ['id_location'], ['id']) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'interval', type_='foreignkey') + op.create_foreign_key('interval_id_location_fkey', 'interval', 'venue', ['id_location'], ['id']) + # ### end Alembic commands ### diff --git a/tests/cli/util/interval_parser_test.py b/tests/cli/util/interval_parser_test.py index c5a02f2..33d3347 100644 --- a/tests/cli/util/interval_parser_test.py +++ b/tests/cli/util/interval_parser_test.py @@ -7,6 +7,10 @@ def parser(): return IntervalParser() +def test_empty_entry_should_return_empty(parser): + interval = parser.parse(' ') + + assert [] == interval def test_single_entry_with_simple_hours_should_parse(parser): interval = parser.parse('Mo-Fr 10-12')[0] @@ -51,6 +55,11 @@ def test_multiple_entries_should_parse(parser): assert len(interval) == 2 +def test_single_entry_with_single_month_should_parse(parser): + interval = parser.parse('Mo 10-12 Jan')[0] + + assert interval['start_month'] == 1 + assert interval['end_month'] == 1 def test_single_entry_with_months_should_parse(parser): interval = parser.parse('Mo 10-12 Jan-Jul')[0] From 6a8ef96d6fabc78f0eea1051be1b29d0d3424bab Mon Sep 17 00:00:00 2001 From: hschne Date: Sat, 6 Jul 2019 18:39:59 +0200 Subject: [PATCH 07/21] Add cli documentation --- luupsmap/cli/data.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/luupsmap/cli/data.py b/luupsmap/cli/data.py index ac22f79..6aa100c 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,30 +28,50 @@ 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() From 91fae5a486e8e08b0ca9a06142fb093790b0c06c Mon Sep 17 00:00:00 2001 From: hschne Date: Sun, 7 Jul 2019 11:24:46 +0200 Subject: [PATCH 08/21] Implement prototype of now filter --- luupsmap/service/venue_service.py | 35 +++++++++++++++++++++++-------- luupsmap/templates/main.html | 15 ++++++++++--- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/luupsmap/service/venue_service.py b/luupsmap/service/venue_service.py index db9741e..8a982d7 100644 --- a/luupsmap/service/venue_service.py +++ b/luupsmap/service/venue_service.py @@ -1,8 +1,10 @@ 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 +from datetime import datetime + class VenueService: def __init__(self): @@ -26,6 +28,15 @@ 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() @@ -35,21 +46,27 @@ def __filter_by(self, types, tags): venues = self.db_session.query(Venue) \ .join(Voucher) \ .join(VoucherType) \ + .join(Location) \ + .outerjoin(Interval) \ .outerjoin(VoucherTag) + today = datetime.today() + weekday = today.weekday() + month = today.month + time = today.time() if types: venues = venues.filter(VoucherType.type.in_(types)) if tags: venues = venues.filter(VoucherTag.tag.in_(tags)) - return venues.all() + if time: + venues = venues.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) + return venues.all() - @as_dto(VenueDto) - def create(self, venue): - self.db_session.add(venue) - return self.db_session.commit() @staticmethod def __determine_type(vouchers): diff --git a/luupsmap/templates/main.html b/luupsmap/templates/main.html index 4720bef..076b3d5 100644 --- a/luupsmap/templates/main.html +++ b/luupsmap/templates/main.html @@ -27,10 +27,11 @@

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 %}
@@ -73,4 +74,12 @@

Tags

{% endfor %} {% endfor %} + + {% endblock %} From 94ff05bd2e73dc5dc067597fd6d28690ab7f4b0c Mon Sep 17 00:00:00 2001 From: hschne Date: Sun, 14 Jul 2019 10:56:39 +0200 Subject: [PATCH 09/21] Refactor filter --- luupsmap/service/venue_service.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/luupsmap/service/venue_service.py b/luupsmap/service/venue_service.py index 8a982d7..35e4108 100644 --- a/luupsmap/service/venue_service.py +++ b/luupsmap/service/venue_service.py @@ -45,29 +45,27 @@ def __find_all(self): def __filter_by(self, types, tags): venues = self.db_session.query(Venue) \ .join(Voucher) \ - .join(VoucherType) \ - .join(Location) \ - .outerjoin(Interval) \ - .outerjoin(VoucherTag) + .join(Location) today = datetime.today() weekday = today.weekday() month = today.month time = today.time() 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)) + venues = venues.join(VoucherTag).filter(VoucherTag.tag.in_(tags)) if time: - venues = venues.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) + 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) return venues.all() - @staticmethod def __determine_type(vouchers): types = {voucher_type for voucher in vouchers for voucher_type in voucher.voucher_types} From dea1f3c60e922c52a5c43e9dfc0e7fb6e29fbeff Mon Sep 17 00:00:00 2001 From: hschne Date: Sun, 14 Jul 2019 11:44:26 +0200 Subject: [PATCH 10/21] Add picker --- luupsmap/static/css/custom.css | 9 + luupsmap/static/css/picker.css | 316 ++++++ luupsmap/static/js/modal.js | 4 +- luupsmap/static/js/picker.de-DE.js | 50 + luupsmap/static/js/picker.js | 1685 ++++++++++++++++++++++++++++ luupsmap/templates/index.html | 3 + luupsmap/templates/main.html | 19 + luupsmap/views.py | 2 +- 8 files changed, 2085 insertions(+), 3 deletions(-) create mode 100755 luupsmap/static/css/picker.css create mode 100755 luupsmap/static/js/picker.de-DE.js create mode 100755 luupsmap/static/js/picker.js diff --git a/luupsmap/static/css/custom.css b/luupsmap/static/css/custom.css index ec4ff45..f432337 100644 --- a/luupsmap/static/css/custom.css +++ b/luupsmap/static/css/custom.css @@ -1,6 +1,15 @@ /* FONTS **************************************************************************************************************/ @import url('https://fonts.googleapis.com/css?family=Amaranth:400,700|Open+Sans:300,400,700'); +/*CSS Variables? */ +:root { + --gray: #999; + --blue: #0074d9; + --color: #333; + --background-color: #fff; + --border: 1px solid #eee; +} + body { font-family: 'Open Sans', sans-serif; } diff --git a/luupsmap/static/css/picker.css b/luupsmap/static/css/picker.css new file mode 100755 index 0000000..b05333d --- /dev/null +++ b/luupsmap/static/css/picker.css @@ -0,0 +1,316 @@ +/*! + * 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 + */ + +:root { + --gray: #999; + --blue: #0074d9; + --color: #333; + --background-color: #fff; + --border: 1px solid #eee; +} + +.picker { + background-color: rgba(0, 0, 0, 0.5); + 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; +} + +.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; + background-color: var(--background-color); + border: 1px solid #eee; + border: var(--border); +} + +.picker-header { + border-bottom: 1px solid #eee; + border-bottom: var(--border); + 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: #0074d9; + 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..7bcc44a --- /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/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 076b3d5..5df9251 100644 --- a/luupsmap/templates/main.html +++ b/luupsmap/templates/main.html @@ -35,6 +35,13 @@

Tags

{% endfor %} +

Zeit

+
+ + +
+
+