mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-11 17:51:39 -05:00
Merge branch 'Freika:master' into fix/import-google-timeline
This commit is contained in:
commit
2231c0aab9
24 changed files with 461 additions and 304 deletions
|
|
@ -1 +1 @@
|
|||
0.14.4
|
||||
0.14.5
|
||||
|
|
|
|||
14
CHANGELOG.md
14
CHANGELOG.md
|
|
@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
# [0.14.5] - 2024-09-28
|
||||
|
||||
### Fixed
|
||||
|
||||
- GPX export now finishes correctly and does not throw an error in the end
|
||||
- Deleting points from the Points page now preserves `start_at` and `end_at` values for the routes. #261
|
||||
- Visits map now being rendered correctly in the Visits page. #262
|
||||
- Fixed issue with timezones for negative UTC offsets. #194, #122
|
||||
- Point page is no longer reloads losing provided timestamps when searching for points on Points page. #283
|
||||
|
||||
### Changed
|
||||
|
||||
- Map layers from Stadia were disabled for now due to necessary API key
|
||||
|
||||
# [0.14.4] - 2024-09-24
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#### **Did you fix whitespace, format code, or make a purely cosmetic patch?**
|
||||
|
||||
Changes that are cosmetic in nature and do not add anything substantial to the stability, functionality, or testability of Dawarich will generally not be accepted.
|
||||
Changes that are cosmetic in nature and do not add anything substantial to the stability, functionality, or testability of Dawarich will generally not be accepted. Same goes for chore changes, like updating dependencies, fixing typos, etc.
|
||||
|
||||
#### **Do you intend to add a new feature or change an existing one?**
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -4,14 +4,12 @@ class PointsController < ApplicationController
|
|||
before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
order_by = params[:order_by] || 'desc'
|
||||
|
||||
@points = points
|
||||
.without_raw_data
|
||||
.where(timestamp: start_at..end_at)
|
||||
.order(timestamp: order_by)
|
||||
.page(params[:page])
|
||||
.per(50)
|
||||
.without_raw_data
|
||||
.where(timestamp: start_at..end_at)
|
||||
.order(timestamp: order_by)
|
||||
.page(params[:page])
|
||||
.per(50)
|
||||
|
||||
@start_at = Time.zone.at(start_at)
|
||||
@end_at = Time.zone.at(end_at)
|
||||
|
|
@ -22,7 +20,9 @@ class PointsController < ApplicationController
|
|||
def bulk_destroy
|
||||
current_user.tracked_points.where(id: params[:point_ids].compact).destroy_all
|
||||
|
||||
redirect_to points_url, notice: 'Points were successfully destroyed.', status: :see_other
|
||||
redirect_to points_url(preserved_params),
|
||||
notice: 'Points were successfully destroyed.',
|
||||
status: :see_other
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -44,14 +44,22 @@ class PointsController < ApplicationController
|
|||
end
|
||||
|
||||
def points
|
||||
params[:import_id] ? points_from_import : points_from_user
|
||||
params[:import_id].present? ? import_points : user_points
|
||||
end
|
||||
|
||||
def points_from_import
|
||||
def import_points
|
||||
current_user.imports.find(params[:import_id]).points
|
||||
end
|
||||
|
||||
def points_from_user
|
||||
def user_points
|
||||
current_user.tracked_points
|
||||
end
|
||||
|
||||
def order_by
|
||||
params[:order_by] || 'desc'
|
||||
end
|
||||
|
||||
def preserved_params
|
||||
params.to_enum.to_h.with_indifferent_access.slice(:start_at, :end_at, :order_by, :import_id)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def year_timespan(year)
|
||||
start_at = Time.utc(year).in_time_zone(Time.zone).beginning_of_year.strftime('%Y-%m-%dT%H:%M')
|
||||
end_at = Time.utc(year).in_time_zone(Time.zone).end_of_year.strftime('%Y-%m-%dT%H:%M')
|
||||
start_at = Time.new(year).beginning_of_year.strftime('%Y-%m-%dT%H:%M')
|
||||
end_at = Time.new(year).end_of_year.strftime('%Y-%m-%dT%H:%M')
|
||||
|
||||
{ start_at:, end_at: }
|
||||
end
|
||||
|
||||
def timespan(month, year)
|
||||
month = DateTime.new(year, month).in_time_zone(Time.zone)
|
||||
month = DateTime.new(year, month)
|
||||
start_at = month.beginning_of_month.to_time.strftime('%Y-%m-%dT%H:%M')
|
||||
end_at = month.end_of_month.to_time.strftime('%Y-%m-%dT%H:%M')
|
||||
|
||||
|
|
|
|||
|
|
@ -10,16 +10,16 @@ import { osmMapLayer } from "../maps/layers";
|
|||
import { osmHotMapLayer } from "../maps/layers";
|
||||
import { OPNVMapLayer } from "../maps/layers";
|
||||
import { openTopoMapLayer } from "../maps/layers";
|
||||
import { stadiaAlidadeSmoothMapLayer } from "../maps/layers";
|
||||
import { stadiaAlidadeSmoothDarkMapLayer } from "../maps/layers";
|
||||
import { stadiaAlidadeSatelliteMapLayer } from "../maps/layers";
|
||||
import { stadiaOsmBrightMapLayer } from "../maps/layers";
|
||||
import { stadiaOutdoorMapLayer } from "../maps/layers";
|
||||
import { stadiaStamenTonerMapLayer } from "../maps/layers";
|
||||
import { stadiaStamenTonerBackgroundMapLayer } from "../maps/layers";
|
||||
import { stadiaStamenTonerLiteMapLayer } from "../maps/layers";
|
||||
import { stadiaStamenWatercolorMapLayer } from "../maps/layers";
|
||||
import { stadiaStamenTerrainMapLayer } from "../maps/layers";
|
||||
// import { stadiaAlidadeSmoothMapLayer } from "../maps/layers";
|
||||
// import { stadiaAlidadeSmoothDarkMapLayer } from "../maps/layers";
|
||||
// import { stadiaAlidadeSatelliteMapLayer } from "../maps/layers";
|
||||
// import { stadiaOsmBrightMapLayer } from "../maps/layers";
|
||||
// import { stadiaOutdoorMapLayer } from "../maps/layers";
|
||||
// import { stadiaStamenTonerMapLayer } from "../maps/layers";
|
||||
// import { stadiaStamenTonerBackgroundMapLayer } from "../maps/layers";
|
||||
// import { stadiaStamenTonerLiteMapLayer } from "../maps/layers";
|
||||
// import { stadiaStamenWatercolorMapLayer } from "../maps/layers";
|
||||
// import { stadiaStamenTerrainMapLayer } from "../maps/layers";
|
||||
import { cyclOsmMapLayer } from "../maps/layers";
|
||||
import { esriWorldStreetMapLayer } from "../maps/layers";
|
||||
import { esriWorldTopoMapLayer } from "../maps/layers";
|
||||
|
|
@ -143,16 +143,16 @@ console.log(selectedLayerName);
|
|||
"OpenStreetMap.HOT": osmHotMapLayer(this.map, selectedLayerName),
|
||||
OPNV: OPNVMapLayer(this.map, selectedLayerName),
|
||||
openTopo: openTopoMapLayer(this.map, selectedLayerName),
|
||||
stadiaAlidadeSmooth: stadiaAlidadeSmoothMapLayer(this.map, selectedLayerName),
|
||||
stadiaAlidadeSmoothDark: stadiaAlidadeSmoothDarkMapLayer(this.map, selectedLayerName),
|
||||
stadiaAlidadeSatellite: stadiaAlidadeSatelliteMapLayer(this.map, selectedLayerName),
|
||||
stadiaOsmBright: stadiaOsmBrightMapLayer(this.map, selectedLayerName),
|
||||
stadiaOutdoor: stadiaOutdoorMapLayer(this.map, selectedLayerName),
|
||||
stadiaStamenToner: stadiaStamenTonerMapLayer(this.map, selectedLayerName),
|
||||
stadiaStamenTonerBackground: stadiaStamenTonerBackgroundMapLayer(this.map, selectedLayerName),
|
||||
stadiaStamenTonerLite: stadiaStamenTonerLiteMapLayer(this.map, selectedLayerName),
|
||||
stadiaStamenWatercolor: stadiaStamenWatercolorMapLayer(this.map, selectedLayerName),
|
||||
stadiaStamenTerrain: stadiaStamenTerrainMapLayer(this.map, selectedLayerName),
|
||||
// stadiaAlidadeSmooth: stadiaAlidadeSmoothMapLayer(this.map, selectedLayerName),
|
||||
// stadiaAlidadeSmoothDark: stadiaAlidadeSmoothDarkMapLayer(this.map, selectedLayerName),
|
||||
// stadiaAlidadeSatellite: stadiaAlidadeSatelliteMapLayer(this.map, selectedLayerName),
|
||||
// stadiaOsmBright: stadiaOsmBrightMapLayer(this.map, selectedLayerName),
|
||||
// stadiaOutdoor: stadiaOutdoorMapLayer(this.map, selectedLayerName),
|
||||
// stadiaStamenToner: stadiaStamenTonerMapLayer(this.map, selectedLayerName),
|
||||
// stadiaStamenTonerBackground: stadiaStamenTonerBackgroundMapLayer(this.map, selectedLayerName),
|
||||
// stadiaStamenTonerLite: stadiaStamenTonerLiteMapLayer(this.map, selectedLayerName),
|
||||
// stadiaStamenWatercolor: stadiaStamenWatercolorMapLayer(this.map, selectedLayerName),
|
||||
// stadiaStamenTerrain: stadiaStamenTerrainMapLayer(this.map, selectedLayerName),
|
||||
cyclOsm: cyclOsmMapLayer(this.map, selectedLayerName),
|
||||
esriWorldStreet: esriWorldStreetMapLayer(this.map, selectedLayerName),
|
||||
esriWorldTopo: esriWorldTopoMapLayer(this.map, selectedLayerName),
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export default class extends Controller {
|
|||
this.radius = this.element.dataset.radius;
|
||||
this.map = L.map(this.containerTarget).setView([this.center[0], this.center[1]], 17);
|
||||
|
||||
osmMapLayer(this.map),
|
||||
osmMapLayer(this.map, "OpenStreetMap")
|
||||
this.addMarkers();
|
||||
|
||||
L.circle([this.center[0], this.center[1]], {
|
||||
|
|
|
|||
|
|
@ -57,165 +57,165 @@ export function openTopoMapLayer(map, selectedLayerName) {
|
|||
}
|
||||
}
|
||||
|
||||
export function stadiaAlidadeSmoothMapLayer(map, selectedLayerName) {
|
||||
let layerName = 'stadiaAlidadeSmooth';
|
||||
let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_smooth/{z}/{x}/{y}{r}.{ext}', {
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
ext: 'png'
|
||||
});
|
||||
// export function stadiaAlidadeSmoothMapLayer(map, selectedLayerName) {
|
||||
// let layerName = 'stadiaAlidadeSmooth';
|
||||
// let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_smooth/{z}/{x}/{y}{r}.{ext}', {
|
||||
// minZoom: 0,
|
||||
// maxZoom: 20,
|
||||
// attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
// ext: 'png'
|
||||
// });
|
||||
|
||||
if (selectedLayerName === layerName) {
|
||||
return layer.addTo(map);
|
||||
} else {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
// if (selectedLayerName === layerName) {
|
||||
// return layer.addTo(map);
|
||||
// } else {
|
||||
// return layer;
|
||||
// }
|
||||
// }
|
||||
|
||||
export function stadiaAlidadeSmoothDarkMapLayer(map, selectedLayerName) {
|
||||
let layerName = 'stadiaAlidadeSmoothDark';
|
||||
let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.{ext}', {
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
ext: 'png'
|
||||
});
|
||||
// export function stadiaAlidadeSmoothDarkMapLayer(map, selectedLayerName) {
|
||||
// let layerName = 'stadiaAlidadeSmoothDark';
|
||||
// let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.{ext}', {
|
||||
// minZoom: 0,
|
||||
// maxZoom: 20,
|
||||
// attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
// ext: 'png'
|
||||
// });
|
||||
|
||||
if (selectedLayerName === layerName) {
|
||||
return layer.addTo(map);
|
||||
} else {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
// if (selectedLayerName === layerName) {
|
||||
// return layer.addTo(map);
|
||||
// } else {
|
||||
// return layer;
|
||||
// }
|
||||
// }
|
||||
|
||||
export function stadiaAlidadeSatelliteMapLayer(map, selectedLayerName) {
|
||||
let layerName = 'stadiaAlidadeSatellite';
|
||||
let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_satellite/{z}/{x}/{y}{r}.{ext}', {
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
attribution: '© CNES, Distribution Airbus DS, © Airbus DS, © PlanetObserver (Contains Copernicus Data) | © <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
ext: 'jpg'
|
||||
});
|
||||
// export function stadiaAlidadeSatelliteMapLayer(map, selectedLayerName) {
|
||||
// let layerName = 'stadiaAlidadeSatellite';
|
||||
// let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_satellite/{z}/{x}/{y}{r}.{ext}', {
|
||||
// minZoom: 0,
|
||||
// maxZoom: 20,
|
||||
// attribution: '© CNES, Distribution Airbus DS, © Airbus DS, © PlanetObserver (Contains Copernicus Data) | © <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
// ext: 'jpg'
|
||||
// });
|
||||
|
||||
if (selectedLayerName === layerName) {
|
||||
return layer.addTo(map);
|
||||
} else {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
// if (selectedLayerName === layerName) {
|
||||
// return layer.addTo(map);
|
||||
// } else {
|
||||
// return layer;
|
||||
// }
|
||||
// }
|
||||
|
||||
export function stadiaOsmBrightMapLayer(map, selectedLayerName) {
|
||||
let layerName = 'stadiaOsmBright';
|
||||
let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/osm_bright/{z}/{x}/{y}{r}.{ext}', {
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
ext: 'png'
|
||||
});
|
||||
// export function stadiaOsmBrightMapLayer(map, selectedLayerName) {
|
||||
// let layerName = 'stadiaOsmBright';
|
||||
// let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/osm_bright/{z}/{x}/{y}{r}.{ext}', {
|
||||
// minZoom: 0,
|
||||
// maxZoom: 20,
|
||||
// attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
// ext: 'png'
|
||||
// });
|
||||
|
||||
if (selectedLayerName === layerName) {
|
||||
return layer.addTo(map);
|
||||
} else {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
// if (selectedLayerName === layerName) {
|
||||
// return layer.addTo(map);
|
||||
// } else {
|
||||
// return layer;
|
||||
// }
|
||||
// }
|
||||
|
||||
export function stadiaOutdoorMapLayer(map, selectedLayerName) {
|
||||
let layerName = 'stadiaOutdoor';
|
||||
let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/outdoors/{z}/{x}/{y}{r}.{ext}', {
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
ext: 'png'
|
||||
});
|
||||
// export function stadiaOutdoorMapLayer(map, selectedLayerName) {
|
||||
// let layerName = 'stadiaOutdoor';
|
||||
// let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/outdoors/{z}/{x}/{y}{r}.{ext}', {
|
||||
// minZoom: 0,
|
||||
// maxZoom: 20,
|
||||
// attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
// ext: 'png'
|
||||
// });
|
||||
|
||||
if (selectedLayerName === layerName) {
|
||||
return layer.addTo(map);
|
||||
} else {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
// if (selectedLayerName === layerName) {
|
||||
// return layer.addTo(map);
|
||||
// } else {
|
||||
// return layer;
|
||||
// }
|
||||
// }
|
||||
|
||||
export function stadiaStamenTonerMapLayer(map, selectedLayerName) {
|
||||
let layerName = 'stadiaStamenToner';
|
||||
let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/stamen_toner/{z}/{x}/{y}{r}.{ext}', {
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://www.stamen.com/" target="_blank">Stamen Design</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
ext: 'png'
|
||||
});
|
||||
// export function stadiaStamenTonerMapLayer(map, selectedLayerName) {
|
||||
// let layerName = 'stadiaStamenToner';
|
||||
// let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/stamen_toner/{z}/{x}/{y}{r}.{ext}', {
|
||||
// minZoom: 0,
|
||||
// maxZoom: 20,
|
||||
// attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://www.stamen.com/" target="_blank">Stamen Design</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
// ext: 'png'
|
||||
// });
|
||||
|
||||
if (selectedLayerName === layerName) {
|
||||
return layer.addTo(map);
|
||||
} else {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
// if (selectedLayerName === layerName) {
|
||||
// return layer.addTo(map);
|
||||
// } else {
|
||||
// return layer;
|
||||
// }
|
||||
// }
|
||||
|
||||
export function stadiaStamenTonerBackgroundMapLayer(map, selectedLayerName) {
|
||||
let layerName = 'stadiaStamenTonerBackground';
|
||||
let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/stamen_toner_background/{z}/{x}/{y}{r}.{ext}', {
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://www.stamen.com/" target="_blank">Stamen Design</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
ext: 'png'
|
||||
});
|
||||
// export function stadiaStamenTonerBackgroundMapLayer(map, selectedLayerName) {
|
||||
// let layerName = 'stadiaStamenTonerBackground';
|
||||
// let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/stamen_toner_background/{z}/{x}/{y}{r}.{ext}', {
|
||||
// minZoom: 0,
|
||||
// maxZoom: 20,
|
||||
// attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://www.stamen.com/" target="_blank">Stamen Design</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
// ext: 'png'
|
||||
// });
|
||||
|
||||
if (selectedLayerName === layerName) {
|
||||
return layer.addTo(map);
|
||||
} else {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
// if (selectedLayerName === layerName) {
|
||||
// return layer.addTo(map);
|
||||
// } else {
|
||||
// return layer;
|
||||
// }
|
||||
// }
|
||||
|
||||
export function stadiaStamenTonerLiteMapLayer(map, selectedLayerName) {
|
||||
let layerName = 'stadiaStamenTonerLite';
|
||||
let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/stamen_toner_lite/{z}/{x}/{y}{r}.{ext}', {
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://www.stamen.com/" target="_blank">Stamen Design</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
ext: 'png'
|
||||
});
|
||||
// export function stadiaStamenTonerLiteMapLayer(map, selectedLayerName) {
|
||||
// let layerName = 'stadiaStamenTonerLite';
|
||||
// let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/stamen_toner_lite/{z}/{x}/{y}{r}.{ext}', {
|
||||
// minZoom: 0,
|
||||
// maxZoom: 20,
|
||||
// attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://www.stamen.com/" target="_blank">Stamen Design</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
// ext: 'png'
|
||||
// });
|
||||
|
||||
if (selectedLayerName === layerName) {
|
||||
return layer.addTo(map);
|
||||
} else {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
// if (selectedLayerName === layerName) {
|
||||
// return layer.addTo(map);
|
||||
// } else {
|
||||
// return layer;
|
||||
// }
|
||||
// }
|
||||
|
||||
export function stadiaStamenWatercolorMapLayer(map, selectedLayerName) {
|
||||
let layerName = 'stadiaStamenWatercolor';
|
||||
let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.{ext}', {
|
||||
minZoom: 1,
|
||||
maxZoom: 16,
|
||||
attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://www.stamen.com/" target="_blank">Stamen Design</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
ext: 'jpg'
|
||||
});
|
||||
// export function stadiaStamenWatercolorMapLayer(map, selectedLayerName) {
|
||||
// let layerName = 'stadiaStamenWatercolor';
|
||||
// let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.{ext}', {
|
||||
// minZoom: 1,
|
||||
// maxZoom: 16,
|
||||
// attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://www.stamen.com/" target="_blank">Stamen Design</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
// ext: 'jpg'
|
||||
// });
|
||||
|
||||
if (selectedLayerName === layerName) {
|
||||
return layer.addTo(map);
|
||||
} else {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
// if (selectedLayerName === layerName) {
|
||||
// return layer.addTo(map);
|
||||
// } else {
|
||||
// return layer;
|
||||
// }
|
||||
// }
|
||||
|
||||
export function stadiaStamenTerrainMapLayer(map, selectedLayerName) {
|
||||
let layerName = 'stadiaStamenTerrain';
|
||||
let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/stamen_terrain/{z}/{x}/{y}{r}.{ext}', {
|
||||
minZoom: 0,
|
||||
maxZoom: 18,
|
||||
attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://www.stamen.com/" target="_blank">Stamen Design</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
ext: 'png'
|
||||
});
|
||||
// export function stadiaStamenTerrainMapLayer(map, selectedLayerName) {
|
||||
// let layerName = 'stadiaStamenTerrain';
|
||||
// let layer = L.tileLayer('https://tiles.stadiamaps.com/tiles/stamen_terrain/{z}/{x}/{y}{r}.{ext}', {
|
||||
// minZoom: 0,
|
||||
// maxZoom: 18,
|
||||
// attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://www.stamen.com/" target="_blank">Stamen Design</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
// ext: 'png'
|
||||
// });
|
||||
|
||||
if (selectedLayerName === layerName) {
|
||||
return layer.addTo(map);
|
||||
} else {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
// if (selectedLayerName === layerName) {
|
||||
// return layer.addTo(map);
|
||||
// } else {
|
||||
// return layer;
|
||||
// }
|
||||
// }
|
||||
|
||||
export function cyclOsmMapLayer(map, selectedLayerName) {
|
||||
let layerName = 'cyclOsm';
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class Points::GpxSerializer
|
|||
gpx.waypoints << GPX::Waypoint.new(
|
||||
lat: point.latitude.to_f,
|
||||
lon: point.longitude.to_f,
|
||||
time: point.recorded_at.strftime('%FT%R:%SZ'),
|
||||
time: point.recorded_at,
|
||||
ele: point.altitude.to_f
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class Exports::Create
|
|||
def points_data(points)
|
||||
case file_format.to_sym
|
||||
when :json then process_geojson_export(points)
|
||||
when :gpx then process_gpx_export(points)
|
||||
when :gpx then process_gpx_export(points)
|
||||
else raise ArgumentError, "Unsupported file format: #{file_format}"
|
||||
end
|
||||
end
|
||||
|
|
@ -75,6 +75,7 @@ class Exports::Create
|
|||
dir_path = Rails.root.join('public', 'exports')
|
||||
Dir.mkdir(dir_path) unless Dir.exist?(dir_path)
|
||||
file_path = dir_path.join("#{export.name}.#{file_format}")
|
||||
|
||||
File.open(file_path, 'w') { |file| file.write(data) }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -57,15 +57,15 @@ class GoogleMaps::SemanticHistoryParser
|
|||
}
|
||||
end
|
||||
elsif timeline_object['placeVisit'].present?
|
||||
if timeline_object['placeVisit']['location']['latitudeE7'].present? &&
|
||||
timeline_object['placeVisit']['location']['longitudeE7'].present?
|
||||
if timeline_object.dig('placeVisit', 'location', 'latitudeE7').present? &&
|
||||
timeline_object.dig('placeVisit', 'location', 'longitudeE7').present?
|
||||
{
|
||||
latitude: timeline_object['placeVisit']['location']['latitudeE7'].to_f / 10**7,
|
||||
longitude: timeline_object['placeVisit']['location']['longitudeE7'].to_f / 10**7,
|
||||
timestamp: Timestamps::parse_timestamp(timeline_object['placeVisit']['duration']['startTimestamp'] || timeline_object['placeVisit']['duration']['startTimestampMs']),
|
||||
raw_data: timeline_object
|
||||
}
|
||||
elsif timeline_object['placeVisit']['otherCandidateLocations'].any?
|
||||
elsif timeline_object.dig('placeVisit', 'otherCandidateLocations')&.any?
|
||||
point = timeline_object['placeVisit']['otherCandidateLocations'][0]
|
||||
|
||||
next unless point['latitudeE7'].present? && point['longitudeE7'].present?
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class Visits::Suggest
|
|||
|
||||
create_visits_notification(user)
|
||||
|
||||
nil unless reverse_geocoding_enabled?
|
||||
return nil unless reverse_geocoding_enabled?
|
||||
|
||||
reverse_geocode(visits)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<% content_for :title, 'Points' %>
|
||||
|
||||
<div class="w-full">
|
||||
<%= form_with url: points_path(import_id: params[:import_id]), method: :get do |f| %>
|
||||
<%= form_with url: points_path(import_id: params[:import_id]), data: { turbo_method: :get }, method: :get do |f| %>
|
||||
<div class="flex flex-col md:flex-row md:space-x-4 md:items-end">
|
||||
<div class="w-full md:w-2/12">
|
||||
<div class="flex flex-col space-y-2">
|
||||
|
|
@ -28,12 +28,12 @@
|
|||
</div>
|
||||
<div class="w-full md:w-2/12">
|
||||
<div class="flex flex-col space-y-2 text-center">
|
||||
<%= link_to 'Export as GeoJSON', exports_path(start_at: @start_at, end_at: @end_at, file_format: :json), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure? This will start background process of exporting points withing timeframe, selected between #{@start_at} and #{@end_at}", turbo_method: :post }, class: "px-4 py-2 bg-green-500 text-white rounded-md join-item" %>
|
||||
<%= link_to 'Export as GeoJSON', exports_path(start_at: @start_at, end_at: @end_at, file_format: :json), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure? This will start background process of exporting points within timeframe, selected between #{@start_at} and #{@end_at}", turbo_method: :post }, class: "px-4 py-2 bg-green-500 text-white rounded-md join-item" %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-2/12">
|
||||
<div class="flex flex-col space-y-2 text-center">
|
||||
<%= link_to 'Export as GPX', exports_path(start_at: @start_at, end_at: @end_at, file_format: :gpx), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure? This will start background process of exporting points withing timeframe, selected between #{@start_at} and #{@end_at}", turbo_method: :post }, class: "px-4 py-2 bg-green-500 text-white rounded-md join-item" %>
|
||||
<%= link_to 'Export as GPX', exports_path(start_at: @start_at, end_at: @end_at, file_format: :gpx), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure? This will start background process of exporting points within timeframe, selected between #{@start_at} and #{@end_at}", turbo_method: :post }, class: "px-4 py-2 bg-green-500 text-white rounded-md join-item" %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<li><%= link_to 'Points', points_url, class: "#{active_class?(points_url)}" %></li>
|
||||
<li><%= link_to 'Stats', stats_url, class: "#{active_class?(stats_url)}" %></li>
|
||||
<li><%= link_to 'Visits<sup>β</sup>'.html_safe, visits_url(status: :confirmed), class: "#{active_class?(visits_url)}" %></li>
|
||||
<li><%= link_to 'Places', places_url, class: "#{active_class?(places_url)}" %></li>
|
||||
<li><%= link_to 'Places<sup>β</sup>', places_url, class: "#{active_class?(places_url)}" %></li>
|
||||
<li><%= link_to 'Imports', imports_url, class: "#{active_class?(imports_url)}" %></li>
|
||||
<li><%= link_to 'Exports', exports_url, class: "#{active_class?(exports_url)}" %></li>
|
||||
</ul>
|
||||
|
|
@ -44,7 +44,7 @@
|
|||
<li><%= link_to 'Points', points_url, class: "#{active_class?(points_url)}" %></li>
|
||||
<li><%= link_to 'Stats', stats_url, class: "#{active_class?(stats_url)}" %></li>
|
||||
<li><%= link_to 'Visits<sup>β</sup>'.html_safe, visits_url(status: :confirmed), class: "#{active_class?(visits_url)}" %></li>
|
||||
<li><%= link_to 'Places', places_url, class: "#{active_class?(places_url)}" %></li>
|
||||
<li><%= link_to 'Places<sup>β</sup>', places_url, class: "#{active_class?(places_url)}" %></li>
|
||||
<li><%= link_to 'Imports', imports_url, class: "#{active_class?(imports_url)}" %></li>
|
||||
<li><%= link_to 'Exports', exports_url, class: "#{active_class?(exports_url)}" %></li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,26 @@ Make sure to exclude "http://" or "https://" from the environment variable. ⚠
|
|||
At the time of writing this, the way to set the environment variable is to edit the docker-compose.yml file. Find all APPLICATION_HOSTS entries in the docker-compose.yml file and make sure to include your domain name. Example:
|
||||
|
||||
```yaml
|
||||
dawarich_app:
|
||||
dawarich_app:
|
||||
image: freikin/dawarich:latest
|
||||
container_name: dawarich_app
|
||||
...
|
||||
environment:
|
||||
APPLICATION_HOSTS: "yourhost.com,www.yourhost.com,127.0.0.1"
|
||||
...
|
||||
APPLICATION_HOST: "yourhost.com" <------------------------------ Edit this
|
||||
APPLICATION_HOSTS: "yourhost.com,www.yourhost.com,127.0.0.1" <-- Edit this
|
||||
```
|
||||
|
||||
```yaml
|
||||
dawarich_sidekiq:
|
||||
image: freikin/dawarich:latest
|
||||
container_name: dawarich_sidekiq
|
||||
...
|
||||
environment:
|
||||
...
|
||||
APPLICATION_HOST: "yourhost.com" <------------------------------ Edit this
|
||||
APPLICATION_HOSTS: "yourhost.com,www.yourhost.com,127.0.0.1" <-- Edit this
|
||||
...
|
||||
```
|
||||
|
||||
For a Synology install, refer to **[Synology Install Tutorial](How_to_install_Dawarich_on_Synology.md)**. In this page, it is explained how to set the APPLICATION_HOSTS environment variable.
|
||||
|
|
@ -24,13 +39,42 @@ Now that the app works with a domain name, the server needs to be set up to use
|
|||
Below are examples of reverse proxy configurations.
|
||||
|
||||
### Nginx
|
||||
```
|
||||
```nginx
|
||||
server {
|
||||
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name example.com;
|
||||
|
||||
brotli on;
|
||||
brotli_comp_level 6;
|
||||
brotli_types
|
||||
text/css
|
||||
text/plain
|
||||
text/xml
|
||||
text/x-component
|
||||
text/javascript
|
||||
application/x-javascript
|
||||
application/javascript
|
||||
application/json
|
||||
application/manifest+json
|
||||
application/vnd.api+json
|
||||
application/xml
|
||||
application/xhtml+xml
|
||||
application/rss+xml
|
||||
application/atom+xml
|
||||
application/vnd.ms-fontobject
|
||||
application/x-font-ttf
|
||||
application/x-font-opentype
|
||||
application/x-font-truetype
|
||||
image/svg+xml
|
||||
image/x-icon
|
||||
image/vnd.microsoft.icon
|
||||
font/ttf
|
||||
font/eot
|
||||
font/otf
|
||||
font/opentype;
|
||||
|
||||
location / {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
|
@ -54,29 +98,34 @@ For Apache2, you might need to enable some modules. Start by entering the follow
|
|||
sudo a2enmod proxy
|
||||
sudo a2enmod proxy_http
|
||||
sudo a2enmod headers
|
||||
sudo a2enmod brotli
|
||||
```
|
||||
|
||||
With the above commands entered, the configuration below should work properly.
|
||||
|
||||
```
|
||||
```apache
|
||||
<VirtualHost *:80>
|
||||
ServerName example.com
|
||||
|
||||
ProxyRequests Off
|
||||
ProxyPreserveHost On
|
||||
|
||||
<Proxy *>
|
||||
Require all granted
|
||||
</Proxy>
|
||||
|
||||
Header always set X-Real-IP %{REMOTE_ADDR}s
|
||||
Header always set X-Forwarded-For %{REMOTE_ADDR}s
|
||||
Header always set X-Forwarded-Proto https
|
||||
Header always set X-Forwarded-Server %{SERVER_NAME}s
|
||||
Header always set Host %{HTTP_HOST}s
|
||||
|
||||
ProxyPass / http://127.0.0.1:3000/
|
||||
ProxyPassReverse / http://127.0.0.1:3000/
|
||||
ServerName example.com
|
||||
|
||||
ProxyRequests Off
|
||||
ProxyPreserveHost On
|
||||
|
||||
<Proxy *>
|
||||
Require all granted
|
||||
</Proxy>
|
||||
|
||||
Header always set X-Real-IP %{REMOTE_ADDR}s
|
||||
Header always set X-Forwarded-For %{REMOTE_ADDR}s
|
||||
Header always set X-Forwarded-Proto https
|
||||
Header always set X-Forwarded-Server %{SERVER_NAME}s
|
||||
Header always set Host %{HTTP_HOST}s
|
||||
|
||||
SetOutputFilter BROTLI
|
||||
AddOutputFilterByType BROTLI_COMPRESS text/css text/plain text/xml text/javascript application/javascript application/json application/manifest+json application/vnd.api+json application/xml application/xhtml+xml application/rss+xml application/atom+xml application/vnd.ms-fontobject application/x-font-ttf application/x-font-opentype application/x-font-truetype image/svg+xml image/x-icon image/vnd.microsoft.icon font/ttf font/eot font/otf font/opentype
|
||||
BrotliCompressionQuality 6
|
||||
|
||||
ProxyPass / http://127.0.0.1:3000/
|
||||
ProxyPassReverse / http://127.0.0.1:3000/
|
||||
|
||||
</VirtualHost>
|
||||
```
|
||||
|
|
@ -94,97 +143,35 @@ Second, create a Docker network for Dawarich to use as the backend network:
|
|||
docker network create dawarich
|
||||
```
|
||||
|
||||
Adjust your Dawarich docker-compose.yaml so that the web app is exposed to your new network and the backend Dawarich network:
|
||||
```
|
||||
version: '3'
|
||||
Adjust the following part of your Dawarich docker-compose.yaml, so that the web app is exposed to your new network and the backend Dawarich network:
|
||||
```yaml
|
||||
networks:
|
||||
dawarich:
|
||||
frontend:
|
||||
external: true
|
||||
services:
|
||||
dawarich_redis:
|
||||
image: redis:7.0-alpine
|
||||
command: redis-server
|
||||
networks:
|
||||
- dawarich
|
||||
volumes:
|
||||
- ./dawarich/redis:/var/shared/redis
|
||||
dawarich_db:
|
||||
image: postgres:14.2-alpine
|
||||
container_name: dawarich_db
|
||||
volumes:
|
||||
- ./dawarich/db:/var/lib/postgresql/data
|
||||
- ./dawarich/shared:/var/shared
|
||||
networks:
|
||||
- dawarich
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: password
|
||||
dawarich_app:
|
||||
image: freikin/dawarich:latest
|
||||
container_name: dawarich_app
|
||||
volumes:
|
||||
- ./dawarich/gems:/usr/local/bundle/gems
|
||||
- ./dawarich/public:/var/app/public
|
||||
networks:
|
||||
- dawarich
|
||||
- frontend
|
||||
stdin_open: true
|
||||
tty: true
|
||||
entrypoint: dev-entrypoint.sh
|
||||
command: ['bin/dev']
|
||||
restart: on-failure
|
||||
environment:
|
||||
RAILS_ENV: development
|
||||
REDIS_URL: redis://dawarich_redis:6379/0
|
||||
DATABASE_HOST: dawarich_db
|
||||
DATABASE_USERNAME: postgres
|
||||
DATABASE_PASSWORD: password
|
||||
DATABASE_NAME: dawarich_development
|
||||
MIN_MINUTES_SPENT_IN_CITY: 60
|
||||
APPLICATION_HOSTS: <YOUR FQDN HERE (ex. dawarich.example.com)>
|
||||
TIME_ZONE: America/New_York
|
||||
depends_on:
|
||||
- dawarich_db
|
||||
- dawarich_redis
|
||||
dawarich_sidekiq:
|
||||
image: freikin/dawarich:latest
|
||||
container_name: dawarich_sidekiq
|
||||
volumes:
|
||||
- ./dawarich/gems:/usr/local/bundle/gems
|
||||
- ./dawarich/public:/var/app/public
|
||||
networks:
|
||||
- dawarich
|
||||
stdin_open: true
|
||||
tty: true
|
||||
entrypoint: dev-entrypoint.sh
|
||||
command: ['sidekiq']
|
||||
restart: on-failure
|
||||
environment:
|
||||
RAILS_ENV: development
|
||||
REDIS_URL: redis://dawarich_redis:6379/0
|
||||
DATABASE_HOST: dawarich_db
|
||||
DATABASE_USERNAME: postgres
|
||||
DATABASE_PASSWORD: password
|
||||
DATABASE_NAME: dawarich_development
|
||||
APPLICATION_HOSTS: <YOUR FQDN HERE (ex. dawarich.example.com)>
|
||||
depends_on:
|
||||
- dawarich_db
|
||||
- dawarich_redis
|
||||
- dawarich_app
|
||||
...
|
||||
```
|
||||
|
||||
Lastly, edit your Caddy config as needed:
|
||||
```
|
||||
```caddy
|
||||
{
|
||||
http_port 80
|
||||
https_port 443
|
||||
}
|
||||
|
||||
<YOUR FQDN HERE (ex. dawarich.example.com)> {
|
||||
timeline.example.com {
|
||||
reverse_proxy dawarich_app:3000
|
||||
|
||||
encode brotli {
|
||||
match {
|
||||
content_type text/css text/plain text/xml text/x-component text/javascript application/x-javascript application/javascript application/json application/manifest+json application/vnd.api+json application/xml application/xhtml+xml application/rss+xml application/atom+xml application/vnd.ms-fontobject application/x-font-ttf application/x-font-opentype application/x-font-truetype image/svg+xml image/x-icon image/vnd.microsoft.icon font/ttf font/eot font/otf font/opentype
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
timeline.example.com is an example, use your own (sub) domain.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,11 @@ FactoryBot.define do
|
|||
country { nil }
|
||||
user
|
||||
|
||||
trait :with_known_location do
|
||||
latitude { 55.755826 }
|
||||
longitude { 37.6173 }
|
||||
end
|
||||
|
||||
trait :with_geodata do
|
||||
geodata do
|
||||
{
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -56,5 +56,12 @@ RSpec.describe '/points', type: :request do
|
|||
|
||||
expect(response).to redirect_to(points_url)
|
||||
end
|
||||
|
||||
it 'preserves the start_at and end_at parameters' do
|
||||
delete bulk_destroy_points_url,
|
||||
params: { point_ids: [point1.id, point2.id], start_at: '2021-01-01', end_at: '2021-01-02' }
|
||||
|
||||
expect(response).to redirect_to(points_url(start_at: '2021-01-01', end_at: '2021-01-02'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ RSpec.describe Points::GpxSerializer do
|
|||
point = points[index]
|
||||
expect(waypoint.lat).to eq(point.latitude)
|
||||
expect(waypoint.lon).to eq(point.longitude)
|
||||
expect(waypoint.time).to eq(point.recorded_at.strftime('%FT%R:%SZ'))
|
||||
expect(waypoint.time).to eq(point.recorded_at)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,14 +13,16 @@ RSpec.describe Exports::Create do
|
|||
let(:export_name) { "#{start_at.to_date}_#{end_at.to_date}" }
|
||||
let(:export) { create(:export, user:, name: export_name, status: :created) }
|
||||
let(:export_content) { Points::GeojsonSerializer.new(points).call }
|
||||
let!(:points) { create_list(:point, 10, user:, timestamp: start_at.to_datetime.to_i) }
|
||||
let!(:points) do
|
||||
create_list(:point, 10, :with_known_location, user:, timestamp: start_at.to_datetime.to_i)
|
||||
end
|
||||
|
||||
it 'writes the data to a file' do
|
||||
create_export
|
||||
|
||||
file_path = Rails.root.join('spec/fixtures/files/geojson/export_same_points.json')
|
||||
|
||||
expect(File.read(file_path)).to eq(export_content)
|
||||
expect(File.read(file_path).strip).to eq(export_content)
|
||||
end
|
||||
|
||||
it 'updates the export url' do
|
||||
|
|
|
|||
|
|
@ -4,5 +4,34 @@ require 'rails_helper'
|
|||
|
||||
RSpec.describe GoogleMaps::RecordsParser do
|
||||
describe '#call' do
|
||||
subject(:parser) { described_class.new(import).call(json) }
|
||||
|
||||
let(:import) { create(:import) }
|
||||
let(:json) do
|
||||
{
|
||||
'latitudeE7' => 123_456_789,
|
||||
'longitudeE7' => 123_456_789,
|
||||
'timestamp' => Time.zone.now.to_s,
|
||||
'altitude' => 0,
|
||||
'velocity' => 0
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates a point' do
|
||||
expect { parser }.to change(Point, :count).by(1)
|
||||
end
|
||||
|
||||
context 'when point already exists' do
|
||||
before do
|
||||
create(
|
||||
:point, user: import.user, import:, latitude: 12.3456789, longitude: 12.3456789,
|
||||
timestamp: Time.zone.now.to_i
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not create a point' do
|
||||
expect { parser }.not_to change(Point, :count)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,5 +4,109 @@ require 'rails_helper'
|
|||
|
||||
RSpec.describe GoogleMaps::SemanticHistoryParser do
|
||||
describe '#call' do
|
||||
subject(:parser) { described_class.new(import, user.id).call }
|
||||
|
||||
let(:user) { create(:user) }
|
||||
|
||||
context 'when activitySegment is present' do
|
||||
context 'when startLocation is blank' do
|
||||
let(:import) { create(:import, raw_data: { 'timelineObjects' => [activity_segment] }) }
|
||||
let(:activity_segment) do
|
||||
{
|
||||
'activitySegment' => {
|
||||
'waypointPath' => {
|
||||
'waypoints' => [
|
||||
{ 'latE7' => 123_456_789, 'lngE7' => 123_456_789 }
|
||||
]
|
||||
},
|
||||
'duration' => { 'startTimestamp' => Time.zone.now.to_s }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates a point' do
|
||||
expect { parser }.to change(Point, :count).by(1)
|
||||
end
|
||||
|
||||
context 'when waypointPath is blank' do
|
||||
let(:activity_segment) do
|
||||
{
|
||||
'activitySegment' => {
|
||||
'duration' => { 'startTimestamp' => Time.zone.now.to_s }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'does not create a point' do
|
||||
expect { parser }.not_to change(Point, :count)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when startLocation is present' do
|
||||
let(:import) { create(:import, raw_data: { 'timelineObjects' => [activity_segment] }) }
|
||||
let(:activity_segment) do
|
||||
{
|
||||
'activitySegment' => {
|
||||
'startLocation' => { 'latitudeE7' => 123_456_789, 'longitudeE7' => 123_456_789 },
|
||||
'duration' => { 'startTimestamp' => Time.zone.now.to_s }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates a point' do
|
||||
expect { parser }.to change(Point, :count).by(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when placeVisit is present' do
|
||||
context 'when location with coordinates is present' do
|
||||
let(:import) { create(:import, raw_data: { 'timelineObjects' => [place_visit] }) }
|
||||
let(:place_visit) do
|
||||
{
|
||||
'placeVisit' => {
|
||||
'location' => { 'latitudeE7' => 123_456_789, 'longitudeE7' => 123_456_789 },
|
||||
'duration' => { 'startTimestamp' => Time.zone.now.to_s }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates a point' do
|
||||
expect { parser }.to change(Point, :count).by(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when location with coordinates is blank' do
|
||||
let(:import) { create(:import, raw_data: { 'timelineObjects' => [place_visit] }) }
|
||||
let(:place_visit) do
|
||||
{
|
||||
'placeVisit' => {
|
||||
'location' => {},
|
||||
'duration' => { 'startTimestamp' => Time.zone.now.to_s }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'does not create a point' do
|
||||
expect { parser }.not_to change(Point, :count)
|
||||
end
|
||||
|
||||
context 'when otherCandidateLocations is present' do
|
||||
let(:place_visit) do
|
||||
{
|
||||
'placeVisit' => {
|
||||
'otherCandidateLocations' => [{ 'latitudeE7' => 123_456_789, 'longitudeE7' => 123_456_789 }],
|
||||
'duration' => { 'startTimestamp' => Time.zone.now.to_s }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates a point' do
|
||||
expect { parser }.to change(Point, :count).by(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,21 +10,21 @@ RSpec.describe Visits::Suggest do
|
|||
|
||||
let!(:points) do
|
||||
[
|
||||
create(:point, user:, timestamp: start_at),
|
||||
create(:point, user:, timestamp: start_at + 5.minutes),
|
||||
create(:point, user:, timestamp: start_at + 10.minutes),
|
||||
create(:point, user:, timestamp: start_at + 15.minutes),
|
||||
create(:point, user:, timestamp: start_at + 20.minutes),
|
||||
create(:point, user:, timestamp: start_at + 25.minutes),
|
||||
create(:point, user:, timestamp: start_at + 30.minutes),
|
||||
create(:point, user:, timestamp: start_at + 35.minutes),
|
||||
create(:point, user:, timestamp: start_at + 40.minutes),
|
||||
create(:point, user:, timestamp: start_at + 45.minutes),
|
||||
create(:point, user:, timestamp: start_at + 50.minutes),
|
||||
create(:point, user:, timestamp: start_at + 55.minutes),
|
||||
create(:point, user:, timestamp: start_at + 95.minutes),
|
||||
create(:point, user:, timestamp: start_at + 100.minutes),
|
||||
create(:point, user:, timestamp: start_at + 105.minutes)
|
||||
create(:point, :with_known_location, user:, timestamp: start_at),
|
||||
create(:point, :with_known_location, user:, timestamp: start_at + 5.minutes),
|
||||
create(:point, :with_known_location, user:, timestamp: start_at + 10.minutes),
|
||||
create(:point, :with_known_location, user:, timestamp: start_at + 15.minutes),
|
||||
create(:point, :with_known_location, user:, timestamp: start_at + 20.minutes),
|
||||
create(:point, :with_known_location, user:, timestamp: start_at + 25.minutes),
|
||||
create(:point, :with_known_location, user:, timestamp: start_at + 30.minutes),
|
||||
create(:point, :with_known_location, user:, timestamp: start_at + 35.minutes),
|
||||
create(:point, :with_known_location, user:, timestamp: start_at + 40.minutes),
|
||||
create(:point, :with_known_location, user:, timestamp: start_at + 45.minutes),
|
||||
create(:point, :with_known_location, user:, timestamp: start_at + 50.minutes),
|
||||
create(:point, :with_known_location, user:, timestamp: start_at + 55.minutes),
|
||||
create(:point, :with_known_location, user:, timestamp: start_at + 95.minutes),
|
||||
create(:point, :with_known_location, user:, timestamp: start_at + 100.minutes),
|
||||
create(:point, :with_known_location, user:, timestamp: start_at + 105.minutes)
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue