{"id":555,"date":"2020-07-20T11:20:00","date_gmt":"2020-07-20T09:20:00","guid":{"rendered":"https:\/\/sebastian.fam-knopp.de\/?p=555"},"modified":"2021-02-19T11:23:05","modified_gmt":"2021-02-19T10:23:05","slug":"standort-tracking-und-barcode-checkin-app","status":"publish","type":"post","link":"https:\/\/sebastian.knopp.it\/?p=555","title":{"rendered":"Standort-Tracking und Barcode CheckIn-App"},"content":{"rendered":"\n<p>Das diesj\u00e4hrige KSJ Sommerlager im Zeichen von Corona stellte uns vor ganz neue Herausforderungen. Um, anders als viele andere Jugendverb\u00e4nde und Pfarreien unsere Fahrt nicht absagen zu m\u00fcssen, trafen wir uns im Rahmen der Leitungsebene zeitweise w\u00f6chentlich um \u00fcber die neuesten Entwicklungen zu beratschlagen, ein eigenes Hygienekonzept auszuarbeiten und Informationsbriefe f\u00fcr die Teilnehmer zu formulieren.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Teilnahme ausschlie\u00dflich in festen Bezugsgruppen<\/h3>\n\n\n\n<p>Ein wesentlicher Teil dieses Hygienekonzept war die Unterteilung unserer Teilnehmer in sogenannte Bezugsgruppen. Die Idee war, dass man innerhalb der zwei Wochen alles gemeinsam innerhalb dieser Bezugsgruppen ohne Maske machen kann und prim\u00e4r auch alle Aktionen mit der gesamten Gruppe macht. Zu jeder Gruppe geh\u00f6rte zudem ein Leiter und anders als in den Jahren zuvor bestimmte nicht jeder f\u00fcr sich selbst das n\u00e4chste Tagesprogramm, sondern die Gruppe gemeinsam. Wir nannten diese Gruppen jugendfreundlich &#8222;SoLa-Family&#8217;s&#8220; und das Konzept kam so gut an, dass wir es wohl auch in Zukunft ohne Pandemie beibehalten wollen werden.<\/p>\n\n\n\n<p>Organisatorisch mussten wir bei einer zweiw\u00f6chigen Fahrt den Teilnehmern die M\u00f6glichkeit geben sich selbst in diese Gruppen einzuteilen. Ich hatte gl\u00fccklicherweise kurz zuvor das Gruppeneinteilungssystem f\u00fcr die geplante SummerSail f\u00fcr WordPress programmiert, welches wir nun statt f\u00fcr Schiffe einfach vorbereitend f\u00fcr die Bezugsgruppen verwenden konnten. Dadurch entstanden auch ganz lustige Gruppennamen wie bspw. &#8222;Der gr\u00f6lende Seemann&#8220;, da die Zeit zur Anpassung des Generators f\u00fcr Gruppennamen dann nun wirklich fehlte. Diese Gruppenw\u00fcnsche der Teilnehmer stellten wir in einem weiteren Schritt so zusammen, dass es p\u00e4dagogisch unserer Ansicht nach gut passte und packten geeignete Leiter dazu.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Altes CheckIn-System nicht mehr verwendbar<\/h3>\n\n\n\n<p>Auch zum Hygienekonzept geh\u00f6rte, dass wir erstmals die Busse nicht zeitgleich, sondern in Etappen fahren lie\u00dfen, damit der Andrang beim Verladen und Einsteigen durch Teilnehmer und Eltern nicht zu gro\u00df wurde. Es durfte jeder Teilnehmer auch nur lediglich eine Begleitung mitbringen. Das alte CheckIn-System war jedoch darauf ausgelegt, dass erst alle Personen zentral eingecheckt werden, dann die Listen gedruckt werden und anschlie\u00dfend diese Listen beim einsteigen abgehakt werden. <\/p>\n\n\n\n<p>Wir brauchten also schnell etwas Neues und ich setzte mich dran endlich mal Swift zu lernen und meine erste native iOS-App zu schreiben. Diese App stellte einen Barcode-Scanner bereit, mit dessen Hilfe die PKPASS- und PDF-Tickets unseres neuen WordPress-Ticketsystems schnell direkt an der T\u00fcr gescannt werden konnten. Lediglich das coronakonforme Einsteigen innerhalb der Bezugsgruppen wurde dieses Jahr von einer weiteren Person mittels Papierliste sichergestellt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Refactoring des eigenen alten Servercodes <\/h3>\n\n\n\n<p>Damit die App funktionieren konnte musste ich zudem die alten serverseitigen Websocket-Schnittstellen (eine alte Fehlentscheidung im Projektdesign) neu als REST-Schnittstellen implementieren. Ich nutzte die Gelegenheit mein altes statisches Datenbank-Modell auf das JavaScript ORM-System <code>sequelize<\/code> umzustellen um so Zeit und Codezeilen zu sparen. Es brachte hierzu praktischerweise direkt einen Generator f\u00fcr die JavaScript-Models mit, welche man lediglich noch h\u00e4ndisch um die korrekten Relationen erweitern musste. St\u00fcck f\u00fcr St\u00fcck entstand so nun auch ein wesentlich saubererer Servercode, welcher perspektivisch den alten Websocket-Code ersetzen k\u00f6nnen wird. F\u00fcr mich das erste richtig gro\u00dfe Refactoring eines eigenen Projekts. Stellenweise hatte ich Zweifel, ob das zeitlich noch zu machen sei, daher konzentrierte ich mich auf die dringend ben\u00f6tigte Kernfunktionalit\u00e4t, schaffte aber gleichzeitig die Basis f\u00fcr ein k\u00fcnftiges modernes Webinterface, welches das alte jQuery-System ersetzen k\u00f6nnen wird. Ohne die Idee des ORM-Mappers, w\u00e4re das Neuschreiben und Anpassen der alten SQL-Queries wohl zu zeitaufwendig geworden.<\/p>\n\n\n\n<p><strong>Docker war auch hier wieder extrem hilfreich.<\/strong> Gleich zu allererst arbeitete ich zwei Tage daran, den alten Server innerhalb Docker konfigurierbar und lauff\u00e4hig zu bekommen. Auch die bisher eigenst\u00e4ndige Programmwahl-Erweiterung auf Angularbasis, welche auf den Server zugriff band ich direkt mit ein und promotete sie so zu einem modernen zweiten Web-Frontend, welches nun auch im neue Features erweitert werden sollte. Ich konnte nun s\u00e4mtliche Code\u00e4nderungen sehr schnell auf einem Testsystem unter Production-Bedingungen ausprobieren, was bei einem so vielschichtigen Projekt sehr hilfreich ist.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Standort-Tracking der Leiter<\/h3>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a href=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/IMG_8774-scaled.jpeg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"768\" data-attachment-id=\"605\" data-permalink=\"https:\/\/sebastian.knopp.it\/?attachment_id=605\" data-orig-file=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/IMG_8774-scaled.jpeg\" data-orig-size=\"2560,1920\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;1.8&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;iPhone 11 Pro Max&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;1593246146&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;4.25&quot;,&quot;iso&quot;:&quot;64&quot;,&quot;shutter_speed&quot;:&quot;0.0082644628099174&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;1&quot;}\" data-image-title=\"Standorte der Busse\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/IMG_8774-300x225.jpeg\" data-large-file=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/IMG_8774-1024x768.jpeg\" src=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/IMG_8774-1024x768.jpeg\" alt=\"\" class=\"wp-image-605\" srcset=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/IMG_8774-1024x768.jpeg 1024w, https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/IMG_8774-300x225.jpeg 300w, https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/IMG_8774-768x576.jpeg 768w, https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/IMG_8774-1536x1152.jpeg 1536w, https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/IMG_8774-2048x1536.jpeg 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption>Standortanzeige der Reisebusse \u00fcber Leiter-Smartphones im Leitungsb\u00fcro<\/figcaption><\/figure><\/div>\n\n\n\n<p>Dieses Feature war die urspr\u00fcngliche Kernidee des ganzen Projekts. Ein kleines Tool, welches die Position der Reisebusse bei Hin- und R\u00fcckfahrt direkt an die Eltern meldete, um so f\u00fcr Diese die Ankunftszeit gut einsch\u00e4tzbar zu machen. Die Standortdaten werden bei Aktivierung durch den Nutzer von der iOS-App im Hintergrund regelm\u00e4\u00dfig gesammelt und via REST-API an den Server geschickt. Zudem erweiterte ich das Angular-Webinterface um zwei Unterseiten mit Google-Maps Karten, eine rein Interne und eine externe Karte. Als Admin kann man hier nun einzelne Marker \u00f6ffentlich schalten, sah aber immer auch alle aktuellsten Positionen des heutigen Tages in der internen Sicht. Die interne Sicht sollte uns im Leitungsb\u00fcro auf einem eigenen Monitor helfen, die Leiter im Blick zu behalten. Die Leiter konnten in der App selbst ihren Namen und eine Bezeichnung wie bspw. &#8222;Bus 1&#8220; setzen, welche dann zusammen mit den Standortdaten \u00fcbermittelt wurde. Eine eindeutige beim ersten App-Start generierte ID garantierte hierbei die Wiedererkennbarkeit beim Namenswechsel.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Evaluation<\/h3>\n\n\n\n<p>Das ist insgesamt schon recht gut gelaufen, jedoch machten Unregelm\u00e4\u00dfigkeiten beim Hintergrund-Scheduling von iOS (die durch den parallelen Release der deutschen Corona-App auffielen) einige Probleme. Au\u00dferdem hatte auch mein Code einen Bug, der bei einem Standort-Updateversuch im Hintergrund ohne vorhandenen Empfang automatisch einen Logout durchf\u00fchrte, sodass unsere Leiter regelm\u00e4\u00dfig ihre App auf der Autobahn pr\u00fcfen mussten. Die App ist zudem noch recht akku-intensiv. Das liegt an der noch recht naiven Implementierung der regelm\u00e4\u00dfigen Standort-Updates alle 50 Meter. Hier gibt es also noch viel Raum f\u00fcr Verbesserung. Insgesamt bin ich f\u00fcr ein in grade mal drei Wochen umzusetzendes Projekt aber sehr zufrieden mit dem Ergebnis.<\/p>\n\n\n\n<p>Die App ist <a rel=\"noreferrer noopener\" href=\"https:\/\/apps.apple.com\/de\/app\/ksj-event-manager\/id1519481983\" target=\"_blank\">im Apple App Store verf\u00fcgbar<\/a> und f\u00fcr Leiter unserer Gruppe nutzbar.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Screenshots der App<\/h3>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.22.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"518\" data-attachment-id=\"607\" data-permalink=\"https:\/\/sebastian.knopp.it\/?attachment_id=607\" data-orig-file=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.22.png\" data-orig-size=\"1676,848\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Bildschirmfoto-2021-02-19-um-11.19.22\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.22-300x152.png\" data-large-file=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.22-1024x518.png\" src=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.22-1024x518.png\" alt=\"\" class=\"wp-image-607\" srcset=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.22-1024x518.png 1024w, https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.22-300x152.png 300w, https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.22-768x389.png 768w, https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.22-1536x777.png 1536w, https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.22.png 1676w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.32.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"608\" data-permalink=\"https:\/\/sebastian.knopp.it\/?attachment_id=608\" data-orig-file=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.32.png\" data-orig-size=\"826,868\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Bildschirmfoto-2021-02-19-um-11.19.32\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.32-285x300.png\" data-large-file=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.32.png\" src=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.32.png\" alt=\"\" class=\"wp-image-608\" width=\"436\" height=\"458\" srcset=\"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.32.png 826w, https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.32-285x300.png 285w, https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/Bildschirmfoto-2021-02-19-um-11.19.32-768x807.png 768w\" sizes=\"auto, (max-width: 436px) 100vw, 436px\" \/><\/a><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Das diesj\u00e4hrige KSJ Sommerlager im Zeichen von Corona stellte uns vor ganz neue Herausforderungen. Um, anders als viele andere Jugendverb\u00e4nde und Pfarreien unsere Fahrt nicht absagen zu m\u00fcssen, trafen wir uns im Rahmen der Leitungsebene zeitweise w\u00f6chentlich um \u00fcber die neuesten Entwicklungen zu beratschlagen, ein eigenes Hygienekonzept auszuarbeiten und Informationsbriefe f\u00fcr die Teilnehmer zu formulieren. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":604,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"default","ast-site-content-layout":"","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","theme-transparent-header-meta":"default","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1],"tags":[],"class_list":["post-555","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-allgemein"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/sebastian.knopp.it\/wp-content\/uploads\/2021\/02\/EventManagerApp_TitleImage.png","jetpack_shortlink":"https:\/\/wp.me\/p4onxe-8X","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/sebastian.knopp.it\/index.php?rest_route=\/wp\/v2\/posts\/555","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sebastian.knopp.it\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sebastian.knopp.it\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sebastian.knopp.it\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sebastian.knopp.it\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=555"}],"version-history":[{"count":4,"href":"https:\/\/sebastian.knopp.it\/index.php?rest_route=\/wp\/v2\/posts\/555\/revisions"}],"predecessor-version":[{"id":610,"href":"https:\/\/sebastian.knopp.it\/index.php?rest_route=\/wp\/v2\/posts\/555\/revisions\/610"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sebastian.knopp.it\/index.php?rest_route=\/wp\/v2\/media\/604"}],"wp:attachment":[{"href":"https:\/\/sebastian.knopp.it\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=555"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sebastian.knopp.it\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=555"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sebastian.knopp.it\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=555"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}