aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--asset-sources/theme.scss10
-rwxr-xr-xci/run_tests.sh2
-rw-r--r--doc/developer/module/hittekaart_py.rst25
-rw-r--r--fietsboek/models/__init__.py6
-rw-r--r--fietsboek/static/theme.css8
-rw-r--r--fietsboek/static/theme.css.map2
-rw-r--r--fietsboek/templates/details.jinja22
-rw-r--r--tests/integration/test_upload.py31
8 files changed, 79 insertions, 7 deletions
diff --git a/asset-sources/theme.scss b/asset-sources/theme.scss
index 1941f36..e8ed1a5 100644
--- a/asset-sources/theme.scss
+++ b/asset-sources/theme.scss
@@ -229,6 +229,16 @@ strong {
background-color: #A44A3F;
color: #fff;
}
+
+.climb-details {
+ margin-bottom: 0.2rem;
+}
+
+.climb-details tr:last-child {
+ td, th {
+ border-bottom-width: 0;
+ }
+}
/* </Climbs> */
.count-field {
diff --git a/ci/run_tests.sh b/ci/run_tests.sh
index 200f794..6fad536 100755
--- a/ci/run_tests.sh
+++ b/ci/run_tests.sh
@@ -33,7 +33,7 @@ case "$DB" in
"postgres")
setup_postgres
- sed -i 's|^sqlalchemy.url = .*$|sqlalchemy.url = postgresql://postgres:postgres@localhost/postgres|' testing.ini
+ sed -i 's|^sqlalchemy.url = .*$|sqlalchemy.url = postgresql+psycopg2://postgres:postgres@localhost/postgres|' testing.ini
;;
*)
diff --git a/doc/developer/module/hittekaart_py.rst b/doc/developer/module/hittekaart_py.rst
index 052c2b0..f665799 100644
--- a/doc/developer/module/hittekaart_py.rst
+++ b/doc/developer/module/hittekaart_py.rst
@@ -24,7 +24,7 @@ Example
Track.from_file(b"Documents/track.gpx", None),
Track.from_coordinates([(45.0, 47.0)]),
]
- storage = Storage.Sqlite(b"/tmp/tiles.sqlite")
+ storage = Storage.OsmAnd(b"/tmp/tiles.sqlite")
generate(settings, tracks, HeatmapRenderer(), storage)
Input and output
@@ -74,15 +74,34 @@ Input and output
:return: The created storage.
:rtype: Storage
- .. staticmethod:: Sqlite(path)
+ .. staticmethod:: OsmAnd(path)
- Output the tiles to the given SQLite database.
+ Output the tiles to the given SQLite database. The table format is
+ compatible with OsmAnd. See `the Rust crate`__ for more information.
:param path: Path to the database.
:type path: bytes
:return: The created storage.
:rtype: Storage
+ .. staticmethod:: MbTiles(path, name, description)
+
+ Output the tiles to the given MbTiles database. Optionally, sets the
+ ``name`` and ``description`` metadata as given. See `the Rust crate`__
+ for more information about the format.
+
+ :param path: Path to the database.
+ :type path: bytes
+ :param name: Name of the resulting map layer.
+ :type name: str | None
+ :param description: Description of the map layer.
+ :type description: str | None
+ :return: The created storage.
+ :rtype: Storage
+
+.. __: https://docs.rs/hittekaart/latest/hittekaart/storage/struct.OsmAnd.html
+.. __: https://docs.rs/hittekaart/latest/hittekaart/storage/struct.MbTiles.html
+
Renderers
---------
diff --git a/fietsboek/models/__init__.py b/fietsboek/models/__init__.py
index 4d4fcaf..b6cd93f 100644
--- a/fietsboek/models/__init__.py
+++ b/fietsboek/models/__init__.py
@@ -26,7 +26,11 @@ configure_mappers()
def get_engine(settings, prefix="sqlalchemy."):
"""Create an SQL Engine from the given settings."""
- engine = engine_from_config(settings, prefix)
+ kwargs = {}
+ if settings[f"{prefix}url"].startswith("postgres"):
+ kwargs["client_encoding"] = "utf8"
+
+ engine = engine_from_config(settings, prefix, **kwargs)
# SQLite is quite loose with foreign keys by default, so make sure it
# checks them.
diff --git a/fietsboek/static/theme.css b/fietsboek/static/theme.css
index 0925d4e..b295203 100644
--- a/fietsboek/static/theme.css
+++ b/fietsboek/static/theme.css
@@ -223,6 +223,14 @@ strong {
color: #fff;
}
+.climb-details {
+ margin-bottom: 0.2rem;
+}
+
+.climb-details tr:last-child td, .climb-details tr:last-child th {
+ border-bottom-width: 0;
+}
+
/* </Climbs> */
.count-field {
width: 50px;
diff --git a/fietsboek/static/theme.css.map b/fietsboek/static/theme.css.map
index c7a2016..b91d750 100644
--- a/fietsboek/static/theme.css.map
+++ b/fietsboek/static/theme.css.map
@@ -1 +1 @@
-{"version":3,"sourceRoot":"","sources":["../../asset-sources/theme.scss"],"names":[],"mappings":"AAAA;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;AAEA;EACE;EACA;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAIA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAlCJ;EAqCE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACI;;;AAGJ;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;AACA;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAEF;AAEA;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAIJ;EACE;;AAEF;EACE;;AACA;EACE;;AAGJ;EACE;;AACA;EACE;;AAGJ;EACE;;AACA;EACE;;AAGJ;EACE;;AACA;EACE;;AAGJ;EACE;EACA;;AACA;EACE;;AAIJ;EACE;EACA;;;AAKF;EACE;EACA;EAEA;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAGF;EACE;;;AAGF;AACA;EACE;;;AAGF;EACE;EACA;;AAEA;EAJF;IAKI,eACE;;;AAIJ;EACE;;AAGF;EACE;;AAEA;EAHF;IAII;;;AAGF;EACE;EACA;;AAIJ;EACE;EACA;;;AAIJ;EACE;;;AAGF;AACA;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE","file":"theme.css"} \ No newline at end of file
+{"version":3,"sourceRoot":"","sources":["../../asset-sources/theme.scss"],"names":[],"mappings":"AAAA;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;AAEA;EACE;EACA;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAIA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAlCJ;EAqCE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACI;;;AAGJ;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;AACA;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAIA;EACE;;;AAGJ;AAEA;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAIJ;EACE;;AAEF;EACE;;AACA;EACE;;AAGJ;EACE;;AACA;EACE;;AAGJ;EACE;;AACA;EACE;;AAGJ;EACE;;AACA;EACE;;AAGJ;EACE;EACA;;AACA;EACE;;AAIJ;EACE;EACA;;;AAKF;EACE;EACA;EAEA;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAGF;EACE;;;AAGF;AACA;EACE;;;AAGF;EACE;EACA;;AAEA;EAJF;IAKI,eACE;;;AAIJ;EACE;;AAGF;EACE;;AAEA;EAHF;IAII;;;AAGF;EACE;EACA;;AAIJ;EACE;EACA;;;AAIJ;EACE;;;AAGF;AACA;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE","file":"theme.css"} \ No newline at end of file
diff --git a/fietsboek/templates/details.jinja2 b/fietsboek/templates/details.jinja2
index 748fc6f..210e301 100644
--- a/fietsboek/templates/details.jinja2
+++ b/fietsboek/templates/details.jinja2
@@ -215,7 +215,7 @@
<img class="mb-2 climb-preview" data-bounds="{{ climb.bounds() | tojson }}" src="{{ request.route_url('climb-preview', climb_id=climb.id) }}">
<img src="{{ request.route_url('climb-profile', climb_id=climb.id) }}">
- <table>
+ <table class="table climb-details">
<tr>
<th scope="row">{{ _("page.details.climb.length") }}</th>
<td>{{ (climb.length / 1000) | round(2) | format_decimal }} km</td>
diff --git a/tests/integration/test_upload.py b/tests/integration/test_upload.py
index eb4e787..1b7516a 100644
--- a/tests/integration/test_upload.py
+++ b/tests/integration/test_upload.py
@@ -47,6 +47,37 @@ def test_upload(testapp, dbsession, route_path, logged_in, tm, monkeypatch):
assert track.description == "Super descriptive text!"
+def test_upload_unicode(testapp, dbsession, route_path, logged_in):
+ # Step 1: Go to the upload form
+ upload_site = testapp.get(route_path('upload'))
+
+ # Step 2: Fill it out
+ upload_form = upload_site.form
+ upload_form['gpx'] = Upload('super_track.gpx', load_gpx_asset('Teasi_1.gpx.gz'))
+ result = upload_form.submit().maybe_follow()
+
+ # Step 3: We are now on the "Finish upload" page
+ assert "<h1>Upload</h1>" in result.text
+
+ finish_form = result.forms[0]
+ finish_form["title"].value = title = "Föö Bär's Teßtträck 😭"
+ finish_form["description"].value = desc = "Велосипед"
+ # The tagged-friend[] has a disabled attribute, which webtest does not
+ # respect. Therefore, we need to manually remove it:
+ finish_form.field_order = [
+ (name, field) for (name, field) in finish_form.field_order if name != "tagged-friend[]"
+ ]
+
+ result = finish_form.submit().maybe_follow()
+ assert re.search("<h1>\\s*Föö Bär", result.text)
+
+ # Step 4: Ensure the track is stored right
+ track = dbsession.execute(select(models.Track)).scalar_one()
+ assert track.title == title
+ assert track.description == desc
+
+
+
def test_upload_cancel(testapp, dbsession, route_path, logged_in):
# Step 1: Go to the upload form
upload_site = testapp.get(route_path('upload'))