diff options
| -rw-r--r-- | asset-sources/theme.scss | 10 | ||||
| -rwxr-xr-x | ci/run_tests.sh | 2 | ||||
| -rw-r--r-- | doc/developer/module/hittekaart_py.rst | 25 | ||||
| -rw-r--r-- | fietsboek/models/__init__.py | 6 | ||||
| -rw-r--r-- | fietsboek/static/theme.css | 8 | ||||
| -rw-r--r-- | fietsboek/static/theme.css.map | 2 | ||||
| -rw-r--r-- | fietsboek/templates/details.jinja2 | 2 | ||||
| -rw-r--r-- | tests/integration/test_upload.py | 31 |
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')) |
