├── ember
├── vendor
│ └── .gitkeep
├── app
│ ├── helpers
│ │ ├── .gitkeep
│ │ ├── lowercase.js
│ │ ├── to-iso-string.js
│ │ ├── sum.js
│ │ ├── includes.js
│ │ ├── multiply.js
│ │ ├── percent.js
│ │ ├── truncate.js
│ │ ├── html-safe.js
│ │ ├── format-lift.js
│ │ ├── format-speed.js
│ │ ├── format-altitude.js
│ │ ├── format-distance.js
│ │ ├── format-hours.js
│ │ ├── initials.js
│ │ ├── markdown.js
│ │ └── format-seconds.js
│ ├── models
│ │ └── .gitkeep
│ ├── routes
│ │ ├── .gitkeep
│ │ ├── flights
│ │ │ ├── all.js
│ │ │ ├── latest.js
│ │ │ ├── date.js
│ │ │ ├── index.js
│ │ │ ├── list.js
│ │ │ ├── club.js
│ │ │ ├── unassigned.js
│ │ │ ├── pilot.js
│ │ │ ├── airport.js
│ │ │ └── pinned.js
│ │ ├── statistics
│ │ │ ├── index.js
│ │ │ ├── club.js
│ │ │ ├── pilot.js
│ │ │ └── airport.js
│ │ ├── ranking
│ │ │ └── index.js
│ │ ├── settings
│ │ │ ├── profile.js
│ │ │ ├── index.js
│ │ │ ├── tracking.js
│ │ │ └── club.js
│ │ ├── flight
│ │ │ ├── map-redirect.js
│ │ │ └── change-aircraft.js
│ │ ├── ranking.js
│ │ ├── tracking
│ │ │ ├── map-redirect.js
│ │ │ ├── index.js
│ │ │ ├── details.js
│ │ │ └── info.js
│ │ ├── flight.js
│ │ ├── about
│ │ │ ├── team.js
│ │ │ ├── imprint.js
│ │ │ └── license.js
│ │ ├── club
│ │ │ ├── index.js
│ │ │ ├── edit.js
│ │ │ └── pilots.js
│ │ ├── clubs.js
│ │ ├── users
│ │ │ └── index.js
│ │ ├── club.js
│ │ ├── user.js
│ │ ├── settings.js
│ │ ├── user
│ │ │ ├── followers.js
│ │ │ └── following.js
│ │ └── login.js
│ ├── components
│ │ ├── .gitkeep
│ │ ├── cesium-scene.hbs
│ │ ├── flight-page.module.scss
│ │ ├── phase-icons-layer.hbs
│ │ ├── timeline-event.hbs
│ │ ├── cesium-plane-model.hbs
│ │ ├── fix-table.module.scss
│ │ ├── tracking-page.module.scss
│ │ ├── x-flag.hbs
│ │ ├── progress-bar.hbs
│ │ ├── timeline.module.scss
│ │ ├── nav-bar-menu-right.module.scss
│ │ ├── pin-star.module.scss
│ │ ├── plane-label-overlay.module.scss
│ │ ├── comments-list.module.scss
│ │ ├── flight-path-layer.hbs
│ │ ├── takeoffs-layer.hbs
│ │ ├── base-map.hbs
│ │ ├── nav-bar.js
│ │ ├── search-result-row.module.scss
│ │ ├── wingman-row.module.scss
│ │ ├── loading-page.hbs
│ │ ├── sidebar
│ │ │ ├── tabs.hbs
│ │ │ ├── tabs.module.scss
│ │ │ ├── panel.hbs
│ │ │ ├── tab.hbs
│ │ │ ├── tab.js
│ │ │ └── tab.module.scss
│ │ ├── timeline.hbs
│ │ ├── x-pager.module.scss
│ │ ├── flight-list-row.module.scss
│ │ ├── fullscreen-button.hbs
│ │ ├── notifications-badge.hbs
│ │ ├── progress-bar.js
│ │ ├── validated-input.js
│ │ ├── plane-label-overlays.hbs
│ │ ├── cesium-plane-models.hbs
│ │ ├── flight-phase-table.module.scss
│ │ ├── settings-page.js
│ │ ├── flight-phase-table.js
│ │ ├── follower-panel.module.scss
│ │ ├── flight-details-table.module.scss
│ │ ├── follower-list.hbs
│ │ ├── flight-performance-panel.hbs
│ │ ├── plane-label-overlay.hbs
│ │ ├── season-dropdown.js
│ │ ├── nav-bar-search-form.js
│ │ ├── upload-result-form.module.scss
│ │ ├── base-barogram.hbs
│ │ ├── notifications-badge.js
│ │ ├── cesium-button.hbs
│ │ ├── pin-button.hbs
│ │ ├── takeoffs-map.hbs
│ │ ├── flight-leg-panel.hbs
│ │ ├── playback-button.hbs
│ │ ├── sidebar.hbs
│ │ ├── timeline-event.js
│ │ ├── follower-page.hbs
│ │ ├── following-page.hbs
│ │ ├── pin-star.hbs
│ │ ├── stats-flights-table.js
│ │ ├── login-form.module.scss
│ │ ├── stats-distance-table.js
│ │ ├── stats-duration-table.js
│ │ ├── form-page.hbs
│ │ ├── stats-pilots-table.js
│ │ ├── aircraft-model-select.hbs
│ │ ├── contest-layer.hbs
│ │ ├── tracking-table.module.scss
│ │ ├── nav-bar-search-form.module.scss
│ │ ├── tracking-table-row.js
│ │ ├── cesium-button.js
│ │ ├── nav-bar-link.hbs
│ │ ├── pilot-select.hbs
│ │ ├── timeline-events
│ │ │ ├── new-user.js
│ │ │ ├── club-join.js
│ │ │ ├── new-user.hbs
│ │ │ ├── flight-upload.js
│ │ │ ├── club-join.hbs
│ │ │ ├── follower.hbs
│ │ │ ├── flight-upload.hbs
│ │ │ ├── -base.js
│ │ │ └── flight-comment.hbs
│ │ ├── tracking-tables.hbs
│ │ ├── base-page.module.scss
│ │ ├── layer-switcher-element.js
│ │ ├── validated-block.hbs
│ │ ├── circling-performance-row.hbs
│ │ ├── validated-input.hbs
│ │ ├── fix-table-row.module.scss
│ │ ├── nav-bar-user-menu.js
│ │ ├── fix-table.hbs
│ │ ├── base-page.hbs
│ │ ├── cesium-button.module.scss
│ │ ├── flight-list-row.js
│ │ ├── playback-button.module.scss
│ │ ├── flight-list-nav.module.scss
│ │ ├── progress-bar.module.scss
│ │ ├── wingman-table.hbs
│ │ ├── layer-switcher.js
│ │ ├── search-result-table.hbs
│ │ ├── tracking-table.hbs
│ │ ├── nav-bar-search-form.hbs
│ │ ├── fix-table-row.js
│ │ ├── pin-button.js
│ │ ├── settings-panels
│ │ │ └── tracking-key.module.scss
│ │ ├── pin-star.js
│ │ ├── fullscreen-button.module.scss
│ │ ├── layer-switcher-element.hbs
│ │ ├── user-quick-stats.js
│ │ ├── background-layers.hbs
│ │ ├── user-distance-flight.hbs
│ │ ├── sortable-table-header.hbs
│ │ ├── background-layers
│ │ │ └── empty.js
│ │ ├── contest-layer-feature.js
│ │ ├── circling-performance-table.hbs
│ │ ├── circling-performance-table.js
│ │ ├── login-form.js
│ │ ├── aircraft-model-select.js
│ │ └── fullscreen-button.js
│ ├── controllers
│ │ ├── .gitkeep
│ │ ├── flights.js
│ │ ├── login.js
│ │ ├── ranking
│ │ │ ├── clubs.js
│ │ │ ├── pilots.js
│ │ │ └── airports.js
│ │ ├── users
│ │ │ ├── recover.js
│ │ │ └── new.js
│ │ ├── ranking.js
│ │ ├── settings
│ │ │ └── club.js
│ │ ├── statistics.js
│ │ ├── flight
│ │ │ ├── index.js
│ │ │ ├── change-pilot.js
│ │ │ └── change-aircraft.js
│ │ ├── flights
│ │ │ ├── all.js
│ │ │ ├── club.js
│ │ │ ├── date.js
│ │ │ ├── list.js
│ │ │ ├── airport.js
│ │ │ ├── latest.js
│ │ │ ├── pilot.js
│ │ │ ├── pinned.js
│ │ │ └── unassigned.js
│ │ ├── flight-upload.js
│ │ ├── application.js
│ │ ├── index.js
│ │ ├── stats
│ │ │ └── wildcard.js
│ │ ├── search.js
│ │ └── about
│ │ │ └── team.js
│ ├── templates
│ │ ├── flight
│ │ │ ├── index_loading.hbs
│ │ │ ├── change-pilot.hbs
│ │ │ └── change-aircraft.hbs
│ │ ├── tracking
│ │ │ ├── details_loading.hbs
│ │ │ ├── index_loading.hbs
│ │ │ ├── index.hbs
│ │ │ └── details.hbs
│ │ ├── user
│ │ │ ├── index.hbs
│ │ │ ├── followers.hbs
│ │ │ └── following.hbs
│ │ ├── club
│ │ │ └── edit.hbs
│ │ ├── settings
│ │ │ ├── password.hbs
│ │ │ ├── club.hbs
│ │ │ ├── tracking.hbs
│ │ │ └── profile.hbs
│ │ ├── users
│ │ │ ├── new.hbs
│ │ │ └── recover.hbs
│ │ ├── login.hbs
│ │ ├── application.hbs
│ │ ├── error.hbs
│ │ ├── statistics
│ │ │ ├── pilot.hbs
│ │ │ ├── club.hbs
│ │ │ ├── airport.hbs
│ │ │ └── index.hbs
│ │ ├── about
│ │ │ ├── team.hbs
│ │ │ ├── imprint.hbs
│ │ │ └── license.hbs
│ │ ├── flights.hbs
│ │ ├── page-not-found.hbs
│ │ ├── flights
│ │ │ ├── all.hbs
│ │ │ ├── list.hbs
│ │ │ ├── pinned.hbs
│ │ │ ├── unassigned.hbs
│ │ │ ├── club.hbs
│ │ │ ├── date.hbs
│ │ │ ├── pilot.hbs
│ │ │ ├── airport.hbs
│ │ │ └── latest.hbs
│ │ └── flight-upload.hbs
│ ├── styles
│ │ ├── bootstrap
│ │ │ ├── _modals.scss
│ │ │ ├── _page-header.scss
│ │ │ ├── _tables.scss
│ │ │ └── _bootstrap-badges.scss
│ │ ├── timeline.module.scss
│ │ ├── notifications.module.scss
│ │ ├── application.module.scss
│ │ ├── app.scss
│ │ ├── _font-awesome.scss
│ │ ├── ranking.module.scss
│ │ └── _spinner.scss
│ ├── resolver.js
│ ├── utils
│ │ ├── pad.js
│ │ ├── iso-date.js
│ │ ├── add-days.js
│ │ ├── raf.js
│ │ ├── locales.js
│ │ ├── parse-query-string.js
│ │ └── geo-distance.js
│ ├── services
│ │ └── search-text.js
│ ├── modifiers
│ │ └── set-map-target.js
│ ├── computed
│ │ ├── is-none.js
│ │ └── computed-point.js
│ ├── app.js
│ ├── formats.js
│ └── validators
│ │ └── current-password.js
├── translations
│ ├── da.yaml
│ ├── it.yaml
│ ├── ja.yaml
│ ├── no.yaml
│ ├── pt.yaml
│ ├── hu.yaml
│ ├── sv.yaml
│ ├── fi.yaml
│ ├── cs.yaml
│ └── ru.yaml
├── .watchmanconfig
├── public
│ ├── robots.txt
│ ├── _redirects
│ ├── flags.png
│ ├── 3d
│ │ └── AS21.glb
│ ├── favicon.ico
│ ├── images
│ │ ├── 2d.png
│ │ ├── 3d.png
│ │ ├── play.png
│ │ ├── stop.png
│ │ ├── layers.png
│ │ ├── marker.png
│ │ ├── about-1.jpeg
│ │ ├── about-2.jpeg
│ │ ├── about-3.jpeg
│ │ ├── about-4.jpeg
│ │ ├── layers
│ │ │ ├── Empty.png
│ │ │ ├── Flight.png
│ │ │ ├── Terrain.png
│ │ │ ├── Airspace.png
│ │ │ ├── Bing Road.png
│ │ │ ├── Bing Satellite.png
│ │ │ ├── Google Physical.png
│ │ │ ├── OpenStreetMap.png
│ │ │ ├── Shaded Relief.png
│ │ │ ├── Google Satellite.png
│ │ │ ├── Takeoff Locations.png
│ │ │ └── Mountain Wave Project.png
│ │ ├── marker-gold.png
│ │ ├── marker-green.png
│ │ ├── glider_symbol.png
│ │ ├── hangglider_symbol.png
│ │ ├── paraglider_symbol.png
│ │ ├── glider_symbol_msie.png
│ │ └── motorglider_symbol.png
│ ├── apple-touch-icon.png
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ └── svg
│ │ └── icons
│ │ ├── play.svg
│ │ ├── minus.svg
│ │ ├── caret-down.svg
│ │ ├── bar-chart.svg
│ │ ├── long-arrow-right.svg
│ │ ├── chevron-down.svg
│ │ ├── chevron-up.svg
│ │ ├── check.svg
│ │ ├── step-forward.svg
│ │ ├── plus.svg
│ │ ├── map-marker.svg
│ │ ├── arrows-h.svg
│ │ ├── bolt.svg
│ │ ├── arrow-left.svg
│ │ ├── cloud.svg
│ │ ├── arrow-right.svg
│ │ ├── star.svg
│ │ ├── circle-o.svg
│ │ ├── pencil.svg
│ │ ├── remove.svg
│ │ ├── facebook-square.svg
│ │ ├── info.svg
│ │ ├── reorder.svg
│ │ ├── user.svg
│ │ ├── star-o.svg
│ │ ├── plane.svg
│ │ ├── ban.svg
│ │ ├── search.svg
│ │ ├── clock-o.svg
│ │ ├── warning.svg
│ │ ├── twitter.svg
│ │ ├── info-circle.svg
│ │ ├── wrench.svg
│ │ ├── asterisk.svg
│ │ ├── download.svg
│ │ ├── rotate-right.svg
│ │ ├── sign-in.svg
│ │ ├── sign-out.svg
│ │ ├── upload.svg
│ │ ├── rotate-left.svg
│ │ └── question.svg
├── .bowerrc
├── config
│ ├── dependency-lint.js
│ ├── coverage.js
│ ├── optional-features.json
│ └── targets.js
├── lib
│ ├── cesium
│ │ ├── package.json
│ │ └── index.js
│ ├── freestyle
│ │ ├── package.json
│ │ ├── index.js
│ │ └── app
│ │ │ ├── templates
│ │ │ ├── components
│ │ │ │ └── usage
│ │ │ │ │ └── x-flag.hbs
│ │ │ └── freestyle.hbs
│ │ │ └── routes
│ │ │ └── freestyle.js
│ └── .eslintrc.js
├── mirage
│ ├── route-handlers
│ │ ├── clubs.js
│ │ └── notifications.js
│ ├── models
│ │ ├── user.js
│ │ ├── club.js
│ │ └── mirage-session.js
│ ├── factories
│ │ ├── club.js
│ │ ├── mirage-session.js
│ │ └── user.js
│ ├── scenarios
│ │ └── default.js
│ ├── utils
│ │ └── session.js
│ ├── serializers
│ │ ├── application.js
│ │ └── club.js
│ └── config.js
├── bower.json
├── tests
│ ├── .eslintrc.js
│ ├── test-helpers
│ │ ├── match-json.js
│ │ └── auth.js
│ ├── test-helper.js
│ ├── helpers
│ │ └── initials-test.js
│ └── acceptance
│ │ └── index-test.js
├── .eslintignore
├── .gitignore
├── .ember-cli
└── .template-lintrc.js
├── tests
├── lib
│ ├── __init__.py
│ ├── test_basic_auth.py
│ └── test_files.py
├── model
│ └── __init__.py
├── api
│ ├── views
│ │ ├── __init__.py
│ │ ├── clubs
│ │ │ └── __init__.py
│ │ ├── users
│ │ │ └── __init__.py
│ │ ├── account
│ │ │ └── __init__.py
│ │ ├── airports
│ │ │ └── __init__.py
│ │ ├── flights
│ │ │ └── __init__.py
│ │ ├── aircraft_models
│ │ │ └── __init__.py
│ │ ├── notifications
│ │ │ └── __init__.py
│ │ ├── conftest.py
│ │ └── user_agent_test.py
│ └── __init__.py
├── tracking
│ ├── __init__.py
│ └── test_datetime.py
├── data
│ ├── igcs.zip
│ ├── __init__.py
│ ├── clubs.py
│ ├── flight_comments.py
│ ├── live_fix.py
│ └── airspace.py
└── schemas
│ ├── fields
│ └── test_location.py
│ └── schemas
│ └── test_flight.py
├── migrations
├── versions
│ ├── .gitkeep
│ ├── 3e82b37a4989_non_null_tracking_ke.py
│ ├── 5efafe47090_removed_eye_candy.py
│ ├── ffa5706b1fb_added_callsign_colum.py
│ ├── 2dade673f10e_add_qnh_column_to_flight.py
│ ├── 66650ad3d70_unregistered_pilot_n.py
│ ├── 58325345d375_add_weglide_fields_to_igcfile.py
│ ├── 1f33435e1753_added_valid_until_co.py
│ ├── 8a8e921cb88_added_scoring_window_columns.py
│ └── 16f9f28f0ea3_added_privacy_level_column.py
└── script.py.mako
├── skylines
├── api
│ ├── __init__.py
│ ├── cache.py
│ ├── views
│ │ ├── airports.py
│ │ └── aircraft_models.py
│ ├── json.py
│ └── args.py
├── lib
│ ├── __init__.py
│ ├── helpers
│ │ └── __init__.py
│ ├── waypoints
│ │ └── __init__.py
│ ├── formatter
│ │ └── __init__.py
│ ├── xcsoar_
│ │ └── __init__.py
│ ├── md5.py
│ ├── basic_auth.py
│ ├── compat.py
│ └── util.py
├── worker
│ ├── __init__.py
│ └── celery.py
├── frontend
│ ├── __init__.py
│ ├── templates
│ │ ├── widgets
│ │ │ └── wrapper.jinja
│ │ ├── __init__.py
│ │ └── error.jinja
│ └── views
│ │ ├── files.py
│ │ ├── __init__.py
│ │ └── assets.py
├── tracking
│ ├── __init__.py
│ ├── datetime.py
│ └── crc.py
├── schemas
│ ├── __init__.py
│ └── validate.py
├── commands
│ ├── notifications
│ │ ├── __init__.py
│ │ └── mark_all_unread.py
│ ├── users
│ │ └── __init__.py
│ ├── shell.py
│ ├── celery.py
│ ├── import_
│ │ └── __init__.py
│ ├── server.py
│ └── tracking
│ │ ├── server.py
│ │ ├── clear.py
│ │ └── fill_missing_keys.py
├── __about__.py
└── __init__.py
├── pytest.ini
├── srtm
├── .gitignore
├── extract.sh
├── download.sh
└── import.sh
├── mapserver
├── airports
│ ├── airport.png
│ └── airport_gray.png
└── fonts
│ └── truetype.txt
├── setup.cfg
├── manage.py
├── .dockerignore
├── wsgi_skylines.py
├── airspace
└── airspace_blacklist.txt
├── wsgi_mapproxy.py
├── config
└── testing.py
├── docker
└── docker-entrypoint-initdb.d
│ └── create-databases.sql
├── systemd
├── celery.service
├── tracking.service
├── mapproxy.service
└── skylines.service
├── .github
├── FUNDING.yml
└── renovate.json
├── .editorconfig
├── INSTALL.mapserver.md
└── Dockerfile
/ember/vendor/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/lib/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/model/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/helpers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/models/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/routes/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/migrations/versions/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/skylines/api/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/skylines/lib/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/skylines/worker/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/api/views/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/tracking/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/components/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/controllers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/skylines/frontend/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/skylines/lib/helpers/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/skylines/tracking/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/api/views/clubs/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/api/views/users/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/skylines/lib/waypoints/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/api/views/account/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/api/views/airports/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/api/views/flights/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/translations/da.yaml:
--------------------------------------------------------------------------------
1 | {
2 | }
3 |
--------------------------------------------------------------------------------
/ember/translations/it.yaml:
--------------------------------------------------------------------------------
1 | {
2 | }
3 |
--------------------------------------------------------------------------------
/ember/translations/ja.yaml:
--------------------------------------------------------------------------------
1 | {
2 | }
3 |
--------------------------------------------------------------------------------
/ember/translations/no.yaml:
--------------------------------------------------------------------------------
1 | {
2 | }
3 |
--------------------------------------------------------------------------------
/ember/translations/pt.yaml:
--------------------------------------------------------------------------------
1 | {
2 | }
3 |
--------------------------------------------------------------------------------
/tests/api/views/aircraft_models/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/api/views/notifications/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | testpaths = tests
3 |
--------------------------------------------------------------------------------
/srtm/.gitignore:
--------------------------------------------------------------------------------
1 | /downloads
2 | /unzipped
3 |
--------------------------------------------------------------------------------
/ember/app/templates/flight/index_loading.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/templates/tracking/details_loading.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/templates/user/index.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {
2 | "ignore_dirs": ["tmp", "dist"]
3 | }
4 |
--------------------------------------------------------------------------------
/ember/app/components/cesium-scene.hbs:
--------------------------------------------------------------------------------
1 | {{yield (hash ol3d=ol3d scene=scene)}}
--------------------------------------------------------------------------------
/ember/app/styles/bootstrap/_modals.scss:
--------------------------------------------------------------------------------
1 | .modal-dialog {
2 | top: 40px;
3 | }
4 |
--------------------------------------------------------------------------------
/skylines/api/cache.py:
--------------------------------------------------------------------------------
1 | from flask_caching import Cache
2 |
3 | cache = Cache()
4 |
--------------------------------------------------------------------------------
/skylines/lib/formatter/__init__.py:
--------------------------------------------------------------------------------
1 | # flake8: noqa
2 |
3 | from .units import *
4 |
--------------------------------------------------------------------------------
/ember/app/components/flight-page.module.scss:
--------------------------------------------------------------------------------
1 | .barogram {
2 | height: 133px;
3 | }
4 |
--------------------------------------------------------------------------------
/ember/app/components/phase-icons-layer.hbs:
--------------------------------------------------------------------------------
1 | {{did-update this.updateSource @coordinates}}
--------------------------------------------------------------------------------
/ember/app/components/timeline-event.hbs:
--------------------------------------------------------------------------------
1 | {{component this.eventComponent event=@event}}
--------------------------------------------------------------------------------
/ember/app/styles/timeline.module.scss:
--------------------------------------------------------------------------------
1 | .timeline {
2 | margin: 18px -10px;
3 | }
4 |
--------------------------------------------------------------------------------
/ember/public/robots.txt:
--------------------------------------------------------------------------------
1 | # http://www.robotstxt.org
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/skylines/frontend/templates/widgets/wrapper.jinja:
--------------------------------------------------------------------------------
1 | {{ callback }}('{{ content }}');
2 |
--------------------------------------------------------------------------------
/srtm/extract.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | unzip -j -d unzipped "downloads/*.zip"
4 |
--------------------------------------------------------------------------------
/ember/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components",
3 | "analytics": false
4 | }
5 |
--------------------------------------------------------------------------------
/ember/app/components/cesium-plane-model.hbs:
--------------------------------------------------------------------------------
1 | {{did-update this.update @coordinate @heading}}
--------------------------------------------------------------------------------
/ember/app/components/fix-table.module.scss:
--------------------------------------------------------------------------------
1 | .fix-table {
2 | margin-bottom: 0;
3 | }
4 |
--------------------------------------------------------------------------------
/ember/app/components/tracking-page.module.scss:
--------------------------------------------------------------------------------
1 | .barogram {
2 | height: 133px;
3 | }
4 |
--------------------------------------------------------------------------------
/ember/app/styles/notifications.module.scss:
--------------------------------------------------------------------------------
1 | .timeline {
2 | margin: 18px -10px;
3 | }
4 |
--------------------------------------------------------------------------------
/ember/app/resolver.js:
--------------------------------------------------------------------------------
1 | import Resolver from 'ember-resolver';
2 |
3 | export default Resolver;
4 |
--------------------------------------------------------------------------------
/ember/app/templates/user/followers.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/templates/user/following.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/components/x-flag.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/data/igcs.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/tests/data/igcs.zip
--------------------------------------------------------------------------------
/ember/app/components/progress-bar.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/public/_redirects:
--------------------------------------------------------------------------------
1 | /api/* https://skylines.aero/api/:splat 200
2 | /* /index.html 200
3 |
--------------------------------------------------------------------------------
/ember/public/flags.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/flags.png
--------------------------------------------------------------------------------
/srtm/download.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | wget -i tiles.txt -P downloads -c --quiet --show-progress
4 |
--------------------------------------------------------------------------------
/ember/config/dependency-lint.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | generateTests: false,
5 | };
6 |
--------------------------------------------------------------------------------
/ember/public/3d/AS21.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/3d/AS21.glb
--------------------------------------------------------------------------------
/ember/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/favicon.ico
--------------------------------------------------------------------------------
/ember/public/images/2d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/2d.png
--------------------------------------------------------------------------------
/ember/public/images/3d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/3d.png
--------------------------------------------------------------------------------
/ember/app/components/timeline.module.scss:
--------------------------------------------------------------------------------
1 | .list {
2 | list-style: none;
3 | margin: 0;
4 | padding: 0;
5 | }
6 |
--------------------------------------------------------------------------------
/ember/lib/cesium/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cesium",
3 | "keywords": [
4 | "ember-addon"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/ember/mirage/route-handlers/clubs.js:
--------------------------------------------------------------------------------
1 | export function register(server) {
2 | server.get('/api/clubs/:id');
3 | }
4 |
--------------------------------------------------------------------------------
/ember/public/images/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/play.png
--------------------------------------------------------------------------------
/ember/public/images/stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/stop.png
--------------------------------------------------------------------------------
/skylines/frontend/templates/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """Templates package for the application."""
3 |
--------------------------------------------------------------------------------
/srtm/import.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | raster2pgsql -a -e -s 4326 -t 100x100 unzipped/*.hgt elevations | psql
4 |
--------------------------------------------------------------------------------
/ember/app/components/nav-bar-menu-right.module.scss:
--------------------------------------------------------------------------------
1 | .badge {
2 | vertical-align: top;
3 | padding: 4px 7px;
4 | }
5 |
--------------------------------------------------------------------------------
/ember/app/utils/pad.js:
--------------------------------------------------------------------------------
1 | export default function pad(number) {
2 | return number < 10 ? `0${number}` : number;
3 | }
4 |
--------------------------------------------------------------------------------
/ember/lib/freestyle/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "freestyle",
3 | "keywords": [
4 | "ember-addon"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/ember/public/images/layers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/layers.png
--------------------------------------------------------------------------------
/ember/public/images/marker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/marker.png
--------------------------------------------------------------------------------
/mapserver/airports/airport.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/mapserver/airports/airport.png
--------------------------------------------------------------------------------
/ember/app/templates/club/edit.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ember/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/ember/public/images/about-1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/about-1.jpeg
--------------------------------------------------------------------------------
/ember/public/images/about-2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/about-2.jpeg
--------------------------------------------------------------------------------
/ember/public/images/about-3.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/about-3.jpeg
--------------------------------------------------------------------------------
/ember/public/images/about-4.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/about-4.jpeg
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [flake8]
2 | ignore = E203,E501,W503,E701,E711,E712,E722,F901
3 |
4 | [pytest]
5 | usefixtures = seeded_random
6 |
--------------------------------------------------------------------------------
/ember/app/components/pin-star.module.scss:
--------------------------------------------------------------------------------
1 | .pin {
2 | cursor: pointer;
3 | }
4 |
5 | .pinned {
6 | visibility: visible;
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/components/plane-label-overlay.module.scss:
--------------------------------------------------------------------------------
1 | .label {
2 | composes: badge from global;
3 | min-height: 1em;
4 | }
5 |
--------------------------------------------------------------------------------
/ember/public/images/layers/Empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/layers/Empty.png
--------------------------------------------------------------------------------
/ember/public/images/marker-gold.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/marker-gold.png
--------------------------------------------------------------------------------
/ember/public/images/marker-green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/marker-green.png
--------------------------------------------------------------------------------
/mapserver/airports/airport_gray.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/mapserver/airports/airport_gray.png
--------------------------------------------------------------------------------
/tests/data/__init__.py:
--------------------------------------------------------------------------------
1 | def add_fixtures(db_session, *fixtures):
2 | db_session.add_all(fixtures)
3 | db_session.commit()
4 |
--------------------------------------------------------------------------------
/ember/public/images/glider_symbol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/glider_symbol.png
--------------------------------------------------------------------------------
/ember/public/images/layers/Flight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/layers/Flight.png
--------------------------------------------------------------------------------
/ember/public/images/layers/Terrain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/layers/Terrain.png
--------------------------------------------------------------------------------
/ember/app/components/comments-list.module.scss:
--------------------------------------------------------------------------------
1 | .spinner {
2 | composes: spinner spinner-white from global;
3 | margin-left: 7px;
4 | }
5 |
--------------------------------------------------------------------------------
/ember/app/templates/settings/password.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ember/config/coverage.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | excludes: [],
5 | reporters: ['text-summary', 'html'],
6 | };
7 |
--------------------------------------------------------------------------------
/ember/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/ember/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/ember/public/images/hangglider_symbol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/hangglider_symbol.png
--------------------------------------------------------------------------------
/ember/public/images/layers/Airspace.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/layers/Airspace.png
--------------------------------------------------------------------------------
/ember/public/images/layers/Bing Road.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/layers/Bing Road.png
--------------------------------------------------------------------------------
/ember/public/images/paraglider_symbol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/paraglider_symbol.png
--------------------------------------------------------------------------------
/ember/translations/hu.yaml:
--------------------------------------------------------------------------------
1 | avg-speed-abbr: Átl. sebesség
2 | avg-vario-abbr: Átl. vario
3 | duration: Időtartam
4 | glide-rate: Siklószám
5 |
--------------------------------------------------------------------------------
/ember/translations/sv.yaml:
--------------------------------------------------------------------------------
1 | avg-speed-abbr: Snitthastighet
2 | avg-vario-abbr: Snittvario
3 | duration: Varaktighet
4 | glide-rate: Glidtal
5 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from skylines.commands import manager
4 |
5 | if __name__ == "__main__":
6 | manager.run()
7 |
--------------------------------------------------------------------------------
/ember/app/templates/users/new.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/ember/public/images/glider_symbol_msie.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/glider_symbol_msie.png
--------------------------------------------------------------------------------
/ember/public/images/motorglider_symbol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/motorglider_symbol.png
--------------------------------------------------------------------------------
/ember/app/controllers/flights.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class FlightsController extends Controller {}
4 |
--------------------------------------------------------------------------------
/ember/app/helpers/lowercase.js:
--------------------------------------------------------------------------------
1 | import { helper } from '@ember/component/helper';
2 |
3 | export default helper(([value]) => value?.toLowerCase());
4 |
--------------------------------------------------------------------------------
/ember/public/images/layers/Bing Satellite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/layers/Bing Satellite.png
--------------------------------------------------------------------------------
/ember/public/images/layers/Google Physical.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/layers/Google Physical.png
--------------------------------------------------------------------------------
/ember/public/images/layers/OpenStreetMap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/layers/OpenStreetMap.png
--------------------------------------------------------------------------------
/ember/public/images/layers/Shaded Relief.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/layers/Shaded Relief.png
--------------------------------------------------------------------------------
/mapserver/fonts/truetype.txt:
--------------------------------------------------------------------------------
1 | sans /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
2 | serif /usr/share/fonts/truetype/dejavu/DejaVuSans-Oblique.ttf
3 |
--------------------------------------------------------------------------------
/skylines/lib/xcsoar_/__init__.py:
--------------------------------------------------------------------------------
1 | # flake8: noqa
2 |
3 | from .analysis import analyse_flight
4 | from .flightpath import flight_path, FlightPathFix
5 |
--------------------------------------------------------------------------------
/skylines/schemas/__init__.py:
--------------------------------------------------------------------------------
1 | from marshmallow import ValidationError
2 |
3 | from .schemas import * # NOQA
4 |
5 | __all__ = ("ValidationError",)
6 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | .cache/
2 | .coverage
3 | .envrc
4 | .git/
5 | .github/
6 | .idea/
7 | .local/
8 | .vagrant/
9 | ember/node_modules/
10 | htdocs/
11 |
--------------------------------------------------------------------------------
/ember/mirage/models/user.js:
--------------------------------------------------------------------------------
1 | import { Model, belongsTo } from 'ember-cli-mirage';
2 |
3 | export default Model.extend({
4 | club: belongsTo(),
5 | });
6 |
--------------------------------------------------------------------------------
/ember/public/images/layers/Google Satellite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/layers/Google Satellite.png
--------------------------------------------------------------------------------
/ember/public/images/layers/Takeoff Locations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/layers/Takeoff Locations.png
--------------------------------------------------------------------------------
/wsgi_skylines.py:
--------------------------------------------------------------------------------
1 | import config
2 |
3 | config.to_envvar()
4 |
5 | from skylines import create_combined_app
6 |
7 | application = create_combined_app()
8 |
--------------------------------------------------------------------------------
/ember/app/components/flight-path-layer.hbs:
--------------------------------------------------------------------------------
1 | {{#each @flights as |flight|}}
2 |
3 | {{/each}}
--------------------------------------------------------------------------------
/ember/app/components/takeoffs-layer.hbs:
--------------------------------------------------------------------------------
1 | {{#each locations as |location|}}
2 |
3 | {{/each}}
--------------------------------------------------------------------------------
/ember/app/helpers/to-iso-string.js:
--------------------------------------------------------------------------------
1 | import { helper } from '@ember/component/helper';
2 |
3 | export default helper(([date]) => new Date(date).toISOString());
4 |
--------------------------------------------------------------------------------
/ember/app/templates/login.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ember/mirage/models/club.js:
--------------------------------------------------------------------------------
1 | import { Model, belongsTo } from 'ember-cli-mirage';
2 |
3 | export default Model.extend({
4 | owner: belongsTo('user'),
5 | });
6 |
--------------------------------------------------------------------------------
/tests/api/views/conftest.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | @pytest.fixture(scope="function", autouse=True)
5 | def autouse_db_session(db_session):
6 | pass
7 |
--------------------------------------------------------------------------------
/ember/app/components/base-map.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ember/app/components/nav-bar.js:
--------------------------------------------------------------------------------
1 | import Component from '@glimmer/component';
2 |
3 | export default class NavBar extends Component {
4 | collapsed = true;
5 | }
6 |
--------------------------------------------------------------------------------
/ember/app/components/search-result-row.module.scss:
--------------------------------------------------------------------------------
1 | .column-icon {
2 | width: 1px;
3 | text-align: center;
4 | }
5 |
6 | .column-type {
7 | width: 70px;
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/services/search-text.js:
--------------------------------------------------------------------------------
1 | import Service from '@ember/service';
2 |
3 | export default class SearchTextService extends Service {
4 | text = null;
5 | }
6 |
--------------------------------------------------------------------------------
/ember/public/images/layers/Mountain Wave Project.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skylines-project/skylines/HEAD/ember/public/images/layers/Mountain Wave Project.png
--------------------------------------------------------------------------------
/ember/app/components/wingman-row.module.scss:
--------------------------------------------------------------------------------
1 | .wingman-row:hover {
2 | background-color: #eeeeee;
3 | }
4 |
5 | .color-stripe {
6 | width: 3px;
7 | padding: 0;
8 | }
9 |
--------------------------------------------------------------------------------
/ember/config/optional-features.json:
--------------------------------------------------------------------------------
1 | {
2 | "application-template-wrapper": false,
3 | "jquery-integration": false,
4 | "template-only-glimmer-components": true
5 | }
6 |
--------------------------------------------------------------------------------
/ember/translations/fi.yaml:
--------------------------------------------------------------------------------
1 | avg-speed-abbr: Keskinopeus
2 | avg-vario-abbr: Keskinosto
3 | duration: Kesto
4 | glide-rate: Liitosuhde
5 | glide-rate-abbr: GR
6 | start: Lähtö
7 |
--------------------------------------------------------------------------------
/ember/mirage/route-handlers/notifications.js:
--------------------------------------------------------------------------------
1 | export function register(server) {
2 | // TODO implement this properly
3 | server.get('/api/notifications', { events: [] });
4 | }
5 |
--------------------------------------------------------------------------------
/ember/translations/cs.yaml:
--------------------------------------------------------------------------------
1 | avg-speed-abbr: Pr. rychlost
2 | avg-vario-abbr: Vário průměr
3 | duration: Doba
4 | glide-rate: Klouzavost
5 | glide-rate-abbr: Klouz.
6 | start: Začátek
7 |
--------------------------------------------------------------------------------
/ember/app/routes/flights/all.js:
--------------------------------------------------------------------------------
1 | import BaseRoute from './-base';
2 |
3 | export default class AllRoute extends BaseRoute {
4 | getURL() {
5 | return '/api/flights/all';
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/styles/application.module.scss:
--------------------------------------------------------------------------------
1 | :root {
2 | --ecn-container-position: 60px;
3 | }
4 |
5 | :global(.c-notification) {
6 | box-shadow: 0 5px 15px hsla(0, 0%, 0%, 0.2);
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/styles/bootstrap/_page-header.scss:
--------------------------------------------------------------------------------
1 | .page-header {
2 | position: relative;
3 | padding: 0;
4 | margin: 10px 0;
5 |
6 | h1 {
7 | margin-top: 0;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ember/app/components/loading-page.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/components/sidebar/tabs.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{yield (hash Tab=(component "sidebar/tab" selectedTab=@selectedTab onSelect=@onSelect))}}
3 |
--------------------------------------------------------------------------------
/ember/app/components/timeline.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#each @events as |event|}}
3 | -
4 |
5 |
6 | {{/each}}
7 |
--------------------------------------------------------------------------------
/ember/app/components/x-pager.module.scss:
--------------------------------------------------------------------------------
1 | .pagination {
2 | composes: pagination from global;
3 |
4 | margin: 0;
5 |
6 | > li > a {
7 | line-height: 22px;
8 | }
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/ember/app/controllers/login.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class LoginController extends Controller {
4 | queryParams = ['next'];
5 | next = null;
6 | }
7 |
--------------------------------------------------------------------------------
/ember/app/routes/flights/latest.js:
--------------------------------------------------------------------------------
1 | import BaseRoute from './-base';
2 |
3 | export default class LatestRoute extends BaseRoute {
4 | getURL() {
5 | return '/api/flights/latest';
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/routes/statistics/index.js:
--------------------------------------------------------------------------------
1 | import BaseRoute from './-base';
2 |
3 | export default class IndexRoute extends BaseRoute {
4 | getURL() {
5 | return '/api/statistics/';
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/utils/iso-date.js:
--------------------------------------------------------------------------------
1 | import pad from './pad';
2 |
3 | export default function isoDate(date) {
4 | return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
5 | }
6 |
--------------------------------------------------------------------------------
/ember/app/controllers/ranking/clubs.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class ClubsController extends Controller {
4 | queryParams = ['page'];
5 | page = 1;
6 | }
7 |
--------------------------------------------------------------------------------
/ember/app/controllers/ranking/pilots.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class PilotsController extends Controller {
4 | queryParams = ['page'];
5 | page = 1;
6 | }
7 |
--------------------------------------------------------------------------------
/ember/app/controllers/users/recover.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class RecoverController extends Controller {
4 | queryParams = ['key'];
5 | key = null;
6 | }
7 |
--------------------------------------------------------------------------------
/ember/lib/freestyle/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | 'use strict';
3 |
4 | module.exports = {
5 | name: 'freestyle',
6 |
7 | isDevelopingAddon() {
8 | return true;
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/ember/app/components/flight-list-row.module.scss:
--------------------------------------------------------------------------------
1 | .row {
2 | &:hover {
3 | .pin {
4 | visibility: visible;
5 | }
6 | }
7 | }
8 |
9 | .pin {
10 | visibility: hidden;
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/ember/app/controllers/ranking.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default Controller.extend({
4 | queryParams: ['year'],
5 | year: new Date().getFullYear().toString(),
6 | });
7 |
--------------------------------------------------------------------------------
/ember/app/controllers/ranking/airports.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class AirportsController extends Controller {
4 | queryParams = ['page'];
5 | page = 1;
6 | }
7 |
--------------------------------------------------------------------------------
/ember/app/helpers/sum.js:
--------------------------------------------------------------------------------
1 | import { helper } from '@ember/component/helper';
2 |
3 | export function sum(numbers) {
4 | return numbers.reduce((a, b) => a + b, 0);
5 | }
6 |
7 | export default helper(sum);
8 |
--------------------------------------------------------------------------------
/ember/app/routes/flights/date.js:
--------------------------------------------------------------------------------
1 | import BaseRoute from './-base';
2 |
3 | export default class DateRoute extends BaseRoute {
4 | getURL({ date }) {
5 | return `/api/flights/date/${date}`;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/routes/flights/index.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 |
3 | export default class IndexRoute extends Route {
4 | redirect() {
5 | this.replaceWith('flights.latest');
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/routes/flights/list.js:
--------------------------------------------------------------------------------
1 | import BaseRoute from './-base';
2 |
3 | export default class ListRoute extends BaseRoute {
4 | getURL({ list }) {
5 | return `/api/flights/list/${list}`;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/routes/ranking/index.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 |
3 | export default class IndexRoute extends Route {
4 | redirect() {
5 | this.replaceWith('ranking.clubs');
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/routes/settings/profile.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 |
3 | export default class ProfileRoute extends Route {
4 | model() {
5 | return this.modelFor('settings');
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/config/targets.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* eslint-env node */
4 |
5 | module.exports = {
6 | browsers: ['ie 9', 'last 1 Chrome versions', 'last 1 Firefox versions', 'last 1 Safari versions'],
7 | };
8 |
--------------------------------------------------------------------------------
/ember/app/routes/flights/club.js:
--------------------------------------------------------------------------------
1 | import BaseRoute from './-base';
2 |
3 | export default class ClubRoute extends BaseRoute {
4 | getURL({ club_id }) {
5 | return `/api/flights/club/${club_id}`;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/routes/flights/unassigned.js:
--------------------------------------------------------------------------------
1 | import BaseRoute from './-base';
2 |
3 | export default class UnassignedRoute extends BaseRoute {
4 | getURL() {
5 | return '/api/flights/unassigned';
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/routes/settings/index.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 |
3 | export default class IndexRoute extends Route {
4 | redirect() {
5 | this.replaceWith('settings.profile');
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/routes/settings/tracking.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 |
3 | export default class TrackingRoute extends Route {
4 | model() {
5 | return this.modelFor('settings');
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/components/fullscreen-button.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/components/notifications-badge.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{this.notificationCounter.counterText}}
3 |
--------------------------------------------------------------------------------
/ember/app/components/progress-bar.js:
--------------------------------------------------------------------------------
1 | import { inject as service } from '@ember/service';
2 | import Component from '@glimmer/component';
3 |
4 | export default class extends Component {
5 | @service progress;
6 | }
7 |
--------------------------------------------------------------------------------
/ember/app/components/validated-input.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 |
3 | export default class ValidatedInput extends Component {
4 | tagName = '';
5 |
6 | type = 'text';
7 | value = null;
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/helpers/includes.js:
--------------------------------------------------------------------------------
1 | import { helper } from '@ember/component/helper';
2 |
3 | export function includes([array, value]) {
4 | return array.includes(value);
5 | }
6 |
7 | export default helper(includes);
8 |
--------------------------------------------------------------------------------
/ember/app/routes/flight/map-redirect.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 |
3 | export default class MapRedirectRoute extends Route {
4 | redirect() {
5 | this.replaceWith('flight.index');
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/routes/flights/pilot.js:
--------------------------------------------------------------------------------
1 | import BaseRoute from './-base';
2 |
3 | export default class PilotRoute extends BaseRoute {
4 | getURL({ pilot_id }) {
5 | return `/api/flights/pilot/${pilot_id}`;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/templates/users/recover.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#if key}}
3 |
4 | {{else}}
5 |
6 | {{/if}}
7 |
--------------------------------------------------------------------------------
/ember/app/components/plane-label-overlays.hbs:
--------------------------------------------------------------------------------
1 | {{#each @fixes as |fix|}}
2 | {{#if fix.coordinate}}
3 |
4 | {{/if}}
5 | {{/each}}
6 |
--------------------------------------------------------------------------------
/ember/app/helpers/multiply.js:
--------------------------------------------------------------------------------
1 | import { helper } from '@ember/component/helper';
2 |
3 | export function multiply(numbers) {
4 | return numbers.reduce((a, b) => a * b, 1);
5 | }
6 |
7 | export default helper(multiply);
8 |
--------------------------------------------------------------------------------
/ember/app/helpers/percent.js:
--------------------------------------------------------------------------------
1 | import { helper } from '@ember/component/helper';
2 |
3 | export function percent([value, max]) {
4 | return Math.round((value * 100) / max);
5 | }
6 |
7 | export default helper(percent);
8 |
--------------------------------------------------------------------------------
/ember/app/routes/statistics/club.js:
--------------------------------------------------------------------------------
1 | import BaseRoute from './-base';
2 |
3 | export default class ClubRoute extends BaseRoute {
4 | getURL({ club_id }) {
5 | return `/api/statistics/club/${club_id}`;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "skylines",
3 | "dependencies": {
4 | "Flot": "flot#0.8.3",
5 | "flot-marks": "https://github.com/TobiasLohner/flot-marks.git#f09ded70f5a229a38ba0b9cfa92dbb448ca4daaf"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/lib/freestyle/app/templates/components/usage/x-flag.hbs:
--------------------------------------------------------------------------------
1 | {{#freestyle-usage "x-flag"}}
2 |
3 |
4 |
5 |
6 | {{/freestyle-usage}}
7 |
--------------------------------------------------------------------------------
/ember/app/components/cesium-plane-models.hbs:
--------------------------------------------------------------------------------
1 | {{#each @fixes as |fix|}}
2 | {{#if fix.coordinate}}
3 |
4 | {{/if}}
5 | {{/each}}
--------------------------------------------------------------------------------
/ember/app/routes/flights/airport.js:
--------------------------------------------------------------------------------
1 | import BaseRoute from './-base';
2 |
3 | export default class AirportRoute extends BaseRoute {
4 | getURL({ airport_id }) {
5 | return `/api/flights/airport/${airport_id}`;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/routes/statistics/pilot.js:
--------------------------------------------------------------------------------
1 | import BaseRoute from './-base';
2 |
3 | export default class PilotRoute extends BaseRoute {
4 | getURL({ pilot_id }) {
5 | return `/api/statistics/pilot/${pilot_id}`;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/components/flight-phase-table.module.scss:
--------------------------------------------------------------------------------
1 | .table {
2 | composes: table table-condensed table-striped from global;
3 |
4 | font-size: 85%;
5 |
6 | thead tr th {
7 | vertical-align: top;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ember/app/components/settings-page.js:
--------------------------------------------------------------------------------
1 | import { inject as service } from '@ember/service';
2 | import Component from '@glimmer/component';
3 |
4 | export default class SettingsPage extends Component {
5 | @service account;
6 | }
7 |
--------------------------------------------------------------------------------
/ember/app/controllers/settings/club.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class ClubController extends Controller {
5 | @service account;
6 | }
7 |
--------------------------------------------------------------------------------
/ember/mirage/factories/club.js:
--------------------------------------------------------------------------------
1 | import { Factory } from 'ember-cli-mirage';
2 |
3 | export default Factory.extend({
4 | name: 'AeroClub Aachen',
5 | timeCreated: '2020-05-24T21:41:03Z',
6 | website: 'https://acac.aero/',
7 | });
8 |
--------------------------------------------------------------------------------
/ember/app/components/flight-phase-table.js:
--------------------------------------------------------------------------------
1 | import { inject as service } from '@ember/service';
2 | import Component from '@glimmer/component';
3 |
4 | export default class FlightPhaseTable extends Component {
5 | @service units;
6 | }
7 |
--------------------------------------------------------------------------------
/ember/app/components/follower-panel.module.scss:
--------------------------------------------------------------------------------
1 | .follower-panel a {
2 | color: #333;
3 | }
4 |
5 | .follower-panel, .follower-panel h4 {
6 | overflow: hidden;
7 | white-space: nowrap;
8 | text-overflow: ellipsis;
9 | }
10 |
--------------------------------------------------------------------------------
/ember/app/controllers/statistics.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class StatisticsController extends Controller {
5 | @service account;
6 | }
7 |
--------------------------------------------------------------------------------
/ember/app/routes/ranking.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 |
3 | export default class RankingRoute extends Route {
4 | queryParams = {
5 | year: { refreshModel: true },
6 | };
7 |
8 | model() {}
9 | }
10 |
--------------------------------------------------------------------------------
/ember/app/routes/statistics/airport.js:
--------------------------------------------------------------------------------
1 | import BaseRoute from './-base';
2 |
3 | export default class AirportRoute extends BaseRoute {
4 | getURL({ airport_id }) {
5 | return `/api/statistics/airport/${airport_id}`;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/templates/settings/club.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ember/app/utils/add-days.js:
--------------------------------------------------------------------------------
1 | import isoDate from './iso-date';
2 |
3 | export default function addDays(date, days = 0) {
4 | let result = new Date(date);
5 | result.setDate(result.getDate() + days);
6 | return isoDate(result);
7 | }
8 |
--------------------------------------------------------------------------------
/ember/translations/ru.yaml:
--------------------------------------------------------------------------------
1 | avg-speed-abbr: Ср. скорость
2 | avg-vario-abbr: Ср. подъем
3 | duration: Длительность
4 | glide-rate: Аэродинамическое качество (относительно земли)
5 | glide-rate-abbr: AK
6 | speed: Скорость
7 | start: Старт
8 |
--------------------------------------------------------------------------------
/airspace/airspace_blacklist.txt:
--------------------------------------------------------------------------------
1 | at FIR LOVV
2 | cz CZ Border (126.1 W, 136.175 E Praha Information)
3 | nl FIR EHAA
4 | no Old Comp Area with Swedish border
5 | pl FIS Gdansk
6 | pl FIS Krakow
7 | pl FIS Olsztyn
8 | pl FIS Poznan
9 | pl FIS Warszawa
--------------------------------------------------------------------------------
/ember/app/modifiers/set-map-target.js:
--------------------------------------------------------------------------------
1 | import { modifier } from 'ember-modifier';
2 |
3 | export default modifier((element, [map]) => {
4 | if (map) {
5 | map.setTarget(element);
6 | return () => map.setTarget(null);
7 | }
8 | });
9 |
--------------------------------------------------------------------------------
/wsgi_mapproxy.py:
--------------------------------------------------------------------------------
1 | import os
2 | from mapproxy.wsgiapp import make_wsgi_app
3 |
4 | config_file = os.path.join(
5 | os.path.dirname(os.path.abspath(__file__)), "mapproxy", "mapproxy.yaml"
6 | )
7 | application = make_wsgi_app(config_file)
8 |
--------------------------------------------------------------------------------
/ember/app/components/flight-details-table.module.scss:
--------------------------------------------------------------------------------
1 | .weglide-button {
2 | composes: btn btn-default from global;
3 |
4 | .logo {
5 | height: 1em;
6 | transform: translate(0, -2px);
7 | margin-right: 3px;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ember/app/components/follower-list.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#each @followers as |follower|}}
3 | -
4 |
5 |
6 | {{/each}}
7 |
8 |
--------------------------------------------------------------------------------
/ember/app/controllers/flight/index.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class IndexController extends Controller {
4 | queryParams = ['baselayer', 'overlays'];
5 | baselayer = null;
6 | overlays = null;
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/routes/tracking/map-redirect.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 |
3 | export default class MapRedirectRoute extends Route {
4 | redirect({ user_ids }) {
5 | this.transitionTo('tracking.details', user_ids);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/components/flight-performance-panel.hbs:
--------------------------------------------------------------------------------
1 | {{t "circling-performance"}}
2 |
3 |
4 | {{t "cruise-performance"}}
5 |
--------------------------------------------------------------------------------
/ember/mirage/scenarios/default.js:
--------------------------------------------------------------------------------
1 | export default function (/* server */) {
2 | /*
3 | Seed your development database using your factories.
4 | This data will not be loaded in your tests.
5 | */
6 | // server.createList('post', 10);
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/components/plane-label-overlay.hbs:
--------------------------------------------------------------------------------
1 |
7 | {{or @flight.competition_id @flight.registration}}
8 |
--------------------------------------------------------------------------------
/ember/app/components/season-dropdown.js:
--------------------------------------------------------------------------------
1 | import Component from '@glimmer/component';
2 |
3 | const YEAR = new Date().getFullYear();
4 |
5 | export default class SeasonDropdown extends Component {
6 | recentYears = [0, 1, 2, 3, 4].map(i => YEAR - i);
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/helpers/truncate.js:
--------------------------------------------------------------------------------
1 | import { helper } from '@ember/component/helper';
2 |
3 | export function truncate([text, length]) {
4 | return text.length <= length ? text : `${text.slice(0, length - 3)}...`;
5 | }
6 |
7 | export default helper(truncate);
8 |
--------------------------------------------------------------------------------
/ember/app/components/nav-bar-search-form.js:
--------------------------------------------------------------------------------
1 | import { inject as service } from '@ember/service';
2 | import Component from '@glimmer/component';
3 |
4 | export default class NavBarSearchForm extends Component {
5 | @service('searchText') searchTextService;
6 | }
7 |
--------------------------------------------------------------------------------
/ember/app/controllers/flights/all.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class AllController extends Controller {
4 | queryParams = ['page', 'column', 'order'];
5 | page = 1;
6 | column = 'date';
7 | order = 'desc';
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/controllers/flights/club.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class ClubController extends Controller {
4 | queryParams = ['page', 'column', 'order'];
5 | page = 1;
6 | column = 'date';
7 | order = 'desc';
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/controllers/flights/date.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class DateController extends Controller {
4 | queryParams = ['page', 'column', 'order'];
5 | page = 1;
6 | column = 'score';
7 | order = 'desc';
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/controllers/flights/list.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class ListController extends Controller {
4 | queryParams = ['page', 'column', 'order'];
5 | page = 1;
6 | column = 'date';
7 | order = 'desc';
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/helpers/html-safe.js:
--------------------------------------------------------------------------------
1 | import { helper } from '@ember/component/helper';
2 | import { htmlSafe as _htmlSafe } from '@ember/string';
3 |
4 | export function htmlSafe([text]) {
5 | return _htmlSafe(text);
6 | }
7 |
8 | export default helper(htmlSafe);
9 |
--------------------------------------------------------------------------------
/ember/app/routes/flight.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 |
3 | export default class FlightRoute extends Route {
4 | model(params) {
5 | let ids = params.flight_ids.split(',').map(it => parseInt(it, 10));
6 | return { ids };
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/skylines/frontend/templates/error.jinja:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ code }} {{ name }}
5 |
6 |
7 | {{ name }}
8 | {% if description %}{{ description }}
{% endif %}
9 |
10 |
11 |
--------------------------------------------------------------------------------
/config/testing.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from tempfile import mkdtemp
4 |
5 | TESTING = True
6 |
7 | SQLALCHEMY_DATABASE_URI = "postgresql:///skylines_test"
8 | SQLALCHEMY_ECHO = False
9 | SKYLINES_FILES_PATH = mkdtemp(suffix="skylines-uploads")
10 |
--------------------------------------------------------------------------------
/ember/app/components/upload-result-form.module.scss:
--------------------------------------------------------------------------------
1 | .upload-result-form {
2 | :global(.ember-flatpickr-input.form-control[readonly]) {
3 | background: white;
4 | }
5 | }
6 |
7 | .barogram {
8 | height: 160px;
9 | margin: 10px 0 15px 15px;
10 | }
11 |
--------------------------------------------------------------------------------
/ember/app/computed/is-none.js:
--------------------------------------------------------------------------------
1 | import { computed } from '@ember/object';
2 | import { isNone as _isNone } from '@ember/utils';
3 |
4 | export default function isNone(key) {
5 | return computed(key, function () {
6 | return _isNone(this.get(key));
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/controllers/flights/airport.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class AirportController extends Controller {
4 | queryParams = ['page', 'column', 'order'];
5 | page = 1;
6 | column = 'date';
7 | order = 'desc';
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/controllers/flights/latest.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class LatestController extends Controller {
4 | queryParams = ['page', 'column', 'order'];
5 | page = 1;
6 | column = 'score';
7 | order = 'desc';
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/controllers/flights/pilot.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class PilotController extends Controller {
4 | queryParams = ['page', 'column', 'order'];
5 | page = 1;
6 | column = 'date';
7 | order = 'desc';
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/controllers/flights/pinned.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class PinnedController extends Controller {
4 | queryParams = ['page', 'column', 'order'];
5 | page = 1;
6 | column = 'date';
7 | order = 'desc';
8 | }
9 |
--------------------------------------------------------------------------------
/skylines/commands/notifications/__init__.py:
--------------------------------------------------------------------------------
1 | from flask_script import Manager
2 |
3 | from .mark_all_unread import MarkAllUnread
4 |
5 | manager = Manager(help="Perform operations related to notifications")
6 | manager.add_command("mark-all-unread", MarkAllUnread())
7 |
--------------------------------------------------------------------------------
/ember/app/styles/app.scss:
--------------------------------------------------------------------------------
1 | @import '_power-select';
2 |
3 | @import 'bootstrap';
4 | @import 'flatpickr';
5 | @import 'font-awesome';
6 |
7 | @import 'tables';
8 | @import 'map';
9 | @import 'spinner';
10 |
11 | // ember-css-modules
12 | @import 'css-modules';
13 |
--------------------------------------------------------------------------------
/ember/app/templates/application.hbs:
--------------------------------------------------------------------------------
1 | {{page-title "SkyLines"}}
2 |
3 |
4 |
5 |
6 | {{#if onFreestyleRoute}}
7 | {{outlet}}
8 | {{else}}
9 |
10 | {{outlet}}
11 | {{/if}}
--------------------------------------------------------------------------------
/ember/app/templates/settings/tracking.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ember/app/components/base-barogram.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{did-update this.setCrosshair this.crosshair}}
3 | {{did-update this.setGridMarkings this.gridMarkings}}
4 | {{did-update this.setXAxis this.xaxis}}
--------------------------------------------------------------------------------
/ember/app/components/notifications-badge.js:
--------------------------------------------------------------------------------
1 | import { inject as service } from '@ember/service';
2 | import Component from '@glimmer/component';
3 |
4 | export default class NotificationsBadge extends Component {
5 | @service notificationCounter;
6 | @service intl;
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/components/sidebar/tabs.module.scss:
--------------------------------------------------------------------------------
1 | .tabs {
2 | position: absolute;
3 | top: 0;
4 | bottom: 0;
5 | width: 40px;
6 | height: 100%;
7 | margin: 0;
8 | padding: 0;
9 | background-color: rgba(255, 255, 255, 0.5);
10 | overflow: hidden;
11 | }
12 |
--------------------------------------------------------------------------------
/ember/app/controllers/flights/unassigned.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default class UnassignedController extends Controller {
4 | queryParams = ['page', 'column', 'order'];
5 | page = 1;
6 | column = 'date';
7 | order = 'desc';
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/templates/error.hbs:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
{{t "error-text"}}
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ember/app/templates/flight/change-pilot.hbs:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/mirage/factories/mirage-session.js:
--------------------------------------------------------------------------------
1 | import { Factory } from 'ember-cli-mirage';
2 |
3 | export default Factory.extend({
4 | afterCreate(session) {
5 | if (!session.user) {
6 | throw new Error('Missing `user` relationship');
7 | }
8 | },
9 | });
10 |
--------------------------------------------------------------------------------
/ember/app/components/cesium-button.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/components/pin-button.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#if this.pinned}}
3 | {{svg-jar "star" class="fa-svg large"}} {{t "flight-pinned"}}
4 | {{else}}
5 | {{svg-jar "star-o" class="fa-svg large"}} {{t "flight-unpinned"}}
6 | {{/if}}
7 |
--------------------------------------------------------------------------------
/ember/app/templates/statistics/pilot.hbs:
--------------------------------------------------------------------------------
1 | {{t "flights"}}
2 |
3 |
4 | {{t "distance"}}
5 |
6 |
7 | {{t "flight-time"}}
8 |
--------------------------------------------------------------------------------
/ember/app/components/takeoffs-map.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ember/app/components/flight-leg-panel.hbs:
--------------------------------------------------------------------------------
1 | {{t "distance"}}
2 |
3 |
4 | {{t "triangle"}}
5 |
--------------------------------------------------------------------------------
/ember/app/templates/about/team.hbs:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 | {{markdown text}}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ember/tests/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parserOptions: {
3 | ecmaVersion: 2017,
4 | },
5 | extends: ['simplabs/configs/ember-qunit', 'prettier'],
6 | env: {
7 | embertest: null,
8 | },
9 | rules: {
10 | 'prettier/prettier': 'error',
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/skylines/commands/users/__init__.py:
--------------------------------------------------------------------------------
1 | from flask_script import Manager
2 |
3 | from .merge import Merge
4 | from .email import Email
5 |
6 | manager = Manager(help="Perform operations related to user accounts")
7 | manager.add_command("email", Email())
8 | manager.add_command("merge", Merge())
9 |
--------------------------------------------------------------------------------
/ember/app/components/playback-button.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/controllers/users/new.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 | import { action } from '@ember/object';
3 |
4 | export default class NewController extends Controller {
5 | @action
6 | transitionTo(...args) {
7 | this.transitionToRoute(...args);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ember/app/routes/about/team.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class TeamRoute extends Route {
5 | @service ajax;
6 |
7 | model() {
8 | return this.ajax.request('/api/team');
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ember/app/routes/club/index.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 |
3 | export default class IndexRoute extends Route {
4 | setupController(controller) {
5 | super.setupController(...arguments);
6 | controller.set('club', this.controllerFor('club').get('model'));
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/routes/clubs.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class ClubsRoute extends Route {
5 | @service ajax;
6 |
7 | model() {
8 | return this.ajax.request('/api/clubs/');
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ember/app/templates/about/imprint.hbs:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 | {{markdown model.content}}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ember/app/templates/about/license.hbs:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
{{model.content}}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ember/app/routes/settings/club.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class ClubRoute extends Route {
5 | @service ajax;
6 |
7 | model() {
8 | return this.ajax.request('/api/clubs');
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ember/app/routes/tracking/index.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class IndexRoute extends Route {
5 | @service ajax;
6 |
7 | model() {
8 | return this.ajax.request('/api/live');
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ember/app/routes/users/index.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class IndexRoute extends Route {
5 | @service ajax;
6 |
7 | model() {
8 | return this.ajax.request('/api/users');
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ember/app/components/sidebar.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{yield (hash
3 | Tabs=(component "sidebar/tabs" selectedTab=this.selectedTab onSelect=this.onSelect)
4 | Panel=(component "sidebar/panel" visibleTab=this.visibleTab)
5 | )}}
6 |
--------------------------------------------------------------------------------
/ember/app/components/sidebar/panel.hbs:
--------------------------------------------------------------------------------
1 | {{#if (eq @visibleTab @id)}}
2 |
3 |
4 |
{{@title}}
5 |
6 | {{yield}}
7 |
8 |
9 |
10 | {{/if}}
--------------------------------------------------------------------------------
/ember/app/components/sidebar/tab.hbs:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/ember/app/components/timeline-event.js:
--------------------------------------------------------------------------------
1 | import Component from '@glimmer/component';
2 |
3 | import computedComponent from '../computed/computed-component';
4 |
5 | export default class TimelineEvent extends Component {
6 | @computedComponent('args.event.type', 'timeline-events/') eventComponent;
7 | }
8 |
--------------------------------------------------------------------------------
/ember/app/controllers/flight-upload.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 | import { action } from '@ember/object';
3 |
4 | export default class FlightUploadController extends Controller {
5 | @action
6 | transitionTo(...args) {
7 | this.transitionToRoute(...args);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ember/app/routes/about/imprint.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class ImprintRoute extends Route {
5 | @service ajax;
6 |
7 | model() {
8 | return this.ajax.request('/api/imprint');
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ember/app/routes/about/license.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class LicenseRoute extends Route {
5 | @service ajax;
6 |
7 | model() {
8 | return this.ajax.request('/api/license');
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/docker/docker-entrypoint-initdb.d/create-databases.sql:
--------------------------------------------------------------------------------
1 | CREATE DATABASE skylines;
2 | \connect skylines;
3 | CREATE EXTENSION postgis;
4 | CREATE EXTENSION fuzzystrmatch;
5 |
6 | CREATE DATABASE skylines_test;
7 | \connect skylines_test;
8 | CREATE EXTENSION postgis;
9 | CREATE EXTENSION fuzzystrmatch;
10 |
--------------------------------------------------------------------------------
/ember/app/components/follower-page.hbs:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/ember/app/components/following-page.hbs:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/ember/app/routes/club.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class ClubRoute extends Route {
5 | @service ajax;
6 |
7 | model({ club_id }) {
8 | return this.ajax.request(`/api/clubs/${club_id}`);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ember/mirage/utils/session.js:
--------------------------------------------------------------------------------
1 | export function getSession(schema) {
2 | let session = schema.mirageSessions.first();
3 | if (!session || Date.parse(session.expires) < Date.now()) {
4 | return {};
5 | }
6 |
7 | let user = schema.users.find(session.userId);
8 | return { session, user };
9 | }
10 |
--------------------------------------------------------------------------------
/ember/app/components/pin-star.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{svg-jar (if this.pinned "star" "star-o") class="fa-svg"}}
3 | Activate this to show the flight on top of other flights on the map
4 |
--------------------------------------------------------------------------------
/ember/app/components/stats-flights-table.js:
--------------------------------------------------------------------------------
1 | import { mapBy, max, sum } from '@ember/object/computed';
2 | import Component from '@glimmer/component';
3 |
4 | export default class StatsFlightsTable extends Component {
5 | @mapBy('args.years', 'flights') flights;
6 | @max('flights') max;
7 | @sum('flights') sum;
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/routes/user.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class UserRoute extends Route {
5 | @service ajax;
6 |
7 | model({ user_id }) {
8 | return this.ajax.request(`/api/users/${user_id}?extended`);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/skylines/lib/md5.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import hashlib
4 |
5 |
6 | def file_md5(f):
7 | """Return the hex MD5 digest of a file-like object."""
8 |
9 | md5 = hashlib.md5()
10 | for chunk in iter(lambda: f.read(8192), b""):
11 | md5.update(chunk)
12 | return md5.hexdigest()
13 |
--------------------------------------------------------------------------------
/ember/app/components/login-form.module.scss:
--------------------------------------------------------------------------------
1 | .form-login {
2 | min-width: 300px;
3 | padding: 0 20px;
4 | margin-bottom: inherit;
5 |
6 |
7 | > div, > button, > a {
8 | margin: 15px 0;
9 | }
10 |
11 | > a {
12 | display: block;
13 | }
14 |
15 | hr {
16 | margin: 8px 0;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/play.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/app/components/stats-distance-table.js:
--------------------------------------------------------------------------------
1 | import { mapBy, max, sum } from '@ember/object/computed';
2 | import Component from '@glimmer/component';
3 |
4 | export default class StatsDistanceTable extends Component {
5 | @mapBy('args.years', 'distance') distances;
6 | @max('distances') max;
7 | @sum('distances') sum;
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/components/stats-duration-table.js:
--------------------------------------------------------------------------------
1 | import { mapBy, max, sum } from '@ember/object/computed';
2 | import Component from '@glimmer/component';
3 |
4 | export default class StatsDurationTable extends Component {
5 | @mapBy('args.years', 'duration') durations;
6 | @max('durations') max;
7 | @sum('durations') sum;
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/routes/tracking/details.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class DetailsRoute extends Route {
5 | @service ajax;
6 |
7 | model({ user_ids }) {
8 | return this.ajax.request(`/api/live/${user_ids}`);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ember/lib/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | node: true,
4 | browser: false,
5 | },
6 | rules: {
7 | // unfortunate workaround for:
8 | // ENOENT: no such file or directory, scandir 'skylines/ember/lib/freestyle/app/app/controllers'
9 | 'prettier/prettier': 'off',
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/minus.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/systemd/celery.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Celery background queue
3 | After=network.target
4 |
5 | [Service]
6 | SyslogIdentifier=celery
7 | WorkingDirectory=/home/skylines/src
8 | ExecStart=/usr/local/bin/pipenv run python manage.py celery runworker
9 | Restart=always
10 |
11 | [Install]
12 | WantedBy=default.target
13 |
--------------------------------------------------------------------------------
/ember/app/components/form-page.hbs:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 | {{yield}}
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ember/app/components/stats-pilots-table.js:
--------------------------------------------------------------------------------
1 | import { mapBy, max, readOnly } from '@ember/object/computed';
2 | import Component from '@glimmer/component';
3 |
4 | export default class StatsPilotsTable extends Component {
5 | @mapBy('args.years', 'pilots') pilots;
6 | @max('pilots') max;
7 | @readOnly('args.sumPilots') sum;
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/controllers/flight/change-pilot.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 | import { action } from '@ember/object';
3 |
4 | export default class ChangePilotController extends Controller {
5 | @action
6 | transitionToFlight() {
7 | this.transitionToRoute('flight', this.get('model.id'));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ember/app/utils/raf.js:
--------------------------------------------------------------------------------
1 | export function nextAnimationFrame() {
2 | let animationFrameId;
3 | let promise = new Promise(resolve => {
4 | animationFrameId = requestAnimationFrame(resolve);
5 | });
6 | promise.__ec_cancel__ = () => {
7 | cancelAnimationFrame(animationFrameId);
8 | };
9 | return promise;
10 | }
11 |
--------------------------------------------------------------------------------
/skylines/__about__.py:
--------------------------------------------------------------------------------
1 | __title__ = "SkyLines"
2 | __summary__ = "Live tracking, flight database and competition framework"
3 | __uri__ = "https://github.com/skylines-project/skylines/"
4 |
5 | __version__ = "0.1.0"
6 |
7 | __author__ = "Tobias Bieniek"
8 | __email__ = "tobias.bieniek@gmx.de"
9 |
10 | __license__ = "AGPLv3"
11 |
--------------------------------------------------------------------------------
/ember/app/controllers/flight/change-aircraft.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 | import { action } from '@ember/object';
3 |
4 | export default class ChangeAircraftController extends Controller {
5 | @action
6 | transitionToFlight() {
7 | this.transitionToRoute('flight', this.get('model.id'));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ember/app/helpers/format-lift.js:
--------------------------------------------------------------------------------
1 | import { inject as service } from '@ember/service';
2 |
3 | import BaseHelper from 'ember-intl/helpers/-format-base';
4 |
5 | export default class extends BaseHelper {
6 | @service units;
7 |
8 | format(value, options) {
9 | return this.units.formatLift(value, options);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ember/app/helpers/format-speed.js:
--------------------------------------------------------------------------------
1 | import { inject as service } from '@ember/service';
2 |
3 | import BaseHelper from 'ember-intl/helpers/-format-base';
4 |
5 | export default class extends BaseHelper {
6 | @service units;
7 |
8 | format(value, options) {
9 | return this.units.formatSpeed(value, options);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/caret-down.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/.eslintignore:
--------------------------------------------------------------------------------
1 | /bin/
2 | /bower_components/
3 | /blueprints/**/files/
4 | /common-tmp/
5 | /coverage/
6 | /public/cesium/
7 | /tests/fixtures/
8 | /tmp/
9 | /vendor/
10 |
11 | /lib/broccoli/app-*.js
12 | /lib/broccoli/test-support-*.js
13 | /lib/broccoli/tests-*.js
14 | /lib/broccoli/vendor-*.js
15 |
16 | !.*
17 | /node_modules/
18 |
--------------------------------------------------------------------------------
/ember/app/components/aircraft-model-select.hbs:
--------------------------------------------------------------------------------
1 |
7 | {{#if (eq model.id null)}}
8 | [{{t "unspecified"}}]
9 | {{else}}
10 | {{model.name}}
11 | {{/if}}
12 |
13 |
--------------------------------------------------------------------------------
/ember/app/components/contest-layer.hbs:
--------------------------------------------------------------------------------
1 | {{did-insert this.setVisible @visible}}
2 | {{did-update this.setVisible @visible}}
3 | {{#each @flights as |flight|}}
4 | {{#each flight.contests as |contest|}}
5 | {{#if contest}}
6 |
7 | {{/if}}
8 | {{/each}}
9 | {{/each}}
--------------------------------------------------------------------------------
/ember/app/components/tracking-table.module.scss:
--------------------------------------------------------------------------------
1 | /* make sure the columns have the same width in all tracking tables */
2 |
3 | .tracking-table {
4 | composes: table table-striped from global;
5 |
6 | th {
7 | width: 22.222%;
8 | }
9 |
10 | th:nth-child(4),
11 | th:nth-child(5) {
12 | width: 11.111%;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ember/app/helpers/format-altitude.js:
--------------------------------------------------------------------------------
1 | import { inject as service } from '@ember/service';
2 |
3 | import BaseHelper from 'ember-intl/helpers/-format-base';
4 |
5 | export default class extends BaseHelper {
6 | @service units;
7 |
8 | format(value, options) {
9 | return this.units.formatAltitude(value, options);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ember/app/helpers/format-distance.js:
--------------------------------------------------------------------------------
1 | import { inject as service } from '@ember/service';
2 |
3 | import BaseHelper from 'ember-intl/helpers/-format-base';
4 |
5 | export default class extends BaseHelper {
6 | @service units;
7 |
8 | format(value, options) {
9 | return this.units.formatDistance(value, options);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/systemd/tracking.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=SkyLines Tracking server
3 | After=network.target
4 |
5 | [Service]
6 | SyslogIdentifier=tracking
7 | WorkingDirectory=/home/skylines/src
8 | ExecStart=/usr/local/bin/pipenv run python manage.py tracking runserver
9 | Restart=on-failure
10 |
11 | [Install]
12 | WantedBy=default.target
13 |
--------------------------------------------------------------------------------
/ember/app/components/nav-bar-search-form.module.scss:
--------------------------------------------------------------------------------
1 | .form {
2 | margin: 5px 0;
3 | }
4 |
5 | .input {
6 | composes: form-control from global;
7 | height: 30px;
8 | color: #bbb;
9 | background-color: #626262;
10 | border: 1px solid #333;
11 |
12 | &:focus {
13 | color: #666;
14 | background-color: white;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ember/app/components/tracking-table-row.js:
--------------------------------------------------------------------------------
1 | import Component from '@glimmer/component';
2 |
3 | import safeComputed from '../computed/safe-computed';
4 |
5 | export default class TrackingTableRow extends Component {
6 | @safeComputed('track.altitude', 'track.elevation', (altitude, elevation) => Math.max(altitude - elevation, 0))
7 | altitudeAGL;
8 | }
9 |
--------------------------------------------------------------------------------
/ember/app/helpers/format-hours.js:
--------------------------------------------------------------------------------
1 | import { helper } from '@ember/component/helper';
2 |
3 | import pad from '../utils/pad';
4 |
5 | export function formatHours([value]) {
6 | let h = Math.floor(value / 3600);
7 | let m = Math.floor((value % 3600) / 60);
8 |
9 | return `${h}:${pad(m)}`;
10 | }
11 |
12 | export default helper(formatHours);
13 |
--------------------------------------------------------------------------------
/ember/app/helpers/initials.js:
--------------------------------------------------------------------------------
1 | import { helper } from '@ember/component/helper';
2 |
3 | export function initials([name]) {
4 | let parts = name.split(/\s/);
5 | let initials = parts.filter(it => it.length > 2 && it.indexOf('.') === -1).map(it => it[0].toUpperCase());
6 | return initials.join('');
7 | }
8 |
9 | export default helper(initials);
10 |
--------------------------------------------------------------------------------
/skylines/frontend/views/files.py:
--------------------------------------------------------------------------------
1 | from flask import Blueprint, current_app, send_from_directory
2 |
3 | files_blueprint = Blueprint("files", "skylines")
4 |
5 |
6 | @files_blueprint.route("/files/")
7 | def index(filename):
8 | path = current_app.config.get("SKYLINES_FILES_PATH")
9 | return send_from_directory(path, filename)
10 |
--------------------------------------------------------------------------------
/ember/app/components/cesium-button.js:
--------------------------------------------------------------------------------
1 | import { action } from '@ember/object';
2 | import Component from '@glimmer/component';
3 |
4 | export default class CesiumButton extends Component {
5 | @action toggle() {
6 | if (this.args.enabled) {
7 | this.args.onDisable();
8 | } else {
9 | this.args.onEnable();
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/ember/app/components/nav-bar-link.hbs:
--------------------------------------------------------------------------------
1 | <@nav.item ...attributes>
2 | {{#@nav.link-to @target}}
3 | {{svg-jar @icon class="fa-svg large"}}
4 | {{svg-jar @icon class="fa-svg fixed-width visible-xs-inline-block"}}
5 | {{@title}}
6 | {{/@nav.link-to}}
7 | @nav.item>
--------------------------------------------------------------------------------
/ember/app/components/pilot-select.hbs:
--------------------------------------------------------------------------------
1 |
8 | {{#if (eq pilot.id null)}}
9 | [{{t "unknown-or-other-person"}}]
10 | {{else}}
11 | {{pilot.name}}
12 | {{/if}}
13 |
14 |
--------------------------------------------------------------------------------
/ember/app/helpers/markdown.js:
--------------------------------------------------------------------------------
1 | import { helper } from '@ember/component/helper';
2 | import { htmlSafe } from '@ember/string';
3 |
4 | import { Remarkable } from 'remarkable';
5 |
6 | let remarkable = new Remarkable();
7 |
8 | export function markdown([text]) {
9 | return htmlSafe(remarkable.render(text));
10 | }
11 |
12 | export default helper(markdown);
13 |
--------------------------------------------------------------------------------
/ember/app/templates/flights.hbs:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 | {{outlet}}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ember/app/templates/page-not-found.hbs:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
{{t "page-not-found.text"}}
9 |
URL: /{{model.wildcard}}
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ember/tests/test-helpers/match-json.js:
--------------------------------------------------------------------------------
1 | /* globals QUnit */
2 |
3 | import match from 'match-json';
4 |
5 | export default function registerMatchJsonAssertion() {
6 | QUnit.assert.matchJson = function (value, expected, message) {
7 | let result = match(value, expected);
8 | this.pushResult({ result, actual: value, expected, message });
9 | };
10 | }
11 |
--------------------------------------------------------------------------------
/ember/app/components/timeline-events/new-user.js:
--------------------------------------------------------------------------------
1 | import { computed } from '@ember/object';
2 |
3 | import Base from './-base';
4 |
5 | export default class NewUser extends Base {
6 | @computed('accountUserIsActor')
7 | get translationKey() {
8 | let i = this.accountUserIsActor ? 2 : 1;
9 | return `timeline-events.new-user.message${i}`;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ember/app/components/timeline-events/club-join.js:
--------------------------------------------------------------------------------
1 | import { computed } from '@ember/object';
2 |
3 | import Base from './-base';
4 |
5 | export default class ClubJoin extends Base {
6 | @computed('accountUserIsActor')
7 | get translationKey() {
8 | let i = this.accountUserIsActor ? 2 : 1;
9 | return `timeline-events.club-join.message${i}`;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ember/app/components/tracking-tables.hbs:
--------------------------------------------------------------------------------
1 | {{#if friendsTracks}}
2 | {{#if othersTracks}}
3 | {{t "friends"}}
4 | {{/if}}
5 |
6 | {{/if}}
7 |
8 | {{#if othersTracks}}
9 | {{#if friendsTracks}}
10 | {{t "other-pilots"}}
11 | {{/if}}
12 |
13 | {{/if}}
14 |
--------------------------------------------------------------------------------
/ember/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 |
7 | # dependencies
8 | /node_modules
9 | /bower_components
10 |
11 | # misc
12 | /.sass-cache
13 | /connect.lock
14 | /coverage/*
15 | /libpeerconnection.log
16 | .eslintcache
17 | npm-debug.log*
18 | yarn-error.log
19 | testem.log
20 |
--------------------------------------------------------------------------------
/ember/app/components/base-page.module.scss:
--------------------------------------------------------------------------------
1 | .content {
2 | composes: container from global;
3 | padding-top: 15px;
4 | padding-bottom: 15px;
5 | }
6 |
7 | .footer {
8 | /* center align it with the container */
9 | text-align: center;
10 | margin-top: 5px;
11 | font-size: 85%;
12 |
13 | a {
14 | color: #333;
15 | text-decoration: none;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ember/app/components/layer-switcher-element.js:
--------------------------------------------------------------------------------
1 | import { computed } from '@ember/object';
2 | import Component from '@glimmer/component';
3 |
4 | export default class LayerSwitcherElement extends Component {
5 | highlighted = false;
6 |
7 | @computed('args.visible', 'highlighted')
8 | get dimmed() {
9 | return !this.args.visible && !this.highlighted;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ember/app/components/timeline-events/new-user.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/routes/club/edit.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class EditRoute extends Route {
5 | @service ajax;
6 |
7 | setupController(controller) {
8 | super.setupController(...arguments);
9 | controller.set('club', this.controllerFor('club').get('model'));
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/tests/schemas/fields/test_location.py:
--------------------------------------------------------------------------------
1 | from skylines.schemas import fields, Schema
2 | from skylines.model.geo import Location
3 |
4 |
5 | def test_serialization():
6 | class TestSchema(Schema):
7 | location = fields.Location()
8 |
9 | data = TestSchema().dump(dict(location=Location(longitude=7, latitude=51))).data
10 | assert data["location"] == [7, 51]
11 |
--------------------------------------------------------------------------------
/ember/app/components/timeline-events/flight-upload.js:
--------------------------------------------------------------------------------
1 | import { computed } from '@ember/object';
2 |
3 | import Base from './-base';
4 |
5 | export default class FlightUpload extends Base {
6 | @computed('accountUserIsActor')
7 | get translationKey() {
8 | let i = this.accountUserIsActor ? 2 : 1;
9 | return `timeline-events.flight-upload.message${i}`;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ember/app/components/validated-block.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/bar-chart.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/long-arrow-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/skylines/commands/notifications/mark_all_unread.py:
--------------------------------------------------------------------------------
1 | from flask_script import Command
2 | from skylines.database import db
3 | from skylines.model import Notification
4 |
5 |
6 | class MarkAllUnread(Command):
7 | """ Mark all notifications as unread """
8 |
9 | def run(self):
10 | Notification.query().update(dict(time_read=None))
11 | db.session.commit()
12 |
--------------------------------------------------------------------------------
/ember/app/components/circling-performance-row.hbs:
--------------------------------------------------------------------------------
1 |
2 | | {{t (concat "circling-direction." @perf.circlingDirection)}} |
3 |
4 | {{@perf.count}}
5 | {{format-lift @perf.vario}}
6 | |
7 |
8 | {{format-seconds @perf.duration}}
9 | {{format-altitude @perf.altDiff}}
10 | |
11 |
12 |
--------------------------------------------------------------------------------
/ember/app/styles/bootstrap/_tables.scss:
--------------------------------------------------------------------------------
1 | .table {
2 | > thead,
3 | > tbody,
4 | > tfoot {
5 | > tr {
6 | > th,
7 | > td {
8 | border-top: none;
9 | }
10 | }
11 | }
12 | > thead > tr > th {
13 | border-bottom: 1px solid $table-border-color;
14 | }
15 | > tbody + tbody {
16 | border-top: 1px solid $table-border-color;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/skylines/lib/basic_auth.py:
--------------------------------------------------------------------------------
1 | from base64 import b64encode
2 |
3 | from skylines.lib.types import is_unicode
4 |
5 |
6 | def encode(name, password):
7 | if is_unicode(name):
8 | name = name.encode("utf-8")
9 |
10 | if is_unicode(password):
11 | password = password.encode("utf-8")
12 |
13 | return u"Basic " + b64encode(name + b":" + password).decode("ascii")
14 |
--------------------------------------------------------------------------------
/ember/app/components/validated-input.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/ember/app/templates/statistics/club.hbs:
--------------------------------------------------------------------------------
1 | {{t "flights"}}
2 |
3 |
4 | {{t "distance"}}
5 |
6 |
7 | {{t "flight-time"}}
8 |
9 |
10 | {{t "pilots"}}
11 |
--------------------------------------------------------------------------------
/ember/app/utils/locales.js:
--------------------------------------------------------------------------------
1 | export default [
2 | { code: 'de', name: 'Deutsch', countryCode: 'de' },
3 | { code: 'en', name: 'English', countryCode: 'gb' },
4 | { code: 'es', name: 'Español', countryCode: 'es' },
5 | { code: 'fr', name: 'Français', countryCode: 'fr' },
6 | { code: 'nl', name: 'Nederlands', countryCode: 'nl' },
7 | { code: 'pl', name: 'Polski', countryCode: 'pl' },
8 | ];
9 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/chevron-down.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/chevron-up.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/migrations/script.py.mako:
--------------------------------------------------------------------------------
1 | # revision identifiers, used by Alembic.
2 | revision = ${repr(up_revision)}
3 | down_revision = ${repr(down_revision)}
4 |
5 | from alembic import op
6 | import sqlalchemy as sa
7 | ${imports if imports else ""}
8 |
9 | def upgrade():
10 | ${upgrades if upgrades else "pass"}
11 |
12 |
13 | def downgrade():
14 | ${downgrades if downgrades else "pass"}
15 |
--------------------------------------------------------------------------------
/ember/app/styles/_font-awesome.scss:
--------------------------------------------------------------------------------
1 | .fa-svg {
2 | position: relative;
3 | height: 1em;
4 | width: auto;
5 | top: 0.125em;
6 |
7 | &.large {
8 | height: 1.33333em;
9 | vertical-align: -7.5%;
10 | }
11 |
12 | &.fixed-width {
13 | width: 1.28571429em;
14 | text-align: center;
15 | }
16 |
17 | &.rotate-270 {
18 | transform: rotateZ(270deg);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ember/app/templates/flights/all.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ember/app/templates/flights/list.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ember/app/templates/statistics/airport.hbs:
--------------------------------------------------------------------------------
1 | {{t "flights"}}
2 |
3 |
4 | {{t "distance"}}
5 |
6 |
7 | {{t "flight-time"}}
8 |
9 |
10 | {{t "pilots"}}
11 |
--------------------------------------------------------------------------------
/ember/app/templates/statistics/index.hbs:
--------------------------------------------------------------------------------
1 | {{t "flights"}}
2 |
3 |
4 | {{t "distance"}}
5 |
6 |
7 | {{t "flight-time"}}
8 |
9 |
10 | {{t "pilots"}}
11 |
--------------------------------------------------------------------------------
/ember/mirage/serializers/application.js:
--------------------------------------------------------------------------------
1 | import { Serializer } from 'ember-cli-mirage';
2 |
3 | export default Serializer.extend({
4 | embed: true,
5 | root: false,
6 |
7 | serialize() {
8 | let json = Serializer.prototype.serialize.apply(this, arguments);
9 |
10 | if ('id' in json) {
11 | json.id = Number(json.id);
12 | }
13 |
14 | return json;
15 | },
16 | });
17 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/check.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/tests/tracking/test_datetime.py:
--------------------------------------------------------------------------------
1 | from datetime import time
2 |
3 | from skylines.tracking.datetime import ms_to_time
4 |
5 |
6 | def test_ms_to_time():
7 | assert ms_to_time(0) == time(0, 0, 0)
8 |
9 | assert ms_to_time(12 * 60 * 60 * 1000 + 34 * 60 * 1000 + 56 * 1000 + 789) == time(
10 | 12, 34, 56, 789
11 | )
12 |
13 | assert ms_to_time(123.789) == time(0, 0, 0, 123)
14 |
--------------------------------------------------------------------------------
/ember/app/computed/computed-point.js:
--------------------------------------------------------------------------------
1 | import Point from 'ol/geom/Point';
2 |
3 | import safeComputed from './safe-computed';
4 |
5 | export default function computedPoint(key, layout = 'XYZM') {
6 | return safeComputed(key, coordinate => {
7 | if (layout === 'XY') {
8 | coordinate = coordinate.slice(0, 2);
9 | }
10 |
11 | return new Point(coordinate, layout);
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/ember/app/templates/flights/pinned.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ember/app/templates/flights/unassigned.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ember/tests/test-helper.js:
--------------------------------------------------------------------------------
1 | import { setApplication } from '@ember/test-helpers';
2 | import { start } from 'ember-qunit';
3 |
4 | import Application from '../app';
5 | import config from '../config/environment';
6 | import registerMatchJsonAssertion from './test-helpers/match-json';
7 |
8 | registerMatchJsonAssertion();
9 |
10 | setApplication(Application.create(config.APP));
11 |
12 | start();
13 |
--------------------------------------------------------------------------------
/ember/app/components/sidebar/tab.js:
--------------------------------------------------------------------------------
1 | import { action } from '@ember/object';
2 | import Component from '@glimmer/component';
3 |
4 | export default class SidebarTab extends Component {
5 | @action onClick(event) {
6 | event.preventDefault();
7 | event.stopPropagation();
8 |
9 | let { id, selectedTab } = this.args;
10 | this.args.onSelect(selectedTab === id ? null : id);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/ember/app/routes/tracking/info.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class InfoRoute extends Route {
5 | @service account;
6 | @service ajax;
7 |
8 | model() {
9 | let userId = this.get('account.user.id');
10 | if (userId) {
11 | return this.ajax.request('/api/settings');
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ember/app/utils/parse-query-string.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default function parseQueryString(qs) {
4 | if (!qs) {
5 | return {};
6 | }
7 |
8 | if (qs[0] === '?') {
9 | qs = qs.slice(1);
10 | }
11 |
12 | let RR = Ember.__loader.require('route-recognizer').default;
13 | let parseQueryString = RR.prototype.parseQueryString;
14 | return parseQueryString(qs);
15 | }
16 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/step-forward.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/app/components/fix-table-row.module.scss:
--------------------------------------------------------------------------------
1 | .fix-table-row td {
2 | width: 18%;
3 | text-align: right;
4 |
5 | &:first-child {
6 | width: 10%;
7 | text-align: center;
8 | vertical-align: middle;
9 | }
10 | }
11 |
12 | .remove-button {
13 | padding: 0;
14 | border: none;
15 | font: inherit;
16 | color: inherit;
17 | background-color: transparent;
18 | cursor: pointer;
19 | }
20 |
--------------------------------------------------------------------------------
/skylines/commands/shell.py:
--------------------------------------------------------------------------------
1 | from flask_script import Shell as BaseShell
2 |
3 | from flask import current_app
4 | from skylines import model, database
5 |
6 |
7 | def make_context():
8 | return dict(app=current_app, model=model, db=database.db)
9 |
10 |
11 | class Shell(BaseShell):
12 | def __init__(self, *args, **kw):
13 | super(Shell, self).__init__(make_context=make_context, *args, **kw)
14 |
--------------------------------------------------------------------------------
/skylines/lib/compat.py:
--------------------------------------------------------------------------------
1 | # flake8: noqa F821
2 |
3 | import sys
4 |
5 |
6 | def _xrange(*args, **kwargs):
7 | if sys.version_info[0] == 2:
8 | return xrange(*args, **kwargs)
9 | else:
10 | return range(*args, **kwargs)
11 |
12 |
13 | if sys.version_info[0] == 2:
14 | binary_type = str
15 | text_type = unicode
16 | else:
17 | binary_type = bytes
18 | text_type = str
19 |
--------------------------------------------------------------------------------
/ember/app/controllers/application.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 | import { action } from '@ember/object';
3 | import { inject as service } from '@ember/service';
4 |
5 | export default class ApplicationController extends Controller {
6 | @service router;
7 |
8 | @action search(text) {
9 | this.router.transitionTo('search', {
10 | queryParams: { text },
11 | });
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ember/app/templates/flight/change-aircraft.hbs:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/ember/app/templates/flights/club.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ember/app/templates/flights/date.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ember/lib/freestyle/app/routes/freestyle.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 |
3 | export default Route.extend({
4 | activate() {
5 | this._super(...arguments);
6 | this.controllerFor('application').set('onFreestyleRoute', true);
7 | },
8 |
9 | deactivate() {
10 | this._super(...arguments);
11 | this.controllerFor('application').set('onFreestyleRoute', false);
12 | },
13 | });
14 |
--------------------------------------------------------------------------------
/skylines/tracking/datetime.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 |
3 | from datetime import time
4 |
5 |
6 | def ms_to_time(ms):
7 | ms = ms % (24 * 3600 * 1000)
8 |
9 | hour = int((ms / (1000 * 60 * 60)) % 24)
10 | minute = int((ms / (1000 * 60)) % 60)
11 | second = int((ms / 1000) % 60)
12 | millisecond = int(ms % 1000)
13 |
14 | return time(hour, minute, second, millisecond)
15 |
--------------------------------------------------------------------------------
/tests/api/__init__.py:
--------------------------------------------------------------------------------
1 | from werkzeug.datastructures import Headers
2 |
3 | from skylines.lib.basic_auth import encode as basic_auth_encode
4 |
5 |
6 | def auth_for(user):
7 | return basic_auth(user.email_address, user.original_password)
8 |
9 |
10 | def basic_auth(email, password):
11 | headers = Headers()
12 | headers.add("Authorization", basic_auth_encode(email, password))
13 | return headers
14 |
--------------------------------------------------------------------------------
/ember/.ember-cli:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | Ember CLI sends analytics information by default. The data is completely
4 | anonymous, but there are times when you might want to disable this behavior.
5 |
6 | Setting `disableAnalytics` to true will prevent any data from being sent.
7 | */
8 | "disableAnalytics": false,
9 | "outputPath": "../skylines/frontend/static",
10 | "proxy": "https://skylines.aero"
11 | }
12 |
--------------------------------------------------------------------------------
/ember/app/controllers/index.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 | import { computed } from '@ember/object';
3 | import { inject as service } from '@ember/service';
4 |
5 | export default class IndexController extends Controller {
6 | @service account;
7 |
8 | @computed('account.user')
9 | get notificationsTarget() {
10 | return this.account.user ? 'notifications' : 'timeline';
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/ember/app/controllers/stats/wildcard.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 | import { readOnly } from '@ember/object/computed';
3 | import { inject as service } from '@ember/service';
4 |
5 | export default class WildcardController extends Controller {
6 | @service account;
7 |
8 | @readOnly('model.name') name;
9 | @readOnly('model.years') years;
10 | @readOnly('model.sumPilots') sumPilots;
11 | }
12 |
--------------------------------------------------------------------------------
/ember/app/templates/flights/pilot.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ember/tests/test-helpers/auth.js:
--------------------------------------------------------------------------------
1 | import { getContext } from '@ember/test-helpers';
2 |
3 | import { authenticateSession } from 'ember-simple-auth/test-support';
4 |
5 | export async function authenticateAs(user) {
6 | let { owner, server } = getContext();
7 |
8 | server.create('mirage-session', { user });
9 |
10 | await authenticateSession();
11 | await owner.lookup('service:account').loadSettings();
12 | }
13 |
--------------------------------------------------------------------------------
/ember/app/components/nav-bar-user-menu.js:
--------------------------------------------------------------------------------
1 | import { inject as service } from '@ember/service';
2 | import Component from '@glimmer/component';
3 |
4 | import { task } from 'ember-concurrency';
5 |
6 | export default class NavBarUserMenu extends Component {
7 | @service account;
8 | @service session;
9 |
10 | @(task(function* () {
11 | yield this.session.invalidate();
12 | }).drop())
13 | logoutTask;
14 | }
15 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/plus.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/skylines/__init__.py:
--------------------------------------------------------------------------------
1 | # flake8: noqa
2 |
3 | from skylines.app import (
4 | create_app,
5 | create_http_app,
6 | create_frontend_app,
7 | create_api_app,
8 | create_combined_app,
9 | create_celery_app,
10 | )
11 |
12 | from skylines.__about__ import (
13 | __title__,
14 | __summary__,
15 | __uri__,
16 | __version__,
17 | __author__,
18 | __email__,
19 | __license__,
20 | )
21 |
--------------------------------------------------------------------------------
/ember/app/routes/settings.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class SettingsRoute extends Route {
5 | @service ajax;
6 | @service session;
7 |
8 | beforeModel(transition) {
9 | this.session.requireAuthentication(transition, 'login');
10 | }
11 |
12 | model() {
13 | return this.ajax.request('/api/settings/');
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ember/app/templates/flights/airport.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ember/app/templates/tracking/index_loading.hbs:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
{{t "loading"}}
9 |
10 |
{{svg-jar "question-circle" class="fa-svg"}} {{t "how-to-use-live-tracking"}}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/map-marker.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/app/templates/flights/latest.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/arrows-h.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/bolt.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/app/components/fix-table.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{#each this.data as |row|}}
4 |
10 | {{/each}}
11 |
12 |
--------------------------------------------------------------------------------
/skylines/commands/celery.py:
--------------------------------------------------------------------------------
1 | from flask_script import Manager
2 | from skylines import create_celery_app
3 | from skylines.worker.celery import celery
4 |
5 | manager = Manager(help="Perform operations related to the Celery task queue")
6 |
7 |
8 | @manager.command
9 | def runworker():
10 | """ Runs the Celery background worker process """
11 | create_celery_app()
12 | celery.worker_main(["skylines.worker", "--loglevel=INFO"])
13 |
--------------------------------------------------------------------------------
/ember/app/components/base-page.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{yield}}
3 |
4 |
5 | © {{t "the-skylines-team"}}
6 | -
7 | {{t "imprint"}}
8 | -
9 | {{t "privacy-policy"}}
10 |
11 |
--------------------------------------------------------------------------------
/ember/app/components/timeline-events/club-join.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/components/cesium-button.module.scss:
--------------------------------------------------------------------------------
1 | .cesium-button {
2 | z-index: 1005 !important;
3 |
4 | position: absolute;
5 | top: 126px;
6 | right: 12px;
7 | padding: 2px;
8 |
9 | background: white;
10 | background: rgba(255,255,255,0.7);
11 | border: 1px solid #bbb;
12 | border-radius: 4px;
13 | box-shadow: 0 0 8px rgba(50,50,50,0.3);
14 |
15 | cursor: pointer;
16 |
17 | &:hover {
18 | background: white;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ember/app/components/flight-list-row.js:
--------------------------------------------------------------------------------
1 | import { or, equal, not } from '@ember/object/computed';
2 | import Component from '@glimmer/component';
3 |
4 | export default class FlightListRow extends Component {
5 | @or('args.flight.pilot.name', 'args.flight.pilotName') pilotName;
6 | @or('args.flight.copilot.name', 'args.flight.copilotName') copilotName;
7 |
8 | @equal('args.flight.privacyLevel', 0) isPublic;
9 | @not('isPublic') isPrivate;
10 | }
11 |
--------------------------------------------------------------------------------
/ember/app/components/playback-button.module.scss:
--------------------------------------------------------------------------------
1 | .play-button {
2 | z-index: 1005 !important;
3 |
4 | position: absolute;
5 | top: 50px;
6 | right: 12px;
7 | padding: 2px;
8 |
9 | background: white;
10 | background: rgba(255,255,255,0.7);
11 | border: 1px solid #bbb;
12 | border-radius: 4px;
13 | box-shadow: 0 0 8px rgba(50,50,50,0.3);
14 |
15 | cursor: pointer;
16 |
17 | &:hover {
18 | background: white;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/arrow-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/cloud.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/arrow-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/systemd/mapproxy.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=MapProxy uWSGI Server
3 | After=network.target
4 |
5 | [Service]
6 | SyslogIdentifier=mapproxy
7 | WorkingDirectory=/home/skylines/src
8 | ExecStart=/usr/local/bin/pipenv run gunicorn -b 127.0.0.1:9109 -w 10 --no-sendfile wsgi_mapproxy
9 | ExecReload=/bin/kill -s HUP $MAINPID
10 | ExecStop=/bin/kill -s TERM $MAINPID
11 | PrivateTmp=true
12 | Restart=on-failure
13 |
14 | [Install]
15 | WantedBy=default.target
16 |
--------------------------------------------------------------------------------
/systemd/skylines.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=SkyLines uWSGI server
3 | After=network.target
4 |
5 | [Service]
6 | SyslogIdentifier=skylines
7 | WorkingDirectory=/home/skylines/src
8 | ExecStart=/usr/local/bin/pipenv run gunicorn -b 127.0.0.1:9115 -w 10 --no-sendfile wsgi_skylines
9 | ExecReload=/bin/kill -s HUP $MAINPID
10 | ExecStop=/bin/kill -s TERM $MAINPID
11 | PrivateTmp=true
12 | Restart=on-failure
13 |
14 | [Install]
15 | WantedBy=default.target
16 |
--------------------------------------------------------------------------------
/tests/lib/test_basic_auth.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from skylines.lib.basic_auth import encode
4 | from skylines.lib.types import is_unicode
5 |
6 |
7 | def test_unicode():
8 | result = encode(u"foo", u"bar")
9 | assert result == u"Basic Zm9vOmJhcg=="
10 | assert is_unicode(result)
11 |
12 |
13 | def test_bytes():
14 | result = encode("foo", "bar")
15 | assert result == u"Basic Zm9vOmJhcg=="
16 | assert is_unicode(result)
17 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: skylines
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | custom: https://paypal.me/tobiasbieniek # Replace with a single custom sponsorship URL
9 |
--------------------------------------------------------------------------------
/ember/app/components/flight-list-nav.module.scss:
--------------------------------------------------------------------------------
1 | .datepicker {
2 | display: none;
3 | }
4 |
5 | .datepicker-button {
6 | padding: 0;
7 | border: none;
8 | outline: none;
9 | font: inherit;
10 | color: inherit;
11 | background-color: transparent;
12 | cursor: pointer;
13 | }
14 |
15 | .flight-list-nav :global(.flatpickr-calendar.static) {
16 | // this causes the calendar arrow to align with the button
17 | left: -12px;
18 | top: 12px;
19 | }
20 |
--------------------------------------------------------------------------------
/ember/app/components/timeline-events/follower.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/star.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/migrations/versions/3e82b37a4989_non_null_tracking_ke.py:
--------------------------------------------------------------------------------
1 | # revision identifiers, used by Alembic.
2 | revision = "3e82b37a4989"
3 | down_revision = "5efafe47090"
4 |
5 | from alembic import op
6 | import sqlalchemy as sa
7 |
8 |
9 | def upgrade():
10 | op.alter_column("users", "tracking_key", existing_type=sa.BIGINT(), nullable=False)
11 |
12 |
13 | def downgrade():
14 | op.alter_column("users", "tracking_key", existing_type=sa.BIGINT(), nullable=True)
15 |
--------------------------------------------------------------------------------
/ember/app/styles/ranking.module.scss:
--------------------------------------------------------------------------------
1 | .header {
2 | composes: page-header from global;
3 | padding-bottom: 50px;
4 |
5 | @media (min-width: 768px) {
6 | padding-bottom: 0;
7 | }
8 | }
9 |
10 | .tabs {
11 | position: absolute;
12 | bottom: 0;
13 | left: 5px;
14 |
15 | @media (min-width: 768px) {
16 | left: auto;
17 | right: 5px;
18 | }
19 |
20 | :global(.nav-tabs) {
21 | margin-bottom: 0;
22 | border-bottom: 0;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ember/app/templates/settings/profile.hbs:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ember/app/templates/tracking/index.hbs:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 | {{svg-jar "question-circle" class="fa-svg"}} {{t "how-to-use-live-tracking"}}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ember/app/components/progress-bar.module.scss:
--------------------------------------------------------------------------------
1 | .progress-bar {
2 | position: fixed;
3 | left: 0;
4 | top: 0;
5 | height: 2px;
6 | background: lighten($skylines-blue, 0%);
7 | border-bottom: solid 1px lighten($skylines-blue, 10%);
8 | box-shadow: 0 0 5px 0 lighten($skylines-blue, 20%);
9 | z-index: 3000;
10 |
11 | :global(.ember-application) & {
12 | top: 40px;
13 |
14 | @media (max-width: 767px) {
15 | top: 50px;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ember/app/components/wingman-table.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | | {{t "aircraft"}} |
5 | {{t "pilot"}} |
6 | {{t "first-last"}} |
7 |
8 |
9 |
10 | {{#each this.nearFlightsWithColors as |nearFlight|}}
11 |
12 | {{/each}}
13 |
14 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/circle-o.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/pencil.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/migrations/versions/5efafe47090_removed_eye_candy.py:
--------------------------------------------------------------------------------
1 | # revision identifiers, used by Alembic.
2 | revision = "5efafe47090"
3 | down_revision = "4b3af9d93c97"
4 |
5 | from alembic import op
6 | import sqlalchemy as sa
7 |
8 |
9 | def upgrade():
10 | op.drop_column("users", u"eye_candy")
11 |
12 |
13 | def downgrade():
14 | op.add_column(
15 | "users",
16 | sa.Column(u"eye_candy", sa.BOOLEAN(), server_default="false", nullable=False),
17 | )
18 |
--------------------------------------------------------------------------------
/skylines/lib/util.py:
--------------------------------------------------------------------------------
1 | def str_to_bool(str):
2 | return str.lower() in ["1", "true", "t", "yes", "y"]
3 |
4 |
5 | def pressure_alt_to_qnh_alt(altitude, qnh):
6 | if qnh is None:
7 | qnh = 1013.25
8 |
9 | k1 = 0.190263
10 | inv_k1 = 1.0 / 0.190263
11 | k2 = 8.417286e-5
12 | inv_k2 = 1.0 / 8.417286e-5
13 |
14 | static_pressure = ((1013.25 ** k1) - k2 * altitude) ** inv_k1
15 |
16 | return (qnh ** k1 - static_pressure ** k1) * inv_k2
17 |
--------------------------------------------------------------------------------
/ember/app/components/layer-switcher.js:
--------------------------------------------------------------------------------
1 | import { inject as service } from '@ember/service';
2 | import Component from '@glimmer/component';
3 | import { tracked } from '@glimmer/tracking';
4 |
5 | import { BASE_LAYERS, OVERLAY_LAYERS } from '../services/map-settings';
6 |
7 | export default class LayerSwitcher extends Component {
8 | @service mapSettings;
9 |
10 | @tracked open = false;
11 |
12 | BASE_LAYERS = BASE_LAYERS;
13 | OVERLAY_LAYERS = OVERLAY_LAYERS;
14 | }
15 |
--------------------------------------------------------------------------------
/ember/app/components/search-result-table.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{#each @results as |result|}}
4 |
5 | {{else}}
6 |
7 | |
8 | {{#if @searchText}}
9 | {{t "search.no-results"}}
10 | {{else}}
11 | {{t "search.no-search-text"}}
12 | {{/if}}
13 | |
14 |
15 | {{/each}}
16 |
17 |
--------------------------------------------------------------------------------
/ember/app/components/timeline-events/flight-upload.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/app/components/tracking-table.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | | {{t "last-fix"}} |
5 | {{t "pilot"}} |
6 | {{t "nearest-airport"}} |
7 | {{t "altitude"}} |
8 | |
9 |
10 |
11 |
12 |
13 | {{#each @tracks as |track|}}
14 |
15 | {{/each}}
16 |
17 |
--------------------------------------------------------------------------------
/ember/app/helpers/format-seconds.js:
--------------------------------------------------------------------------------
1 | import { helper } from '@ember/component/helper';
2 |
3 | import pad from '../utils/pad';
4 |
5 | export function formatSeconds([value]) {
6 | value %= 86400;
7 | let h = Math.floor(value / 3600);
8 | let m = Math.floor((value % 3600) / 60);
9 | let s = Math.floor((value % 3600) % 60);
10 |
11 | // Format the result into time strings
12 | return `${h}:${pad(m)}:${pad(s)}`;
13 | }
14 |
15 | export default helper(formatSeconds);
16 |
--------------------------------------------------------------------------------
/skylines/commands/import_/__init__.py:
--------------------------------------------------------------------------------
1 | from flask_script import Manager
2 |
3 | from .airspace import AirspaceCommand
4 | from .dmst_index import DMStIndex
5 | from .mwp import MWP
6 | from .welt2000 import Welt2000
7 |
8 | manager = Manager(help="Import external data into the database")
9 | manager.add_command("airspace", AirspaceCommand())
10 | manager.add_command("dmst-index", DMStIndex())
11 | manager.add_command("mwp", MWP())
12 | manager.add_command("welt2000", Welt2000())
13 |
--------------------------------------------------------------------------------
/ember/app/components/timeline-events/-base.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 | import { inject as service } from '@ember/service';
3 |
4 | import safeComputed from '../../computed/safe-computed';
5 |
6 | export default class Base extends Component {
7 | tagName = '';
8 |
9 | @service account;
10 |
11 | event = null;
12 |
13 | @safeComputed('account.user', 'event.actor', (accountUser, actor) => accountUser.id === actor.id)
14 | accountUserIsActor;
15 | }
16 |
--------------------------------------------------------------------------------
/ember/app/components/timeline-events/flight-comment.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/mirage/serializers/club.js:
--------------------------------------------------------------------------------
1 | import BaseSerializer from './application';
2 |
3 | export default BaseSerializer.extend({
4 | serialize(object) {
5 | let json = BaseSerializer.prototype.serialize.apply(this, arguments);
6 |
7 | if (object.owner) {
8 | json.owner = {
9 | id: Number(object.owner.id),
10 | name: object.owner.name,
11 | };
12 | } else {
13 | json.owner = null;
14 | }
15 |
16 | return json;
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:js-app",
4 | ":dependencyDashboard",
5 | ":maintainLockFilesWeekly",
6 | ":semanticCommitsDisabled",
7 | ":automergeLinters",
8 | ":automergeTesters"
9 | ],
10 | "python": {
11 | "enabled": false
12 | },
13 | "postUpdateOptions": ["yarnDedupeFewer"],
14 | "packageRules": [{
15 | "packageNames": ["ember-cli", "ember-data", "ember-source"],
16 | "separateMinorPatch": true
17 | }]
18 | }
19 |
--------------------------------------------------------------------------------
/ember/.template-lintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: 'recommended',
3 |
4 | rules: {
5 | 'block-indentation': false,
6 | 'link-href-attributes': false,
7 | 'link-rel-noopener': false,
8 | 'no-inline-styles': false,
9 | 'no-invalid-interactive': false,
10 | 'no-negated-condition': false,
11 | 'require-button-type': false,
12 | 'require-valid-alt-text': false,
13 | 'style-concatenation': false,
14 | 'table-groups': false,
15 | },
16 | };
17 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/remove.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/facebook-square.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/info.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/tests/data/clubs.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 | from skylines.model import Club
4 |
5 |
6 | def lva(**kwargs):
7 | return Club(
8 | name=u"LV Aachen",
9 | website=u"http://www.lv-aachen.de",
10 | time_created=datetime(2015, 12, 24, 12, 34, 56),
11 | ).apply_kwargs(kwargs)
12 |
13 |
14 | def sfn(**kwargs):
15 | return Club(
16 | name=u"Sportflug Niederberg", time_created=datetime(2017, 1, 1, 12, 34, 56)
17 | ).apply_kwargs(kwargs)
18 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 | indent_style = space
14 | indent_size = 2
15 |
16 | [*.py]
17 | indent_size = 4
18 |
19 | [*.hbs]
20 | insert_final_newline = false
21 |
22 | [*.{diff,md}]
23 | trim_trailing_whitespace = false
24 |
--------------------------------------------------------------------------------
/skylines/commands/server.py:
--------------------------------------------------------------------------------
1 | from flask_script import Server as BaseServer
2 | from skylines.app import create_combined_app, create_api_app
3 |
4 |
5 | class Server(BaseServer):
6 | def handle(self, app, *args, **kw):
7 | app = create_combined_app()
8 | super(Server, self).handle(app, *args, **kw)
9 |
10 |
11 | class APIServer(BaseServer):
12 | def handle(self, app, *args, **kw):
13 | app = create_api_app()
14 | super(APIServer, self).handle(app, *args, **kw)
15 |
--------------------------------------------------------------------------------
/INSTALL.mapserver.md:
--------------------------------------------------------------------------------
1 | ## Running a local mapserver
2 |
3 | If you want to run the map server locally you will need to install the
4 | `mapserver` CLI. On Debian you can do:
5 |
6 | $ apt-get install mapserver-cli
7 |
8 | To import airspaces into the database, install the `python-gdal` package (using
9 | gdal extension directly from pypi is not recommended) and import the required
10 | airspace files:
11 |
12 | $ ./manage.py import airspace airspace/airspace_list.txt airspace/airspace_blacklist.txt
13 |
--------------------------------------------------------------------------------
/migrations/versions/ffa5706b1fb_added_callsign_colum.py:
--------------------------------------------------------------------------------
1 | # revision identifiers, used by Alembic.
2 | revision = "ffa5706b1fb"
3 | down_revision = "438d391a92b4"
4 |
5 | from alembic import op
6 | import sqlalchemy as sa
7 | from sqlalchemy.dialects import postgresql
8 |
9 |
10 | def upgrade():
11 | op.add_column(
12 | "users", sa.Column("tracking_callsign", sa.Unicode(length=5), nullable=True)
13 | )
14 |
15 |
16 | def downgrade():
17 | op.drop_column("users", "tracking_callsign")
18 |
--------------------------------------------------------------------------------
/ember/app/styles/_spinner.scss:
--------------------------------------------------------------------------------
1 | @keyframes spinner {
2 | to {
3 | transform: rotate(360deg);
4 | }
5 | }
6 |
7 | .spinner {
8 | display: inline-block;
9 | width: 1.5rem;
10 | height: 1.5rem;
11 | vertical-align: text-bottom;
12 | border: .2em solid rgba(0, 0, 0, 0.35);
13 | border-right-color: black;
14 | border-radius: 50%;
15 | animation: spinner 1s linear infinite;
16 | }
17 |
18 | .spinner-white {
19 | border-color: rgba(255, 255, 255, 0.35);
20 | border-right-color: white;
21 | }
22 |
--------------------------------------------------------------------------------
/ember/app/components/nav-bar-search-form.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/reorder.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/tests/data/flight_comments.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 |
3 | from datetime import datetime
4 |
5 | from skylines.model import FlightComment
6 |
7 |
8 | def yeah(flight, **kwargs):
9 | return FlightComment(
10 | time_created=datetime(2017, 1, 19, 12, 34, 56), flight=flight, text=u"Yeah!"
11 | ).apply_kwargs(kwargs)
12 |
13 |
14 | def emoji(flight, **kwargs):
15 | return FlightComment(
16 | time_created=datetime(2020, 12, 19, 12, 34, 56), flight=flight, text=u"👍"
17 | ).apply_kwargs(kwargs)
18 |
--------------------------------------------------------------------------------
/tests/data/live_fix.py:
--------------------------------------------------------------------------------
1 | from skylines.model import TrackingFix
2 |
3 |
4 | def create(pilot, time, latitude, longitude):
5 | """Creates a test fix for the passed pilot"""
6 | fix = TrackingFix(
7 | pilot=pilot,
8 | time=time,
9 | time_visible=time,
10 | track=0,
11 | ground_speed=10,
12 | airspeed=10,
13 | altitude=100,
14 | elevation=0,
15 | vario=0,
16 | )
17 |
18 | fix.set_location(latitude, longitude)
19 |
20 | return fix
21 |
--------------------------------------------------------------------------------
/ember/app/components/fix-table-row.js:
--------------------------------------------------------------------------------
1 | import { action, computed } from '@ember/object';
2 | import { htmlSafe } from '@ember/template';
3 | import Component from '@glimmer/component';
4 |
5 | export default class extends Component {
6 | @computed('args.row.color')
7 | get badgeStyle() {
8 | return htmlSafe(`background-color: ${this.args.row.color}`);
9 | }
10 |
11 | @action
12 | handleClick() {
13 | if (this.args.selectable) {
14 | this.args.onSelect(this.args.row.id);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ember/app/controllers/search.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 | import { action } from '@ember/object';
3 | import { oneWay, readOnly } from '@ember/object/computed';
4 |
5 | export default class SearchController extends Controller {
6 | queryParams = ['text'];
7 |
8 | @readOnly('text') searchText;
9 | @oneWay('searchText') searchTextInput;
10 | @readOnly('model.results') results;
11 |
12 | @action
13 | search(text) {
14 | this.transitionToRoute({ queryParams: { text } });
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/user.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/app/routes/club/pilots.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class PilotsRoute extends Route {
5 | @service ajax;
6 |
7 | model() {
8 | let { club_id } = this.paramsFor('club');
9 | return this.ajax.request(`/api/users?club=${club_id}`);
10 | }
11 |
12 | setupController(controller) {
13 | super.setupController(...arguments);
14 | controller.set('club', this.controllerFor('club').get('model'));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/star-o.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/skylines/api/views/airports.py:
--------------------------------------------------------------------------------
1 | from flask import Blueprint
2 |
3 | from skylines.api.json import jsonify
4 | from skylines.lib.dbutil import get_requested_record
5 | from skylines.model import Airport
6 | from skylines.schemas import AirportSchema
7 |
8 | airports_blueprint = Blueprint("airports", "skylines")
9 |
10 |
11 | @airports_blueprint.route("/airports/")
12 | def index(airport_id):
13 | airport = get_requested_record(Airport, airport_id)
14 | return jsonify(AirportSchema().dump(airport).data)
15 |
--------------------------------------------------------------------------------
/skylines/frontend/views/__init__.py:
--------------------------------------------------------------------------------
1 | from .errors import register as register_error_handlers
2 |
3 | from .assets import assets_blueprint
4 | from .files import files_blueprint
5 | from .livetrack24 import lt24_blueprint
6 | from .widgets import widgets_blueprint
7 |
8 |
9 | def register(app):
10 | register_error_handlers(app)
11 |
12 | app.register_blueprint(assets_blueprint)
13 | app.register_blueprint(files_blueprint)
14 | app.register_blueprint(lt24_blueprint)
15 | app.register_blueprint(widgets_blueprint)
16 |
--------------------------------------------------------------------------------
/tests/data/airspace.py:
--------------------------------------------------------------------------------
1 | from geoalchemy2.shape import from_shape
2 | from shapely.geometry import Polygon
3 |
4 | from skylines.model import Airspace
5 |
6 |
7 | def test_airspace(**kwargs):
8 | shape = Polygon(((30, 10), (40, 40), (20, 40), (10, 20), (30, 10)))
9 |
10 | return Airspace(
11 | name="TestAirspace",
12 | airspace_class="WAVE",
13 | top="FL100",
14 | base="4500ft",
15 | country_code="de",
16 | the_geom=from_shape(shape, srid=4326),
17 | ).apply_kwargs(kwargs)
18 |
--------------------------------------------------------------------------------
/ember/app/app.js:
--------------------------------------------------------------------------------
1 | import Application from '@ember/application';
2 |
3 | import loadInitializers from 'ember-load-initializers';
4 |
5 | import config from './config/environment';
6 | import Resolver from './resolver';
7 | import { startSentry } from './sentry';
8 |
9 | startSentry();
10 |
11 | class App extends Application {
12 | modulePrefix = config.modulePrefix;
13 | podModulePrefix = config.podModulePrefix;
14 | Resolver = Resolver;
15 | }
16 |
17 | loadInitializers(App, config.modulePrefix);
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/ember/app/components/pin-button.js:
--------------------------------------------------------------------------------
1 | import { action, computed } from '@ember/object';
2 | import { inject as service } from '@ember/service';
3 | import Component from '@glimmer/component';
4 |
5 | export default class PinButton extends Component {
6 | @service pinnedFlights;
7 |
8 | @computed('pinnedFlights.pinned.[]', 'args.flightId')
9 | get pinned() {
10 | return this.pinnedFlights.pinned.includes(this.args.flightId);
11 | }
12 |
13 | @action toggle() {
14 | this.pinnedFlights.toggle(this.args.flightId);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ember/app/components/settings-panels/tracking-key.module.scss:
--------------------------------------------------------------------------------
1 | .panel-heading {
2 | composes: panel-heading from global;
3 | position: relative;
4 | }
5 |
6 | .help-icon {
7 | position: absolute;
8 | right: 0;
9 | top: 0;
10 | font-size: 20pt;
11 | color: #aaa;
12 | text-decoration: none;
13 |
14 | &:hover {
15 | color: #666;
16 | }
17 | }
18 |
19 | .tracking-key {
20 | composes: label from global;
21 |
22 | font-size: 100%;
23 | font-family: monospace;
24 | margin: 0 6px;
25 | background: #008eda;
26 | }
27 |
--------------------------------------------------------------------------------
/ember/lib/cesium/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 |
5 | const Funnel = require('broccoli-funnel');
6 |
7 | module.exports = {
8 | name: 'cesium',
9 |
10 | isDevelopingAddon() {
11 | return true;
12 | },
13 |
14 | treeForPublic() {
15 | let cesiumPkgPath = require.resolve('cesium');
16 | let cesiumPath = path.dirname(cesiumPkgPath);
17 | let cesiumBuildPath = path.join(cesiumPath, 'Build', 'Cesium');
18 | return new Funnel(cesiumBuildPath, { destDir: 'cesium' });
19 | },
20 | };
21 |
--------------------------------------------------------------------------------
/ember/mirage/models/mirage-session.js:
--------------------------------------------------------------------------------
1 | import { Model, belongsTo } from 'ember-cli-mirage';
2 |
3 | /**
4 | * This is a mirage-only model, that is used to keep track of the current
5 | * session and the associated `user` model, because in route handlers we don't
6 | * have access to the auth data that the actual auth service is using for
7 | * these things.
8 | *
9 | * This mock implementation means that there can only ever exist one
10 | * session at a time.
11 | */
12 | export default Model.extend({
13 | user: belongsTo(),
14 | });
15 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/plane.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/app/components/pin-star.js:
--------------------------------------------------------------------------------
1 | import { action, computed } from '@ember/object';
2 | import { inject as service } from '@ember/service';
3 | import Component from '@glimmer/component';
4 |
5 | export default class PinStar extends Component {
6 | @service pinnedFlights;
7 |
8 | @computed('pinnedFlights.pinned.[]', 'args.flightId')
9 | get pinned() {
10 | return this.pinnedFlights.pinned.includes(this.args.flightId);
11 | }
12 |
13 | @action handleClick() {
14 | this.pinnedFlights.toggle(this.args.flightId);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ember/app/controllers/about/team.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 | import { computed } from '@ember/object';
3 | import { inject as service } from '@ember/service';
4 |
5 | export default class TeamController extends Controller {
6 | @service intl;
7 |
8 | @computed('model.content', 'intl.locale')
9 | get text() {
10 | let intl = this.intl;
11 |
12 | return this.get('model.content')
13 | .replace('Developers', intl.t('developers'))
14 | .replace('Translators', intl.t('translators'));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ember/app/templates/flight-upload.hbs:
--------------------------------------------------------------------------------
1 | {{#if result}}
2 |
3 |
6 |
7 |
8 |
9 | {{else}}
10 |
11 |
12 |
13 | {{/if}}
14 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/ban.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/search.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/skylines/commands/tracking/server.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 |
3 | import sys
4 | from flask_script import Command
5 |
6 | from skylines.app import create_app
7 | from skylines.tracking.server import TrackingServer
8 |
9 |
10 | class Server(Command):
11 | """ Runs the live tracking UDP server """
12 |
13 | def run(self):
14 | print("Receiving datagrams on :5597")
15 | sys.stdout.flush()
16 | server = TrackingServer(":5597")
17 | server.init_app(create_app())
18 | server.serve_forever()
19 |
--------------------------------------------------------------------------------
/ember/app/styles/bootstrap/_bootstrap-badges.scss:
--------------------------------------------------------------------------------
1 | // Colored "badges"
2 |
3 | .badge-default {
4 | @include label-variant($label-default-bg);
5 | }
6 |
7 | .badge-primary {
8 | @include label-variant($label-primary-bg);
9 | }
10 |
11 | .badge-success {
12 | @include label-variant($label-success-bg);
13 | }
14 |
15 | .badge-info {
16 | @include label-variant($label-info-bg);
17 | }
18 |
19 | .badge-warning {
20 | @include label-variant($label-warning-bg);
21 | }
22 |
23 | .badge-danger {
24 | @include label-variant($label-danger-bg);
25 | }
26 |
--------------------------------------------------------------------------------
/ember/tests/helpers/initials-test.js:
--------------------------------------------------------------------------------
1 | import { module, test } from 'qunit';
2 |
3 | import { initials } from 'skylines/helpers/initials';
4 |
5 | module('Unit | Helper | initials', function () {
6 | test('works', function (assert) {
7 | assert.equal(initials(['John Doe']), 'JD');
8 | assert.equal(initials(['John Adam Bart Charly Doe']), 'JABCD');
9 | assert.equal(initials(['John Fitz. Kennedy']), 'JK');
10 | assert.equal(initials(['John F. Kennedy']), 'JK');
11 | assert.equal(initials(['John F Kennedy']), 'JK');
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/skylines/schemas/validate.py:
--------------------------------------------------------------------------------
1 | from marshmallow.validate import * # NOQA
2 | from marshmallow.validate import Validator, ValidationError
3 |
4 |
5 | class NotEmpty(Validator):
6 | def __call__(self, value):
7 | if len(value) == 0:
8 | raise ValidationError("Must not be empty.")
9 |
10 |
11 | class EmptyOr(Validator):
12 | def __init__(self, other_validator):
13 | self.other_validator = other_validator
14 |
15 | def __call__(self, value):
16 | if len(value) != 0:
17 | self.other_validator(value)
18 |
--------------------------------------------------------------------------------
/tests/api/views/user_agent_test.py:
--------------------------------------------------------------------------------
1 | from werkzeug.datastructures import Headers
2 |
3 |
4 | def test_request_with_user_agent_works(client):
5 | res = client.get("/")
6 | assert res.status_code == 404
7 |
8 |
9 | def test_missing_user_agent_returns_403(client):
10 | headers = Headers()
11 | headers.set("User-Agent", "")
12 |
13 | res = client.get("/", headers=headers)
14 | assert res.status_code == 403
15 |
16 | json = res.json
17 | assert isinstance(json, dict)
18 | assert "User-Agent header" in json.get("message")
19 |
--------------------------------------------------------------------------------
/ember/app/components/fullscreen-button.module.scss:
--------------------------------------------------------------------------------
1 | .fullscreen-button {
2 | z-index: 1006 !important;
3 |
4 | position: absolute;
5 | top: 88px;
6 | right: 12px;
7 | padding: 0;
8 |
9 | width: 32px;
10 | height: 32px;
11 |
12 | font-size: 26px;
13 | line-height: 30px;
14 |
15 | background: white;
16 | background: rgba(255,255,255,0.7);
17 | border: 1px solid #bbb;
18 | border-radius: 4px;
19 | box-shadow: 0 0 8px rgba(50,50,50,0.3);
20 |
21 | cursor: pointer;
22 |
23 | &:hover {
24 | background: white;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/skylines/commands/tracking/clear.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 |
3 | from flask_script import Command, Option
4 | from skylines.database import db
5 | from skylines.model import TrackingFix
6 |
7 |
8 | class Clear(Command):
9 | """ Clear all live tracks for a certain user """
10 |
11 | option_list = (Option("user", type=int, help="a user ID"),)
12 |
13 | def run(self, user):
14 | result = TrackingFix.query(pilot_id=user).delete()
15 | db.session.commit()
16 |
17 | print("%d live tracks cleared." % result)
18 |
--------------------------------------------------------------------------------
/ember/app/components/layer-switcher-element.hbs:
--------------------------------------------------------------------------------
1 |
9 |
10 | {{svg-jar "check" class="fa-svg"}} {{@name}}
11 |
--------------------------------------------------------------------------------
/ember/mirage/config.js:
--------------------------------------------------------------------------------
1 | import * as Clubs from './route-handlers/clubs';
2 | import * as Notifications from './route-handlers/notifications';
3 | import * as Settings from './route-handlers/settings';
4 | import * as Users from './route-handlers/users';
5 |
6 | export default function () {
7 | this.passthrough('/translations/**');
8 | this.passthrough('/write-coverage');
9 |
10 | this.get('/api/locale', { locale: 'en' });
11 |
12 | Clubs.register(this);
13 | Notifications.register(this);
14 | Settings.register(this);
15 | Users.register(this);
16 | }
17 |
--------------------------------------------------------------------------------
/ember/tests/acceptance/index-test.js:
--------------------------------------------------------------------------------
1 | import { visit, currentURL } from '@ember/test-helpers';
2 | import { module, test } from 'qunit';
3 |
4 | import { setupApplicationTest } from '../test-helpers';
5 |
6 | module('Acceptance | index', function (hooks) {
7 | setupApplicationTest(hooks);
8 |
9 | hooks.beforeEach(async function (assert) {
10 | await visit('/');
11 | assert.equal(currentURL(), '/');
12 | });
13 |
14 | test('shows a welcome message', function (assert) {
15 | assert.dom('[data-test-welcome-message]').exists();
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/skylines/api/json.py:
--------------------------------------------------------------------------------
1 | from flask import request, json, current_app
2 |
3 |
4 | def jsonify(_data=None, **kwargs):
5 | if _data is None:
6 | _data = kwargs
7 |
8 | if not isinstance(_data, (dict, list)):
9 | raise TypeError
10 |
11 | # Determine JSON indentation
12 | indent = None
13 | if current_app.config["JSONIFY_PRETTYPRINT_REGULAR"] and not request.is_xhr:
14 | indent = 2
15 |
16 | content = json.dumps(_data, indent=indent)
17 |
18 | return current_app.response_class(content, mimetype="application/json")
19 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use Python 2.7 by default, but allow
2 | # e.g. `--build-arg PYTHON_VERSION=3.6` usage
3 | ARG PYTHON_VERSION=2.7
4 | FROM python:${PYTHON_VERSION}
5 |
6 | RUN pip install --upgrade pip
7 | RUN pip install --upgrade virtualenv
8 | RUN pip install pipenv==v2020.11.15
9 |
10 | WORKDIR /home/skylines/code/
11 |
12 | # Install Python dependencies
13 | COPY Pipfile Pipfile.lock /home/skylines/code/
14 | RUN pipenv install --dev --python=${PYTHON_VERSION}
15 |
16 | # Run the development server by default
17 | CMD ["pipenv", "run", "./manage.py", "runserver"]
18 |
--------------------------------------------------------------------------------
/ember/app/components/user-quick-stats.js:
--------------------------------------------------------------------------------
1 | import Component from '@glimmer/component';
2 |
3 | import safeComputed from '../computed/safe-computed';
4 |
5 | export default class UserQuickStats extends Component {
6 | @safeComputed('args.stats.distance', 'args.stats.duration', (distance, duration) => distance / duration) speed;
7 | @safeComputed('args.stats.distance', 'args.stats.flights', (distance, flights) => distance / flights) avgDistance;
8 | @safeComputed('args.stats.duration', 'args.stats.flights', (duration, flights) => duration / flights) avgDuration;
9 | }
10 |
--------------------------------------------------------------------------------
/ember/app/components/background-layers.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{#if this.bingApiKey}}
6 |
7 |
8 | {{/if}}
9 | {{#if this.mapBoxUrl}}
10 |
11 | {{/if}}
12 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/clock-o.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/app/components/user-distance-flight.hbs:
--------------------------------------------------------------------------------
1 |
2 | | {{@label}} |
3 |
4 | {{#if @flight}}
5 |
11 |
12 | {{t "show"}}
13 |
14 | {{/if}}
15 | |
16 |
--------------------------------------------------------------------------------
/ember/app/routes/user/followers.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class FollowersRoute extends Route {
5 | @service ajax;
6 |
7 | model() {
8 | let { user_id } = this.paramsFor('user');
9 | return this.ajax.request(`/api/users/${user_id}/followers`);
10 | }
11 |
12 | setupController(controller, model) {
13 | super.setupController(...arguments);
14 | controller.set('user', this.modelFor('user'));
15 | controller.set('followers', model.followers);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ember/app/routes/user/following.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class FollowingRoute extends Route {
5 | @service ajax;
6 |
7 | model() {
8 | let { user_id } = this.paramsFor('user');
9 | return this.ajax.request(`/api/users/${user_id}/following`);
10 | }
11 |
12 | setupController(controller, model) {
13 | super.setupController(...arguments);
14 | controller.set('user', this.modelFor('user'));
15 | controller.set('following', model.following);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ember/app/utils/geo-distance.js:
--------------------------------------------------------------------------------
1 | const RADIUS = 6367009;
2 | const DEG_TO_RAD = Math.PI / 180;
3 |
4 | export default function (loc1_deg, loc2_deg) {
5 | let loc1 = [loc1_deg[0] * DEG_TO_RAD, loc1_deg[1] * DEG_TO_RAD];
6 | let loc2 = [loc2_deg[0] * DEG_TO_RAD, loc2_deg[1] * DEG_TO_RAD];
7 |
8 | let dlon = loc2[0] - loc1[0];
9 | let dlat = loc2[1] - loc1[1];
10 |
11 | let a = Math.pow(Math.sin(dlat / 2), 2) + Math.cos(loc1[1]) * Math.cos(loc2[1]) * Math.pow(Math.sin(dlon / 2), 2);
12 |
13 | let c = 2 * Math.asin(Math.sqrt(a));
14 |
15 | return RADIUS * c;
16 | }
17 |
--------------------------------------------------------------------------------
/ember/mirage/factories/user.js:
--------------------------------------------------------------------------------
1 | import { Factory } from 'ember-cli-mirage';
2 |
3 | export default Factory.extend({
4 | admin: false,
5 | altitudeUnit: 0,
6 | distanceUnit: 1,
7 | email: 'johnny.dee@gmail.com',
8 | firstName: 'John',
9 | followers: 107,
10 | following: 128,
11 | lastName: 'Doe',
12 | liftUnit: 0,
13 | name() {
14 | return `${this.firstName} ${this.lastName}`;
15 | },
16 | password: 'secret123',
17 | recoveryKey: null,
18 | speedUnit: 1,
19 | trackingCallsign: 'JD',
20 | trackingDelay: 0,
21 | trackingKey: 'ABCDEF42',
22 | });
23 |
--------------------------------------------------------------------------------
/skylines/commands/tracking/fill_missing_keys.py:
--------------------------------------------------------------------------------
1 | from flask_script import Command
2 | from skylines.database import db
3 | from skylines.model import User
4 |
5 |
6 | class FillMissingKeys(Command):
7 | """ Generate tracking keys for user accounts that don't have one. """
8 |
9 | def run(self):
10 | keyless_users = User.query(tracking_key=None)
11 |
12 | for i, user in enumerate(keyless_users):
13 | user.generate_tracking_key()
14 |
15 | if i % 25 == 0:
16 | db.session.flush()
17 |
18 | db.session.commit()
19 |
--------------------------------------------------------------------------------
/ember/app/routes/flight/change-aircraft.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 | import RSVP from 'rsvp';
4 |
5 | export default class ChangeAircraftRoute extends Route {
6 | @service ajax;
7 |
8 | model() {
9 | let id = this.modelFor('flight').ids[0];
10 |
11 | let flight = this.ajax.request(`/api/flights/${id}/`).then(it => it.flight);
12 | let aircraftModels = this.ajax.request('/api/aircraft-models').then(it => it.models);
13 |
14 | return RSVP.hash({ id, flight, aircraftModels });
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ember/app/components/sortable-table-header.hbs:
--------------------------------------------------------------------------------
1 | {{#if (not-eq @column @sortColumn)}}
2 | {{yield}}
3 | {{else if (eq @sortOrder "asc")}}
4 | {{svg-jar "chevron-up" class="fa-svg"}} {{yield}}
5 | {{else}}
6 | {{svg-jar "chevron-down" class="fa-svg"}} {{yield}}
7 | {{/if}}
8 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/warning.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/app/components/background-layers/empty.js:
--------------------------------------------------------------------------------
1 | import Component from '@glimmer/component';
2 |
3 | import TileLayer from 'ol/layer/Tile';
4 |
5 | export default class extends Component {
6 | layer = new TileLayer({});
7 |
8 | constructor() {
9 | super(...arguments);
10 |
11 | this.layer.setProperties({
12 | name: 'Empty',
13 | id: 'Empty',
14 | base_layer: true,
15 | display_in_layer_switcher: true,
16 | });
17 |
18 | this.args.map.addLayer(this.layer);
19 | }
20 |
21 | willDestroy() {
22 | this.args.map.removeLayer(this.layer);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ember/app/formats.js:
--------------------------------------------------------------------------------
1 | export default {
2 | time: {
3 | hhmm: {
4 | hour: '2-digit',
5 | minute: '2-digit',
6 | },
7 | hhmmss: {
8 | hour: '2-digit',
9 | minute: '2-digit',
10 | second: '2-digit',
11 | },
12 | full: {
13 | year: 'numeric',
14 | month: 'short',
15 | day: 'numeric',
16 | hour: '2-digit',
17 | minute: '2-digit',
18 | second: '2-digit',
19 | },
20 | },
21 | date: {
22 | ddmmyyyy: {
23 | year: 'numeric',
24 | month: '2-digit',
25 | day: '2-digit',
26 | },
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/tests/schemas/schemas/test_flight.py:
--------------------------------------------------------------------------------
1 | from skylines.schemas import FlightSchema
2 |
3 |
4 | def test_deserialization_passes_for_valid_model_id():
5 | data = FlightSchema(partial=True).load(dict(modelId=4)).data
6 | assert data["model_id"] == 4
7 |
8 |
9 | def test_deserialization_passes_for_missing_model_id():
10 | data = FlightSchema(partial=True).load(dict()).data
11 | assert "model_id" not in data
12 |
13 |
14 | def test_deserialization_passes_for_null_model_id():
15 | data = FlightSchema(partial=True).load(dict(modelId=None)).data
16 | assert data["model_id"] == None
17 |
--------------------------------------------------------------------------------
/ember/app/validators/current-password.js:
--------------------------------------------------------------------------------
1 | import { inject as service } from '@ember/service';
2 |
3 | import BaseValidator from 'ember-cp-validations/validators/base';
4 |
5 | export default class CurrentPassword extends BaseValidator {
6 | @service ajax;
7 | @service intl;
8 |
9 | async validate(password, options) {
10 | if (!password) {
11 | return;
12 | }
13 |
14 | let json = { password };
15 | let { result } = await this.ajax.request('/api/settings/password/check', { method: 'POST', json });
16 | return result ? true : this.intl.t(options.messageKey);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/twitter.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/app/components/sidebar/tab.module.scss:
--------------------------------------------------------------------------------
1 | .list-item {
2 | width: 100%;
3 | height: 40px;
4 | color: #999;
5 | font-size: 12pt;
6 | overflow: hidden;
7 | transition: all 80ms;
8 | list-style-type: none;
9 |
10 | &:hover {
11 | color: #000;
12 | background-color: #ccc;
13 | }
14 |
15 | &.active {
16 | color: #fff;
17 | background-color: #428bca;
18 | }
19 | }
20 |
21 | .button {
22 | width: 100%;
23 | height: 100%;
24 | padding: 0;
25 | border: none;
26 | font: inherit;
27 | color: inherit;
28 | background-color: transparent;
29 | cursor: pointer;
30 | }
31 |
--------------------------------------------------------------------------------
/ember/app/routes/login.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default class LoginRoute extends Route {
5 | @service session;
6 |
7 | beforeModel() {
8 | this.session.prohibitAuthentication('index');
9 | }
10 |
11 | setupController() {
12 | super.setupController(...arguments);
13 | this.controllerFor('application').set('inLoginRoute', true);
14 | }
15 |
16 | resetController() {
17 | super.resetController(...arguments);
18 | this.controllerFor('application').set('inLoginRoute', false);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/tests/lib/test_files.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import pytest
4 |
5 | from skylines.lib import files
6 | from skylines.lib.types import is_unicode
7 |
8 |
9 | @pytest.mark.parametrize(
10 | "input,expected",
11 | [
12 | (b"foo/bar/baz.igc", u"baz.igc"),
13 | (u"HERR.müller@123.igc", u"herr.m_ller_123.igc"),
14 | (u"abc/...1234.igc", u"1234.igc"),
15 | (u"", u"empty"),
16 | ],
17 | )
18 | def test_sanitise_filename(input, expected):
19 | output = files.sanitise_filename(input)
20 |
21 | assert is_unicode(output)
22 | assert output == expected
23 |
--------------------------------------------------------------------------------
/ember/app/components/contest-layer-feature.js:
--------------------------------------------------------------------------------
1 | import Component from '@glimmer/component';
2 |
3 | import Feature from 'ol/Feature';
4 |
5 | export default class ContestLayerFeature extends Component {
6 | constructor() {
7 | super(...arguments);
8 |
9 | const { contest, source } = this.args;
10 |
11 | let { geometry, isTriangle } = contest;
12 | this.feature = new Feature({ geometry, isTriangle });
13 |
14 | source.addFeature(this.feature);
15 | }
16 |
17 | willDestroy() {
18 | super.willDestroy(...arguments);
19 | this.args.source.removeFeature(this.feature);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/migrations/versions/2dade673f10e_add_qnh_column_to_flight.py:
--------------------------------------------------------------------------------
1 | # revision identifiers, used by Alembic.
2 | revision = "2dade673f10e"
3 | down_revision = "1d8eda758ba6"
4 |
5 | from alembic import op
6 | import sqlalchemy as sa
7 |
8 |
9 | def upgrade():
10 | ### commands auto generated by Alembic - please adjust! ###
11 | op.add_column("flights", sa.Column("qnh", sa.Float(), nullable=True))
12 | ### end Alembic commands ###
13 |
14 |
15 | def downgrade():
16 | ### commands auto generated by Alembic - please adjust! ###
17 | op.drop_column("flights", "qnh")
18 | ### end Alembic commands ###
19 |
--------------------------------------------------------------------------------
/ember/app/templates/tracking/details.hbs:
--------------------------------------------------------------------------------
1 | {{#if (eq model.pilots.length 1)}}
2 | {{page-title (t "live-tracking-of-PILOT" pilot=model.pilots.0.name)}}
3 | {{else if (eq model.pilots.length 2)}}
4 | {{page-title (t "live-tracking-of-PILOT-and-PILOT2" pilot=model.pilots.0.name pilot2=model.pilots.1.name)}}
5 | {{else}}
6 | {{page-title (t "live-tracking-of-PILOT-and-NUM-other-pilots" pilot=model.pilots.0.name num=(sum model.pilots.length -1))}}
7 | {{/if}}
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/info-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/wrench.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/skylines/worker/celery.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from celery import Celery
3 |
4 |
5 | def init_app(self, app):
6 | self.name = app.import_name
7 | self.config_from_object(app.config)
8 |
9 | TaskBase = self.Task
10 |
11 | class ContextTask(TaskBase):
12 | abstract = True
13 |
14 | def __call__(self, *args, **kwargs):
15 | with app.app_context():
16 | return TaskBase.__call__(self, *args, **kwargs)
17 |
18 | self.Task = ContextTask
19 |
20 |
21 | celery = Celery(include=["skylines.worker.tasks"])
22 | Celery.init_app = init_app
23 |
--------------------------------------------------------------------------------
/ember/app/components/circling-performance-table.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | |
5 | {{t "count"}} {{t "avg-vario-abbr"}} |
6 | {{t "duration"}} ΔH |
7 |
8 | {{#if this.total}}{{/if}}
9 | {{#if this.left}}{{/if}}
10 | {{#if this.right}}{{/if}}
11 | {{#if this.mixed}}{{/if}}
12 |
13 |
--------------------------------------------------------------------------------
/migrations/versions/66650ad3d70_unregistered_pilot_n.py:
--------------------------------------------------------------------------------
1 | # revision identifiers, used by Alembic.
2 | revision = "66650ad3d70"
3 | down_revision = "3e82b37a4989"
4 |
5 | from alembic import op
6 | import sqlalchemy as sa
7 |
8 |
9 | def upgrade():
10 | op.add_column(
11 | "flights", sa.Column("pilot_name", sa.Unicode(length=255), nullable=True)
12 | )
13 | op.add_column(
14 | "flights", sa.Column("co_pilot_name", sa.Unicode(length=255), nullable=True)
15 | )
16 |
17 |
18 | def downgrade():
19 | op.drop_column("flights", "pilot_name")
20 | op.drop_column("flights", "co_pilot_name")
21 |
--------------------------------------------------------------------------------
/skylines/frontend/views/assets.py:
--------------------------------------------------------------------------------
1 | import os.path
2 |
3 | from flask import Blueprint, current_app, send_from_directory, send_file
4 | from werkzeug.exceptions import NotFound
5 |
6 | assets_blueprint = Blueprint("assets", "skylines")
7 |
8 |
9 | @assets_blueprint.route("/")
10 | @assets_blueprint.route("/")
11 | def static(**kwargs):
12 | path = kwargs.get("path", "")
13 | assets_folder = current_app.config.get("ASSETS_LOAD_DIR")
14 | try:
15 | return send_from_directory(assets_folder, path)
16 | except NotFound:
17 | return send_file(os.path.join(assets_folder, "index.html"))
18 |
--------------------------------------------------------------------------------
/skylines/tracking/crc.py:
--------------------------------------------------------------------------------
1 | import struct
2 | from crc16 import crc16xmodem
3 |
4 |
5 | def calc_crc(data):
6 | assert len(data) >= 16
7 |
8 | crc = crc16xmodem(data[:4])
9 | crc = crc16xmodem(b"\0\0", crc)
10 | crc = crc16xmodem(data[6:], crc)
11 | return crc
12 |
13 |
14 | def check_crc(data):
15 | assert len(data) >= 16
16 |
17 | crc1 = calc_crc(data)
18 | crc2 = struct.unpack_from("!H", data, 4)[0]
19 | return crc1 == crc2
20 |
21 |
22 | def set_crc(data):
23 | assert len(data) >= 16
24 |
25 | crc = calc_crc(data)
26 | return data[:4] + struct.pack("!H", crc) + data[6:]
27 |
--------------------------------------------------------------------------------
/ember/app/components/circling-performance-table.js:
--------------------------------------------------------------------------------
1 | import { computed } from '@ember/object';
2 | import Component from '@glimmer/component';
3 |
4 | export default class CirclingPerformanceTable extends Component {
5 | @findBy('perf', 'circlingDirection', 'left') left;
6 | @findBy('perf', 'circlingDirection', 'right') right;
7 | @findBy('perf', 'circlingDirection', 'mixed') mixed;
8 | @findBy('perf', 'circlingDirection', 'total') total;
9 | }
10 |
11 | function findBy(array, key, value) {
12 | return computed(`${array}.@each.${key}`, function () {
13 | return this.args[array]?.findBy(key, value);
14 | });
15 | }
16 |
--------------------------------------------------------------------------------
/migrations/versions/58325345d375_add_weglide_fields_to_igcfile.py:
--------------------------------------------------------------------------------
1 | # revision identifiers, used by Alembic.
2 | revision = "58325345d375"
3 | down_revision = "70a6f5e6f0e1"
4 |
5 | from alembic import op
6 | import sqlalchemy as sa
7 | from sqlalchemy.dialects.postgresql import JSONB
8 |
9 |
10 | def upgrade():
11 | op.add_column("igc_files", sa.Column("weglide_data", JSONB(), nullable=True))
12 | op.add_column("igc_files", sa.Column("weglide_status", sa.Integer(), nullable=True))
13 |
14 |
15 | def downgrade():
16 | op.drop_column("igc_files", "weglide_status")
17 | op.drop_column("igc_files", "weglide_data")
18 |
--------------------------------------------------------------------------------
/ember/app/components/login-form.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 | import { inject as service } from '@ember/service';
3 |
4 | import { task } from 'ember-concurrency';
5 |
6 | export default class LoginForm extends Component {
7 | tagName = '';
8 |
9 | @service session;
10 |
11 | inline = false;
12 | error = null;
13 |
14 | @(task(function* () {
15 | let { email, password } = this;
16 |
17 | try {
18 | yield this.session.authenticate('authenticator:oauth2', email, password);
19 | } catch (error) {
20 | this.set('error', error);
21 | }
22 | }).drop())
23 | loginTask;
24 | }
25 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/asterisk.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/download.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/migrations/versions/1f33435e1753_added_valid_until_co.py:
--------------------------------------------------------------------------------
1 | # revision identifiers, used by Alembic.
2 | revision = "1f33435e1753"
3 | down_revision = "ffa5706b1fb"
4 |
5 | from alembic import op
6 | import sqlalchemy as sa
7 |
8 |
9 | def upgrade():
10 | ### commands auto generated by Alembic - please adjust! ###
11 | op.add_column("airports", sa.Column("valid_until", sa.DateTime(), nullable=True))
12 | ### end Alembic commands ###
13 |
14 |
15 | def downgrade():
16 | ### commands auto generated by Alembic - please adjust! ###
17 | op.drop_column("airports", "valid_until")
18 | ### end Alembic commands ###
19 |
--------------------------------------------------------------------------------
/migrations/versions/8a8e921cb88_added_scoring_window_columns.py:
--------------------------------------------------------------------------------
1 | # revision identifiers, used by Alembic.
2 | revision = "8a8e921cb88"
3 | down_revision = "54d325616135"
4 |
5 | from alembic import op
6 | import sqlalchemy as sa
7 |
8 |
9 | def upgrade():
10 | op.add_column(
11 | "flights", sa.Column("scoring_start_time", sa.DateTime(), nullable=True)
12 | )
13 | op.add_column(
14 | "flights", sa.Column("scoring_end_time", sa.DateTime(), nullable=True)
15 | )
16 |
17 |
18 | def downgrade():
19 | op.drop_column("flights", "scoring_start_time")
20 | op.drop_column("flights", "scoring_end_time")
21 |
--------------------------------------------------------------------------------
/ember/app/routes/flights/pinned.js:
--------------------------------------------------------------------------------
1 | import { inject as service } from '@ember/service';
2 |
3 | import BaseRoute from './-base';
4 |
5 | export default class PinnedRoute extends BaseRoute {
6 | @service pinnedFlights;
7 |
8 | model(params) {
9 | let pinned = this.get('pinnedFlights.pinned') || [];
10 | if (pinned.length === 0) {
11 | return { count: 0, flights: [] };
12 | }
13 |
14 | return this.ajax.request(`/api/flights/list/${pinned.join(',')}`, {
15 | data: {
16 | page: params.page,
17 | column: params.column,
18 | order: params.order,
19 | },
20 | });
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/rotate-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/app/components/aircraft-model-select.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 | import { computed } from '@ember/object';
3 |
4 | export default Component.extend({
5 | tagName: '',
6 |
7 | models: null,
8 | modelId: null,
9 |
10 | modelsWithNull: computed('models.[]', function () {
11 | return [{ id: null }].concat(this.models);
12 | }),
13 |
14 | model: computed('modelsWithNull.@each.id', 'modelId', function () {
15 | return this.modelsWithNull.findBy('id', this.modelId || null);
16 | }),
17 |
18 | actions: {
19 | onChange(model) {
20 | this.onChange(model.id);
21 | },
22 | },
23 | });
24 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/sign-in.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/sign-out.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/upload.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/skylines/api/args.py:
--------------------------------------------------------------------------------
1 | from flask import abort
2 | from webargs import fields
3 |
4 | from skylines.model import Location
5 |
6 |
7 | pagination_args = {
8 | "page": fields.Integer(missing=1, location="query", validate=lambda val: val > 0),
9 | "per_page": fields.Integer(
10 | missing=30, location="query", validate=lambda val: val <= 100
11 | ),
12 | }
13 |
14 |
15 | def parse_location(args):
16 | try:
17 | latitude = float(args["lat"])
18 | longitude = float(args["lon"])
19 | return Location(latitude=latitude, longitude=longitude)
20 |
21 | except (KeyError, ValueError):
22 | abort(400)
23 |
--------------------------------------------------------------------------------
/skylines/api/views/aircraft_models.py:
--------------------------------------------------------------------------------
1 | from flask import Blueprint
2 |
3 | from skylines.api.json import jsonify
4 | from skylines.model import AircraftModel
5 | from skylines.schemas import AircraftModelSchema
6 |
7 | aircraft_models_blueprint = Blueprint("aircraft_models", "skylines")
8 |
9 |
10 | @aircraft_models_blueprint.route("/aircraft-models", strict_slashes=False)
11 | def index():
12 | models = (
13 | AircraftModel.query()
14 | .order_by(AircraftModel.kind)
15 | .order_by(AircraftModel.name)
16 | .all()
17 | )
18 |
19 | return jsonify(models=AircraftModelSchema().dump(models, many=True).data)
20 |
--------------------------------------------------------------------------------
/ember/app/components/fullscreen-button.js:
--------------------------------------------------------------------------------
1 | import { action } from '@ember/object';
2 | import { inject as service } from '@ember/service';
3 | import Component from '@glimmer/component';
4 |
5 | import screenfull from 'screenfull';
6 |
7 | export default class FullscreenButton extends Component {
8 | @service intl;
9 | @service notifications;
10 |
11 | @action toggle() {
12 | if (screenfull.isEnabled) {
13 | let element = this.args.fullscreenElement;
14 | screenfull.toggle(document.querySelector(element));
15 | } else {
16 | this.notifications.warning(this.intl.t('fullscreen-not-supported'));
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/rotate-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/public/svg/icons/question.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/ember/lib/freestyle/app/templates/freestyle.hbs:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 | {{#each components as |componentName|}}
17 |
18 | {{component (concat "usage/" componentName)}}
19 |
20 | {{/each}}
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/migrations/versions/16f9f28f0ea3_added_privacy_level_column.py:
--------------------------------------------------------------------------------
1 | # revision identifiers, used by Alembic.
2 | revision = "16f9f28f0ea3"
3 | down_revision = "8a8e921cb88"
4 |
5 | from alembic import op
6 | import sqlalchemy as sa
7 |
8 |
9 | flights = sa.sql.table("flights", sa.sql.column("privacy_level", sa.SmallInteger))
10 |
11 |
12 | def upgrade():
13 | op.add_column("flights", sa.Column("privacy_level", sa.SmallInteger()))
14 |
15 | op.execute(flights.update().values({"privacy_level": 0}))
16 |
17 | op.alter_column("flights", "privacy_level", nullable=False)
18 |
19 |
20 | def downgrade():
21 | op.drop_column("flights", "privacy_level")
22 |
--------------------------------------------------------------------------------