diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index dc9907d26..383c31022 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -42,7 +42,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -53,7 +53,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -67,4 +67,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 9518dd399..4fe59d640 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -20,6 +20,7 @@ jobs: ECO_COUNTER_STATIONS_URL: https://dev.turku.fi/datasets/ecocounter/liikennelaskimet.geojson ECO_COUNTER_OBSERVATIONS_URL: https://data.turku.fi/cjtv3brqr7gectdv7rfttc/counters-15min.csv TRAFFIC_COUNTER_OBSERVATIONS_BASE_URL: https://data.turku.fi/2yxpk2imqi2mzxpa6e6knq/ + CACHE_LOCATION: redis://localhost:6379/0 steps: - uses: actions/checkout@v3 @@ -29,9 +30,8 @@ jobs: python-version: 3.10.0 - name: Install required Ubuntu packages run: | - sudo apt-get install gdal-bin - sudo apt-get install voikko-fi - sudo apt-get install libvoikko-dev + sudo apt-get update && sudo apt-get -y --no-install-recommends install gdal-bin voikko-fi libvoikko-dev + sudo apt-get install redis-server - name: Create needed postgis extensions run: | psql -h localhost -U postgres template1 -c 'create extension hstore;' diff --git a/eco_counter/api/serializers.py b/eco_counter/api/serializers.py index 685c594dd..b04306415 100644 --- a/eco_counter/api/serializers.py +++ b/eco_counter/api/serializers.py @@ -1,5 +1,3 @@ -from datetime import date, timedelta - from django.db.models import Q from rest_framework import serializers @@ -38,10 +36,6 @@ class StationSerializer(serializers.ModelSerializer): y = serializers.SerializerMethodField() lon = serializers.SerializerMethodField() lat = serializers.SerializerMethodField() - sensor_types = serializers.SerializerMethodField() - data_until_date = serializers.SerializerMethodField() - data_from_date = serializers.SerializerMethodField() - is_active = serializers.SerializerMethodField() class Meta: model = Station @@ -78,48 +72,6 @@ def get_lon(self, obj): obj.location.transform(4326) return obj.location.x - def get_sensor_types(self, obj): - # Return the sensor types(car, bike etc) that has a total year value >0. - # i.e., there are sensors for counting the type of data. - types = ["at", "pt", "jt", "bt"] - result = [] - for type in types: - filter = {"station": obj, f"value_{type}__gt": 0} - if YearData.objects.filter(**filter).count() > 0: - result.append(type) - return result - - def get_is_active(self, obj): - num_days = [1, 7, 30, 365] - res = {} - for days in num_days: - from_date = date.today() - timedelta(days=days - 1) - day_qs = Day.objects.filter(station=obj, date__gte=from_date) - day_data_qs = DayData.objects.filter(day__in=day_qs) - if day_data_qs.filter(Q_EXP).count() > 0: - res[days] = True - else: - res[days] = False - return res - - def get_data_until_date(self, obj): - try: - return ( - DayData.objects.filter(Q_EXP, station=obj).latest("day__date").day.date - ) - except DayData.DoesNotExist: - return None - - def get_data_from_date(self, obj): - try: - return ( - DayData.objects.filter(Q_EXP, station=obj) - .earliest("day__date") - .day.date - ) - except DayData.DoesNotExist: - return None - class YearSerializer(serializers.ModelSerializer): station_name = serializers.PrimaryKeyRelatedField( diff --git a/eco_counter/api/views.py b/eco_counter/api/views.py index 695e6f0ec..f2ca1f997 100644 --- a/eco_counter/api/views.py +++ b/eco_counter/api/views.py @@ -1,5 +1,7 @@ import sys +from django.utils.decorators import method_decorator +from django.views.decorators.cache import cache_page from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.exceptions import ParseError @@ -54,6 +56,7 @@ class StationViewSet(viewsets.ReadOnlyModelViewSet): queryset = Station.objects.all() serializer_class = StationSerializer + @method_decorator(cache_page(60 * 60)) def list(self, request): queryset = Station.objects.all() filters = self.request.query_params diff --git a/eco_counter/management/commands/import_counter_data.py b/eco_counter/management/commands/import_counter_data.py index 01fbcc203..b1ad9207c 100644 --- a/eco_counter/management/commands/import_counter_data.py +++ b/eco_counter/management/commands/import_counter_data.py @@ -44,9 +44,13 @@ from .utils import ( check_counters_argument, gen_eco_counter_test_csv, + get_data_from_date, + get_data_until_date, get_eco_counter_csv, + get_is_active, get_lam_counter_csv, get_or_create_telraam_station, + get_sensor_types, get_telraam_data_frames, get_test_dataframe, get_traffic_counter_csv, @@ -360,6 +364,7 @@ def save_observations(csv_data, start_time, csv_data_source=ECO_COUNTER, station import_state.current_month_number = end_date.month import_state.current_day_number = end_date.day import_state.save() + add_additional_data_to_stations(csv_data_source) logger.info(f"Imported observations until:{str(end_date)}") @@ -458,6 +463,17 @@ def import_data(counters): gc.collect() +def add_additional_data_to_stations(csv_data_source): + + logger.info(f"Updating {csv_data_source} stations informations...") + for station in Station.objects.filter(csv_data_source=csv_data_source): + station.data_from_date = get_data_from_date(station) + station.data_until_date = get_data_until_date(station) + station.sensor_types = get_sensor_types(station) + station.is_active = get_is_active(station) + station.save() + + class Command(BaseCommand): help = "Imports traffic counter data in the Turku region." diff --git a/eco_counter/management/commands/utils.py b/eco_counter/management/commands/utils.py index 90363b66b..5a9169129 100644 --- a/eco_counter/management/commands/utils.py +++ b/eco_counter/management/commands/utils.py @@ -9,6 +9,7 @@ from django.contrib.gis.gdal import DataSource from django.contrib.gis.geos import GEOSGeometry, LineString, MultiLineString, Point from django.core.management.base import CommandError +from django.db.models import Q from eco_counter.constants import ( COUNTER_CHOICES_STR, @@ -33,11 +34,12 @@ TRAFFIC_COUNTER_CSV_URLS, TRAFFIC_COUNTER_METADATA_GEOJSON, ) -from eco_counter.models import Station +from eco_counter.models import Day, DayData, Station, YearData from eco_counter.tests.constants import TEST_COLUMN_NAMES from mobility_data.importers.utils import get_root_dir logger = logging.getLogger("eco_counter") +Q_EXP = Q(value_at__gt=0) | Q(value_pt__gt=0) | Q(value_jt__gt=0) | Q(value_bt__gt=0) class LAMStation: @@ -112,6 +114,53 @@ def __init__(self, mac, location, geometry): self.geometry = geometry +def get_is_active(station): + # Returns if the station has been active during time period defined in num_days + num_days = [1, 7, 30, 365] + res = {} + for days in num_days: + from_date = date.today() - timedelta(days=days - 1) + day_qs = Day.objects.filter(station=station, date__gte=from_date) + day_data_qs = DayData.objects.filter(day__in=day_qs) + if day_data_qs.filter(Q_EXP).count() > 0: + res[days] = True + else: + res[days] = False + return res + + +def get_sensor_types(station): + # Return the sensor types(car, bike etc) that has a total year value >0. + # i.e., there are sensors for counting the type of data. + types = ["at", "pt", "jt", "bt"] + result = [] + for type in types: + filter = {"station": station, f"value_{type}__gt": 0} + if YearData.objects.filter(**filter).count() > 0: + result.append(type) + return result + + +def get_data_until_date(station): + try: + return ( + DayData.objects.filter(Q_EXP, station=station).latest("day__date").day.date + ) + except DayData.DoesNotExist: + return None + + +def get_data_from_date(station): + try: + return ( + DayData.objects.filter(Q_EXP, station=station) + .earliest("day__date") + .day.date + ) + except DayData.DoesNotExist: + return None + + def check_counters_argument(counters): for counter in counters: if counter not in COUNTERS_LIST: diff --git a/eco_counter/migrations/0019_station_data_from_date_station_data_until_date.py b/eco_counter/migrations/0019_station_data_from_date_station_data_until_date.py new file mode 100644 index 000000000..67a9400a0 --- /dev/null +++ b/eco_counter/migrations/0019_station_data_from_date_station_data_until_date.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.10 on 2024-01-25 11:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("eco_counter", "0018_add_geometry_to_station"), + ] + + operations = [ + migrations.AddField( + model_name="station", + name="data_from_date", + field=models.DateField(blank=True, null=True), + ), + migrations.AddField( + model_name="station", + name="data_until_date", + field=models.DateField(blank=True, null=True), + ), + ] diff --git a/eco_counter/migrations/0020_station_sensor_types.py b/eco_counter/migrations/0020_station_sensor_types.py new file mode 100644 index 000000000..6ec16b47a --- /dev/null +++ b/eco_counter/migrations/0020_station_sensor_types.py @@ -0,0 +1,21 @@ +# Generated by Django 4.1.10 on 2024-01-25 11:58 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("eco_counter", "0019_station_data_from_date_station_data_until_date"), + ] + + operations = [ + migrations.AddField( + model_name="station", + name="sensor_types", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=2), default=list, size=None + ), + ), + ] diff --git a/eco_counter/migrations/0021_station_is_active.py b/eco_counter/migrations/0021_station_is_active.py new file mode 100644 index 000000000..d4a44c880 --- /dev/null +++ b/eco_counter/migrations/0021_station_is_active.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.10 on 2024-01-25 12:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("eco_counter", "0020_station_sensor_types"), + ] + + operations = [ + migrations.AddField( + model_name="station", + name="is_active", + field=models.JSONField(blank=True, null=True), + ), + ] diff --git a/eco_counter/models.py b/eco_counter/models.py index a58565184..c7d0a9ed1 100644 --- a/eco_counter/models.py +++ b/eco_counter/models.py @@ -35,6 +35,10 @@ class Station(models.Model): # Optioal id of the station, used when fetching LAM # and TELRAAM station data station_id = models.CharField(max_length=16, null=True) + data_until_date = models.DateField(null=True, blank=True) + data_from_date = models.DateField(null=True, blank=True) + sensor_types = ArrayField(models.CharField(max_length=2), default=list) + is_active = models.JSONField(null=True, blank=True) def __str__(self): return "%s %s" % (self.name, self.location) diff --git a/eco_counter/tests/conftest.py b/eco_counter/tests/conftest.py index ed3fb16ef..71202cece 100644 --- a/eco_counter/tests/conftest.py +++ b/eco_counter/tests/conftest.py @@ -43,6 +43,9 @@ def stations(): name=TEST_EC_STATION_NAME, location="POINT(60.4487578455581 22.269454227550053)", csv_data_source=ECO_COUNTER, + sensor_types=["at"], + data_until_date="2020-01-07", + data_from_date="2020-01-01", ) ) stations.append( @@ -201,24 +204,28 @@ def is_active_fixtures(): name="Station with 0 day of data", location="POINT(0 0)", csv_data_source=LAM_COUNTER, + is_active={"1": False, "7": False, "30": False, "365": False}, ) station1 = Station.objects.create( id=1, name="Station with 1 day of data", location="POINT(0 0)", csv_data_source=LAM_COUNTER, + is_active={"1": True, "7": True, "30": True, "365": True}, ) station7 = Station.objects.create( id=7, name="Station with 7 days of data", location="POINT(0 0)", csv_data_source=LAM_COUNTER, + is_active={"1": False, "7": True, "30": True, "365": True}, ) station30 = Station.objects.create( id=30, name="Station with 30 days of data", location="POINT(0 0)", csv_data_source=LAM_COUNTER, + is_active={"1": False, "7": False, "30": True, "365": True}, ) start_date = date.today() current_date = start_date diff --git a/eco_counter/tests/test_import_counter_data.py b/eco_counter/tests/test_import_counter_data.py index b3c69d02f..bad84144e 100644 --- a/eco_counter/tests/test_import_counter_data.py +++ b/eco_counter/tests/test_import_counter_data.py @@ -7,7 +7,7 @@ imports and calculates the data correctly. """ import calendar -from datetime import timedelta +from datetime import datetime, timedelta from io import StringIO from unittest.mock import patch @@ -402,7 +402,16 @@ def test_import_traffic_counter_data(stations): start_time = dateutil.parser.parse("2020-01-01T00:00") end_time = dateutil.parser.parse("2020-02-29T23:45") import_command(test_counter=(TRAFFIC_COUNTER, start_time, end_time)) - num_tc_stations = Station.objects.filter(csv_data_source=TRAFFIC_COUNTER).count() + stations_qs = Station.objects.filter(csv_data_source=TRAFFIC_COUNTER) + num_tc_stations = stations_qs.count() + station1 = stations_qs.first() + assert station1.sensor_types == ["at", "pt", "jt", "bt"] + assert station1.data_from_date == datetime.strptime("2020-01-01", "%Y-%m-%d").date() + assert ( + station1.data_until_date == datetime.strptime("2020-02-29", "%Y-%m-%d").date() + ) + assert station1.is_active == {"1": False, "7": False, "30": False, "365": False} + state = ImportState.objects.get(csv_data_source=TRAFFIC_COUNTER) assert state.current_year_number == 2020 assert state.current_month_number == 2 diff --git a/environment_data/api/serializers.py b/environment_data/api/serializers.py index 6efde594c..7130d74c5 100644 --- a/environment_data/api/serializers.py +++ b/environment_data/api/serializers.py @@ -34,14 +34,14 @@ class Meta: def get_parameters_in_use(self, obj): res = {} - for param in obj.parameters.all(): - qs = YearData.objects.filter( - station=obj, measurements__parameter=param, measurements__value__gte=0 - ) - if qs.count(): - res[param.name] = True + available_parameters_qs = Parameter.objects.filter(data_type=obj.data_type) + + for available_parameter in available_parameters_qs: + name = available_parameter.name + if obj.parameters.filter(name=name).exists(): + res[name] = True else: - res[param.name] = False + res[name] = False return res def get_data_type_verbose(self, obj): diff --git a/environment_data/api/views.py b/environment_data/api/views.py index c567bfb73..c52a89ce6 100644 --- a/environment_data/api/views.py +++ b/environment_data/api/views.py @@ -1,3 +1,5 @@ +from django.utils.decorators import method_decorator +from django.views.decorators.cache import cache_page from drf_spectacular.utils import extend_schema, extend_schema_view from rest_framework import status, viewsets from rest_framework.response import Response @@ -41,6 +43,7 @@ class StationViewSet(viewsets.ReadOnlyModelViewSet): queryset = Station.objects.all() serializer_class = StationSerializer + @method_decorator(cache_page(60 * 60)) def list(self, request, *args, **kwargs): queryset = self.queryset filters = self.request.query_params diff --git a/environment_data/management/commands/air_quality_utils.py b/environment_data/management/commands/air_quality_utils.py index b7ea3fc96..64bfeb02a 100644 --- a/environment_data/management/commands/air_quality_utils.py +++ b/environment_data/management/commands/air_quality_utils.py @@ -33,7 +33,6 @@ def get_dataframe(stations, from_year=START_YEAR, from_month=1, initial_import=F else: params["startTime"] = f"{start_date_time.year}-01-01T00:00Z" if start_date_time.year == current_date_time.year: - params["endTime"] = current_date_time.strftime(TIME_FORMAT) else: params["endTime"] = f"{start_date_time.year}-12-31T23:59Z" diff --git a/environment_data/management/commands/import_environment_data.py b/environment_data/management/commands/import_environment_data.py index aa35b4f19..4588bd8f7 100644 --- a/environment_data/management/commands/import_environment_data.py +++ b/environment_data/management/commands/import_environment_data.py @@ -168,16 +168,17 @@ def save_weeks(df, stations): if week.years.count() == 0: week.years.add(year) values = get_measurements(data_frame, station.name) - week_data, _ = WeekData.objects.get_or_create(station=station, week=week) + week_data, created = WeekData.objects.get_or_create( + station=station, week=week + ) + if not created: + week_data.measurements.all().delete() for item in values.items(): parameter = get_parameter(item[0]) - if not week_data.measurements.filter( + measurement = Measurement.objects.create( value=item[1], parameter=parameter - ): - measurement = Measurement.objects.create( - value=item[1], parameter=parameter - ) - week_data.measurements.add(measurement) + ) + week_data.measurements.add(measurement) def save_days(df, stations): @@ -402,7 +403,23 @@ def save_parameter_types(df, data_type, initial_import=False): "data_type": data_type, }, ) + + +def save_station_parameters(data_type): + # Add parameters that have data to the station + for station in Station.objects.filter(data_type=data_type): + for parameter in Parameter.objects.filter(data_type=data_type): + if ( + YearData.objects.filter( + station=station, + measurements__parameter=parameter, + measurements__value__gte=0, + ).count() + > 0 + ): station.parameters.add(parameter) + else: + station.parameters.remove(parameter) def save_stations(stations, data_type, initial_import_stations=False): @@ -528,6 +545,7 @@ def handle(self, *args, **options): ) save_parameter_types(df, data_type, initial_import) save_measurements(df, data_type, initial_import) + save_station_parameters(data_type) logger.info( f"Imported {DATA_TYPES_FULL_NAME[data_type]} observations until:{str(df.index[-1])}" ) diff --git a/environment_data/migrations/0001_initial.py b/environment_data/migrations/0001_initial.py index 88ed91dd3..5bcc6be69 100644 --- a/environment_data/migrations/0001_initial.py +++ b/environment_data/migrations/0001_initial.py @@ -8,7 +8,6 @@ class Migration(migrations.Migration): - initial = True dependencies = [] diff --git a/environment_data/tests/conftest.py b/environment_data/tests/conftest.py index b90fc7113..6dad1c53b 100644 --- a/environment_data/tests/conftest.py +++ b/environment_data/tests/conftest.py @@ -57,6 +57,8 @@ def measurements(parameters): def parameters(): Parameter.objects.create(id=1, name="AQINDEX_PT1H_avg") Parameter.objects.create(id=2, name="NO2_PT1H_avg") + Parameter.objects.create(id=3, name="WS_PT1H_avg") + return Parameter.objects.all() diff --git a/environment_data/tests/test_api.py b/environment_data/tests/test_api.py index 0db01fa8e..6635fc5c0 100644 --- a/environment_data/tests/test_api.py +++ b/environment_data/tests/test_api.py @@ -20,7 +20,8 @@ def test_station(api_client, stations, year_datas): assert result["data_type_verbose"] == DATA_TYPES_FULL_NAME[AIR_QUALITY] assert result["name"] == "Test" assert result["parameters_in_use"]["AQINDEX_PT1H_avg"] is True - assert result["parameters_in_use"]["NO2_PT1H_avg"] is False + assert result["parameters_in_use"]["NO2_PT1H_avg"] is True + assert result["parameters_in_use"]["WS_PT1H_avg"] is False @pytest.mark.django_db diff --git a/environment_data/tests/test_importer.py b/environment_data/tests/test_importer.py index dc1093d32..b0f25a5ba 100644 --- a/environment_data/tests/test_importer.py +++ b/environment_data/tests/test_importer.py @@ -68,11 +68,51 @@ def get_test_dataframe( return df +@pytest.mark.django_db +def test_import_week_data_measurements(): + from environment_data.management.commands.import_environment_data import ( + save_measurements, + save_parameter_types, + save_stations, + ) + + data_type = AIR_QUALITY + options = {"initial_import": True} + ImportState.objects.create( + data_type=data_type, year_number=aq_constants.START_YEAR, month_number=1 + ) + clear_cache() + stations = get_stations() + save_stations(stations, data_type, options["initial_import"]) + start_time = dateutil.parser.parse("2020-01-01T00:00:00Z") + end_time = dateutil.parser.parse("2020-01-17T23:45:00Z") + columns = [] + for station_name in STATION_NAMES: + for parameter in aq_constants.OBSERVABLE_PARAMETERS: + columns.append(f"{station_name} {parameter}") + df = get_test_dataframe(columns, start_time, end_time) + save_parameter_types(df, data_type, options["initial_import"]) + save_measurements(df, data_type, options["initial_import"]) + options = {"initial_import": False} + assert WeekData.objects.first().measurements.count() == Parameter.objects.count() + assert WeekData.objects.count() == Parameter.objects.count() + assert WeekData.objects.first().measurements.first().value == 3.0 + # Run incremental importer with different min_value to ensure no duplicate measurements are created + clear_cache() + df = get_test_dataframe(columns, start_time, end_time, min_value=1, max_value=4) + save_parameter_types(df, data_type, options["initial_import"]) + save_measurements(df, data_type, options["initial_import"]) + assert WeekData.objects.count() == Parameter.objects.count() + assert WeekData.objects.first().measurements.count() == Parameter.objects.count() + assert WeekData.objects.first().measurements.first().value == 2.5 + + @pytest.mark.django_db def test_importer(): from environment_data.management.commands.import_environment_data import ( save_measurements, save_parameter_types, + save_station_parameters, save_stations, ) @@ -106,6 +146,10 @@ def test_importer(): is True ) save_measurements(df, data_type, options["initial_import"]) + save_station_parameters(data_type) + assert list(Station.objects.all()[0].parameters.all()) == list( + Parameter.objects.all() + ) import_state = ImportState.objects.get(data_type=data_type) assert import_state.year_number == 2021 assert import_state.month_number == 12 @@ -252,7 +296,6 @@ def test_importer(): import_state = ImportState.objects.get(data_type=data_type) assert import_state.year_number == 2022 assert import_state.month_number == 1 - # Test initial import clear_cache() options = {"initial_import": True} diff --git a/mobility_data/README.md b/mobility_data/README.md index 95fc03ad8..36398ce48 100644 --- a/mobility_data/README.md +++ b/mobility_data/README.md @@ -188,6 +188,11 @@ Imports the outdoor gym devices from the services.unit model. i.e., sets referen ./manage.py import_parking_machines ``` +### School and kindergarten accessibility areas +``` +./manage.py import_wfs SchoolAndKindergartenAccessibilityArea +``` + ## Deletion To delete mobile units for a content type. ``` diff --git a/mobility_data/api/views.py b/mobility_data/api/views.py index 59660ed98..cd7295e51 100644 --- a/mobility_data/api/views.py +++ b/mobility_data/api/views.py @@ -5,6 +5,8 @@ from django.core.exceptions import ValidationError from django.db import connection, reset_queries from django.db.models import Q +from django.utils.decorators import method_decorator +from django.views.decorators.cache import cache_page from munigeo import api as munigeo_api from rest_framework import status, viewsets from rest_framework.exceptions import ParseError @@ -236,6 +238,7 @@ def get_queryset(self): return queryset + @method_decorator(cache_page(60 * 60)) def list(self, request): queryset = self.get_queryset() page = self.paginate_queryset(queryset) diff --git a/mobility_data/data/LatauspisteetTurku.csv b/mobility_data/data/LatauspisteetTurku.csv old mode 100755 new mode 100644 index a2fcd6fd5..f1fd5212d --- a/mobility_data/data/LatauspisteetTurku.csv +++ b/mobility_data/data/LatauspisteetTurku.csv @@ -1,83 +1,94 @@ -Teho (kW);Määrä;Pistoke;Latauspiste nro;Osoite;X;Y;Hallinnoija_fi;Hallinnoija_sv;Hallinnoija_en;Latauskohde;Maksu;Sähköhinta (€/kWh);Tuntihinta (€/h);Käyttötapa;Tieto hankittu;Tiedot päivitetty ;Muuta +Teho (kW);Määrä;Pistoke;Latauspiste nro;Osoite;X;Y;Hallinnoija_fi;Hallinnoija_sv;Hallinnoija_en;Latauskohde;Maksu;Sähköhinta (€/kWh);Tuntihinta (€/h);Käyttötapa;Tieto hankittu;Tiedot päivitetty ;Muuta +22;4;Type 2;1;Messukentänkatu 9-13;23455035;6704984;Turun Messukeskus;Åbo Mässcentrum;Turku Fair Center;Asiakas;Maksullinen;;;Virta app;;12.5.2022; +22;4;Type 2;2;Markulantie 150;23457792;6707016;Kesko;Kesko;Kesko;Asiakas/julkinen;Maksullinen;;;K-lataus;;12.5.2022; +50;2;CHAdeMO;2;Markulantie 150;23457792;6707016;Kesko;Kesko;Kesko;Asiakas/julkinen;Maksullinen;;;K-lataus;;12.5.2022; +50;2;CCS;2;Markulantie 150;23457792;6707016;Kesko;Kesko;Kesko;Asiakas/julkinen;Maksullinen;;;K-lataus;;12.5.2022; +11;1;Type 2;5;Vanha tampereentie 186;23462473;6708481;Wurth oy;Wurth oy;Wurth oy;Asiakas/suljettu;Ilmainen;;;Avaus hallinnoijalta;;12.5.2022; +17;1;Tesla;6;Kuormakatu 16;23463014,3;6707786,1;Unique Home;Unique Home;Unique Home;Asiakas/julkinen;;;;Tesla;;12.5.2022; +17;1;Type 2;6;Kuormakatu 16;23463014,3;6707786,1;Unique Home;Unique Home;Unique Home;Asiakas/julkinen;;;;Tesla;;12.5.2022; +22;2;Type 2;7;Juhana Hertuan Puistokatu 21;23457433;6703384;Auriga Business Center;Auriga Business Center;Auriga Business Center;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; 22;2;Type 2;8;Vallihaudankatu 1;23457649;6703032;Aimopark;Aimopark;Aimopark;Julkinen;Sisältyy hintaan;;;P-paikan hintaan;;12.5.2022; +22;4;Type 2;9;Ratapihankatu 53;23458575;6704625;;;;Julkinen;;;;;;12.5.2022; +11;3;Type 2;10;Kauppiaskatu 6;23459800;6704530;Scandic Hotels;Scandic Hotels;Scandic Hotels;Asiakas/suljettu;Maksullinen;;;Moovy;;12.5.2022; +22;2;Type 2;11;Puistokatu 10;23458778;6704142;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; +22;2;Type 2;12;Linnankatu 32;23459297;6704078;Radisson Blu;Radisson Blu;Radisson Blu;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; 22;2;Type 2;13;Yliopistonkatu 29;23459269;6704538;Aimopark;Aimopark;Aimopark;Julkinen;Sisältyy hintaan;;;P-paikan hintaan;;12.5.2022; -22;1;Type 2;13;Yliopistonkatu 29;23459349;6704540;Aimopark;Aimopark;Aimopark;Julkinen;Sisältyy hintaan;;;;;12.5.2022; +22;1;Type 2;13;Yliopistonkatu 29;23459349;6704540;Aimopark;Aimopark;Aimopark;Julkinen;Sisältyy hintaan;;;P-paikan hintaan;;12.5.2022; 22;2;Type 2;14;Kristiinankatu 11;23459447;6704486;Aimopark;Aimopark;Aimopark;Julkinen;Sisältyy hintaan;;;P-paikan hintaan;;12.5.2022; +22;2;Type 2;15;Puutarhakatu 4;23459372;6704737;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; 22;10;Type 2;16;Läntinenpitkäkatu 12b;23459314;6704978;Aimopark;Aimopark;Aimopark;Julkinen;Sisältyy hintaan;;;P-paikan hintaan;;12.5.2022; -4;8;Type 2;26;Untamonkatu;23461382;6704213;Aimopark;Aimopark;Aimopark;Julkinen;Sisältyy hintaan;;;P-paikan hintaan;;12.5.2022; -22;2;Type 2;40;Brahenkatu 3;23459987;6704688;Aimopark;Aimopark;Aimopark;Julkinen;Sisältyy hintaan;;;P-paikan hintaan;;12.5.2022; -22;2;Type 2;39;Korinpunojankatu 1;23457739;6710364;AL-autopalvelukeskus;AL-autopalvelukeskus;AL-autopalvelukeskus;Asiakas/suljettu;Maksullinen;;;Virta app;;12.5.2022; -22;2;Type 2;7;Juhana Hertuan Puistokatu 21;23457433;6703384;Auriga Business Center;Auriga Business Center;Auriga Business Center;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; -22;1;Tesla;28;Tierankatu 7;23462381;6703870;Autoklinikka;Autoklinikka;Autoklinikka;Asiakas/julkinen;Maksullinen;;;Tesla;;12.5.2022; -22;14;Type 2;58;Länsi-Avantintie 2;23465962;6706495;DB Schenker;DB Schenker;DB Schenker;Julkinen;Maksullinen;;;Virta app;;12.5.2022; -22;2;Type 2;38;Lentoasemantie 150;23460158;6711226;Finavia;Finavia;Finavia;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; -22;10;Type 2;43;Aviatie 2;23460479;6709908;Finnish design shop Showroom;Finnish design shop Showroom;Finnish design shop Showroom;Julkinen;Maksullinen;;;Virta app;;12.5.2022; -22;1;Type 2;35;Kaurakatu 43;23461543;6701593;Flör kukka ja puutarha;Flör kukka ja puutarha;Flör kukka ja puutarha;Asiakas/suljettu;Ilmainen;;;Avaus hallinnoijalta;;12.5.2022; +22;20;Type 2;17;Yliopistonkatu 29;23459664;6704639;Toriparkki;Torgparkeringen;Toriparkki;Julkinen;Sisältyy hintaan;;;Moovy;;12.5.2022; 22;2;Type 2;18;Puutori;23459724;6704995;Fortum;Fortum;Fortum;Julkinen;Maksullinen;;;Fortum C&D;;12.5.2022; +22;2;Type 2;19;Brahenkatu 11;23459802;6704952;Osuuspankki;Andelsbanken;Osuuspankki;Asiakas/julkinen;Maksullinen;;;Eparking;;12.5.2022; +22;1;Type 2;20;Vähäheikkiläntie 58;23458546;6702374;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; +50;1;CCS;20;Vähäheikkiläntie 58;23458546;6702374;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; +50;1;CHAdeMO;20;Vähäheikkiläntie 58;23458546;6702374;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; 22;1;Type 2;21;Kurjenmäenkatu 7;23460487;6703344;Hesburger;Hesburger;Hesburger;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; 75;2;CCS;21;Kurjenmäenkatu 7;23460487;6703344;Hesburger;Hesburger;Hesburger;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; -22;2;Type 2;31;Suurpäänkatu 2;23464465;6703637;Hesburger;Hesburger;Hesburger;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; -50;1;CCS;31;Suurpäänkatu 2;23464465;6703637;Hesburger;Hesburger;Hesburger;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; -50;1;CHAdeMO;31;Suurpäänkatu 2;23464465;6703637;Hesburger;Hesburger;Hesburger;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; -22;4;Type 2;29;Kongressikuja 1;23460907;6705525;Holiday Club Resorts;Holiday Club Resorts;Holiday Club Resorts;Asiakas/suljettu;Maksullinen;;;Virta app;;12.5.2022; -22;10;Type 2;41;Lustokatu 7;23464091;6706183;Hostelli Linnasmäki;Hostelli Linnasmäki;Hostelli Linnasmäki;Asiakas/suljettu;sisältyy hintaan;;;avaus hallinnoijalta;;12.5.2022; -11;2;type 2;48;Kakolankatu 14;23458543;6703776;Hotelli Kakola;Hotel Kakola;Hotel Kakola;asiakas/suljettu;sisältyy hintaan;;;p-paikan hintaan;;12.5.2022; -22;2;Type 2;71;Lautakunnankatu 1;23465113;6699763;Kaarinan Kaupunki;S:t Karins Stad;Kaarina;Julkinen;Maksullinen;;;Eparking;;12.5.2022; -22;8;Type 2;61;Myllynkatu 11;23456135;6709117;Kauppakeskus Mylly;Affärscentret Mylly;Shopping Center Mylly;Julkinen;Ilmainen;;;;;12.5.2022; -22;4;Type 2;33;Skanssinkatu 10;23462782;6702104;Kauppakeskus Skanssi;Affärscentret Skanssi;Shopping Center Skanssi;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; -22;4;Type 2;2;Markulantie 150;23457792;6707016;Kesko;Kesko;Kesko;Asiakas/julkinen;Maksullinen;;;K-lataus;;12.5.2022; -50;2;CHAdeMO;2;Markulantie 150;23457792;6707016;Kesko;Kesko;Kesko;Asiakas/julkinen;Maksullinen;;;K-lataus;;12.5.2022; -50;2;CCS;2;Markulantie 150;23457792;6707016;Kesko;Kesko;Kesko;Asiakas/julkinen;Maksullinen;;;K-lataus;;12.5.2022; -50;1;CCS;3;Rieskalähteentie 89;23459340;6707085;Kesko;Kesko;Kesko;Asiakas/suljettu;Maksullinen;;;K-lataus;;12.5.2022; 22;4;Type 2;22;Uudenmaantie 17;23460586;6703517;Kesko;Kesko;Kesko;Asiakas/julkinen;Maksullinen;;;K-lataus;;12.5.2022; 50;2;CCS;22;Uudenmaantie 17;23460586;6703517;Kesko;Kesko;Kesko;Asiakas/julkinen;Maksullinen;;;K-lataus;;12.5.2022; 50;2;CHAdeMO;22;Uudenmaantie 17;23460586;6703517;Kesko;Kesko;Kesko;Asiakas/julkinen;Maksullinen;;;K-lataus;;12.5.2022; +22;2;Type 2;23;Hämeenkatu 8;23460563;6704660;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; +22;2;Type 2;24;Kiinanmyllynkatu 10;23461145;6704644;Sodexo;Sodexo;Sodexo;Julkinen;Maksullinen;;;Virta app;;12.5.2022; +22;4;Type 2;25;Joukahaisenkatu 6;23461220;6704430;S-ryhmä;S-gruppen;S-group;Asiakas/suljettu;Maksullinen;;;Plug-It;;12.5.2022; +4;8;Type 2;26;Untamonkatu;23461382;6704213;Aimopark;Aimopark;Aimopark;Julkinen;Sisältyy hintaan;;;P-paikan hintaan;;12.5.2022; +22;4;Type 2;27;Lemminkäisenkatu 32;23461582;6704050;Turun Teknologiakiinteistöt;Turun Teknologiakiinteistöt;Turun Teknologiakiinteistöt;Asiakas/suljettu;Maksullinen;;;Virta app;;12.5.2022; +22;4;Type 2;29;Kongressikuja 1;23460907;6705525;Holiday Club Resorts;Holiday Club Resorts;Holiday Club Resorts;Asiakas/suljettu;Maksullinen;;;Virta app;;12.5.2022; +22;2;Tesla;29;Kongressikuja 1;23460907;6705525;Tesla;Tesla;Tesla;Asiakas/suljettu;Maksullinen;;;Tesla;;12.5.2022; 22;4;Type 2;30;Hakakatu 16;23461905;6705273;Kesko;Kesko;Kesko;Asiakas/julkinen;Maksullinen;;;K-lataus;;12.5.2022; 50;2;CCS;30;Hakakatu 16;23461905;6705273;Kesko;Kesko;Kesko;Asiakas/julkinen;Maksullinen;;;K-lataus;;12.5.2022; 50;2;CHAdeMO;30;Hakakatu 16;23461905;6705273;Kesko;Kesko;Kesko;Asiakas/julkinen;Maksullinen;;;K-lataus;;12.5.2022; +22;2;Type 2;31;Suurpäänkatu 2;23464465;6703637;Hesburger;Hesburger;Hesburger;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; +50;1;CCS;31;Suurpäänkatu 2;23464465;6703637;Hesburger;Hesburger;Hesburger;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; +50;1;CHAdeMO;31;Suurpäänkatu 2;23464465;6703637;Hesburger;Hesburger;Hesburger;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; +22;1;Type 2;32;Mustionkatu 31;23463271;6702414;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; +50;1;CCS;32;Mustionkatu 31;23463271;6702414;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; +50;1;CHAdeMO;32;Mustionkatu 31;23463271;6702414;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; +22;4;Type 2;33;Skanssinkatu 10;23462782;6702104;Kauppakeskus Skanssi;Affärscentret Skanssi;Shoping center Skanssi;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; 22;4;Type 2;34;Itäkaari 20;23462711;6702242;Kesko;Kesko;Kesko;Asiakas/julkinen;Maksullinen;;;K-lataus;;12.5.2022; +22;1;Type 2;35;Kaurakatu 43;23461543;6701593;Flör kukka ja puutarha;Flör kukka ja puutarha;Flör kukka ja puutarha;Asiakas/suljettu;Ilmainen;;;Avaus hallinnoijalta;;12.5.2022; +22;1;Type 2;36;Aninkaistenkatu 20;23459724;6705152;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; +43;1;Type 2;36;Aninkaistenkatu 20;23459724;6705152;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; +50;1;CCS;36;Aninkaistenkatu 20;23459724;6705152;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; +50;1;CHAdeMO;36;Aninkaistenkatu 20;23459724;6705152;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; +22;2;Type 2;37;Vaunukatu 8;23459288;6705244;Logomo;Logomo;Logomo;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; +22;2;Type 2;38;Lentoasemantie 150;23460158;6711226;Finnavia;Finnavia;Finnavia;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; +22;2;Type 2;39;Korinpunojankatu 1;23457739;6710364;AL-autopalvelukeskus;AL-autopalvelukeskus;AL-autopalvelukeskus;Asiakas/suljettu;Maksullinen;;;Virta app;;12.5.2022; +22;2;Type 2;40;Brahenkatu 3;23459987;6704688;Aimopark;Aimopark;Aimopark;Julkinen;Sisältyy hintaan;;;P-paikan hintaan;;12.5.2022; +22;10;Type 2;41;Lustokatu 7;23464091;6706183;Hostelli Linnasmäki;Hostelli Linnasmäki;Hostelli Linnasmäki;Asiakas/suljettu;sisältyy hintaan;;;avaus hallinnoijalta;;12.5.2022; +22;2;Type 2;42;Mäkilänpolku 1;23458465;6708375;Nättinummen Lämpö Oy;Nättinummen Lämpö Oy;Nättinummen Lämpö Oy;asiakas/julkinen;maksullinen;;;virta app;;12.5.2022; +22;10;Type 2;43;Aviatie 2;23460479;6709908;Finnish design shop Showroom;Finnish design shop Showroom;Finnish design shop Showroom;Julkinen;Maksullinen;;;Virta app;;12.5.2022; 150;2;CCS;44;Vanha Kakskerrantie 1;23457565;6700949;Kesko;Kesko;Kesko;asiakas/julkinen;Maksullinen;;;k-lataus;;12.5.2022; 50;1;chAdeMO;44;vanha Kakskerrantie 1;23457565;6700949;kesko;Kesko;Kesko;asiakas/julkinen;maksullinen;;;k-lataus;;12.5.2022; 11;1;type 2;44;vanha Kakskerrantie 1;23457565;6700949;kesko;Kesko;Kesko;asiakas/julkinen;maksullinen;;;k-lataus;;12.5.2022; -22;4;Type 2;62;Voudinkatu 5;23454422;6708920;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;12.5.2022; -50;2;CCS;62;Voudinkatu 5;23454422;6708920;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;12.5.2022; -50;2;CHAdeMO;62;Voudinkatu 5;23454422;6708920;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;12.5.2022; -350;2;CCS;66;Ajurintie 1;23484722,1;6701606,8;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;Ionity;;12.5.2022; -22;2;Type 2;66;Ajurintie 1;23484722,1;6701606,8;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;12.5.2022; -22;1;Type 2;69;Krossinkatu 1;23465975;6700970;Kesko;Kesko;Kesko;Julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; -14;2;Type 2;70;Kuskinkatu 3;23465480;6699761;Koy Kaarinan City;Koy Kaarinan City;Koy Kaarinan City;Julkinen;Maksullinen;;;Virta app;;12.5.2022; -22;1;Type 2;20;Vähäheikkiläntie 58;23458546;6702374;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; -50;1;CCS;20;Vähäheikkiläntie 58;23458546;6702374;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; -50;1;CHAdeMO;20;Vähäheikkiläntie 58;23458546;6702374;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; -22;1;Type 2;32;Mustionkatu 31;23463271;6702414;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; -50;1;CCS;32;Mustionkatu 31;23463271;6702414;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; -50;1;CHAdeMO;32;Mustionkatu 31;23463271;6702414;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; +150;5;ccs;45;Piiskakuja 10;23462773;6708376;Rinta Jouppi;Rinta Jouppi;Rinta Jouppi;asiakas/julkinen;maksullinen;;;Recharge Infra;;12.5.2022; 50;1;ccs;46;Viilarinkatu 12;23457083;6706956;Lidl;Lidl;Lidl;asiakas/julkinen;ilmainen;;;aikarajoittettu;;12.5.2022; 50;1;chadeMO;46;viilarinkatu 12;23457083;6706956;lidl;Lidl;Lidl;asiakas/julkinen;ilmainen;;;aikarajoittettu;;12.5.2022; 22;1;Type 2;46;viilarinkatu 12;23457083;6706956;Lidl;Lidl;Lidl;asiakas/julkinen;ilmainen;;;aikarajoittettu;;12.5.2022; -22;4;Type 2;53;Kalevantie 19-23;23461968;6704367;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; -22;4;Type 2;55;Kollikatu 1;23458203;6704989;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; -22;4;Type 2;56;Kärsämäentie 2;23460565;6707027;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; -22;4;Type 2;60;Simpukkatie 4;23470398;6710289;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; -22;2;Type 2;37;Vaunukatu 8;23459288;6705244;Logomo;Logomo;Logomo;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; 11;2;type 2;47;Pläkkisepänkatu 2;23456346;6706250;Motonet;Motonet;Motonet;asiakas/julkinen;Maksullinen;;;Motonet;;12.5.2022; -50;1;CCS;68;Varastomiehenkatu 4;23465795;6701265;Motonet;Motonet;Motonet;Asiakas/suljettu;Maksullinen;;;Motonet;;12.5.2022; -50;1;CHAdeMO;68;Varastomiehenkatu 4;23465795;6701265;Motonet;Motonet;Motonet;Asiakas/suljettu;Maksullinen;;;Motonet;;12.5.2022; -11;2;Type 2;68;Varastomiehenkatu 4;23465795;6701265;Motonet;Motonet;Motonet;Asiakas/suljettu;Maksullinen;;;Motonet;;12.5.2022; -150;4;CCS;59;Haimiontie 25;23467100,9;6708415,6;Neste;Neste;Neste;Julkinen;Maksullinen;;;Recharge Infra;;12.5.2022; -22;2;Type 2;42;Mäkilänpolku 1;23458465;6708375;Nättinummen Lämpö Oy;Nättinummen Lämpö Oy;Nättinummen Lämpö Oy;asiakas/julkinen;maksullinen;;;virta app;;12.5.2022; -22;1;Type 2;4;Vanha tampereentie 184;23462443;6708459;Onninen oy;Onninen ab;Onninen oy;Asiakas/suljettu;Ilmainen;;;Avaus hallinnoijalta;;12.5.2022; -22;2;Type 2;19;Brahenkatu 11;23459802;6704952;Osuuspankki;Andelsbanken;Osuuspankki;Asiakas/julkinen;Maksullinen;;;Eparking;;12.5.2022; -22;2;Type 2;12;Linnankatu 32;23459297;6704078;Radisson Blu;Radisson Blu;Radisson Blu;Asiakas/julkinen;Maksullinen;;;Virta app;;12.5.2022; -150;5;ccs;45;Piiskakuja 10;23462773;6708376;Rinta Jouppi;Rinta Jouppi;Rinta Jouppi;asiakas/julkinen;maksullinen;;;Recharge Infra;;12.5.2022; -11;3;Type 2;10;Kauppiaskatu 6;23459800;6704530;Scandic Hotels;Scandic Hotels;Scandic Hotels;Asiakas/suljettu;Maksullinen;;;Moovy;;12.5.2022; -22;4;Type 2;25;Joukahaisenkatu 6;23461220;6704430;S-ryhmä;S-gruppen;S-group;Asiakas/suljettu;Maksullinen;;;Plug-It;;12.5.2022; +11;2;type 2;48;Kakolankatu 14;23458543;6703776;Hotelli Kakola;Hotel Kakola;Hotel Kakola;asiakas/suljettu;sisältyy hintaan;;;p-paikan hintaan;;12.5.2022; +50;2;CCS;49;Lemminkäisenkatu 34;23461574;6703930;Veritas;Veritas;Veritas;Julkinen;Maksullinen;;;Virta app;;12.5.2022; +50;2;CHAdeMO;49;Lemminkäisenkatu 34;23461574;6703930;Veritas;Veritas;Veritas;Julkinen;Maksullinen;;;Virta app;;12.5.2022; +22;4;Type 2;50;Naturakuja 1;23460535;6705009;Turun Yliopisto;Åbo universitet;University of Turku;Asiakas/julkinen;Maksullinen;;;Eparking;;12.5.2022; +22;2;Type 2;51;Alakyläntie 2;23457435;6705351;Viherlassila;Viherlassila;Viherlassila;Asiakas/julkinen;Ilmainen;;;Avaus hallinnoijalta;;12.5.2022; +300;4;CCS;52;Biolinja 27;23463079;6702221;;;;;;;;;;12.5.2022; +4;22;Type 2;53;Kalevantie 19-23;23461968;6704367;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; 100;1;CCS;54;Kalevantie 41;23462841;6703955;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; 50;1;CHAdeMO;54;Kalevantie 41;23462841;6703955;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; 22;4;Type 2;54;Kalevantie 41;23462841;6703955;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; +22;4;Type 2;55;Kollikatu 1;23458203;6704989;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; +22;4;Type 2;56;Kärsämäentie 2;23460565;6707027;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; 100;1;CCS;57;Viilarinkatu 3;23457458;6707108;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; 50;1;CHAdeMO;57;Viilarinkatu 3;23457458;6707108;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; 22;4;Type 2;57;Viilarinkatu 3;23457458;6707108;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; +22;14;Type 2;58;Länsi-Avantintie 2;23465962;6706495;DB Schenker;DB Schenker;DB Schenker;Julkinen;Maksullinen;;;Virta app;;12.5.2022; +150;4;CCS;59;Haimiontie 25;23467100,9;6708415,6;Neste;Neste;Neste;Julkinen;Maksullinen;;;Recharge Infra;;12.5.2022; +22;4;Type 2;60;Simpukkatie 4;23470398;6710289;Lidl;Lidl;Lidl;Asiakas/julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; +22;8;Type 2;61;Myllynkatu 11;23456135;6709117;Kauppakeskus Mylly;Affärscentret Mylly;Shoping center Mylly;Julkinen;Ilmainen;;;;;12.5.2022; +22;4;Type 2;62;Voudinkatu 5;23454422;6708920;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;12.5.2022; +50;2;CCS;62;Voudinkatu 5;23454422;6708920;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;12.5.2022; +50;2;CHAdeMO;62;Voudinkatu 5;23454422;6708920;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;12.5.2022; +22;2;Type 2;63;Tasalanaukio 1-9;23454143;6708392;;;;Julkinen;Maksullinen;;;Eparking;;12.5.2022; 43;1;Type 2;64;Piilipuunkatu 2;23453830,2;6710682,1;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; 50;1;CCS;64;Piilipuunkatu 2;23453830,2;6710682,1;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; 50;1;CHAdeMO;64;Piilipuunkatu 2;23453830,2;6710682,1;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; @@ -85,29 +96,57 @@ 50;1;CCS;65;Länsiportti 1;23478296;6703497;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; 50;1;CHAdeMO;65;Länsiportti 1;23478296;6703497;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; 43;1;Type 2;65;Länsiportti 1;23478296;6703497;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; +350;2;CCS;66;Ajurintie 1;23484722,1;6701606,8;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;Ionity;;12.5.2022; +22;2;Type 2;66;Ajurintie 1;23484722,1;6701606,8;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;12.5.2022; 100;1;CCS;67;Kairiskulmantie 3;23462927;6700598;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; 50;1;CHAdeMO;67;Kairiskulmantie 3;23462927;6700598;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; 22;4;Type 2;67;Kairiskulmantie 3;23462927;6700598;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;12.5.2022; -22;2;Tesla;29;Kongressikuja 1;23460907;6705525;Tesla;Tesla;Tesla;Asiakas/suljettu;Maksullinen;;;Tesla;;12.5.2022; -22;20;Type 2;17;Yliopistonkatu 29;23459664;6704639;Toriparkki;Torgparkeringen;Toriparkki;Julkinen;Sisältyy hintaan;;;Moovy;;12.5.2022; -22;2;Type 2;11;Puistokatu 10;23458778;6704142;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; -22;2;Type 2;15;Puutarhakatu 4;23459372;6704737;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; -22;2;Type 2;23;Hämeenkatu 8;23460563;6704660;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; -22;2;Type 2;24;Kiinanmyllynkatu 10;23461145;6704644;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; -22;1;Type 2;36;Aninkaistenkatu 20;23459724;6705152;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; -43;1;Type 2;36;Aninkaistenkatu 20;23459724;6705152;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; -50;1;CCS;36;Aninkaistenkatu 20;23459724;6705152;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; -50;1;CHAdeMO;36;Aninkaistenkatu 20;23459724;6705152;Turku Energia;Åbo Energi;Turku Energia;Julkinen;Maksullinen;;;Virta app;;12.5.2022; -22;4;Type 2;1;Messukentänkatu 9-13;23455035;6704984;Turun Messukeskus;Åbo Mässcentrum;Turku Fair Center;Asiakas;Maksullinen;;;Virta app;;12.5.2022; -22;4;Type 2;27;Lemminkäisenkatu 32;23461582;6704050;Turun Teknologiakiinteistöt;Turun Teknologiakiinteistöt;Turku Technology Properties;Asiakas/suljettu;Maksullinen;;;Virta app;;12.5.2022; -22;4;Type 2;50;Naturakuja 1;23460535;6705009;Turun Yliopisto;Turun Teknologiakiinteistöt;University of Turku;Asiakas/julkinen;Maksullinen;;;Eparking;;12.5.2022; -17;1;Tesla;6;Kuormakatu 16;23463014,3;6707786,1;Unique Home;Unique Home;Unique Home;Asiakas/julkinen;;;;Tesla;;12.5.2022; -17;1;Type 2;6;Kuormakatu 16;23463014,3;6707786,1;Unique Home;Unique Home;Unique Home;Asiakas/julkinen;;;;Tesla;;12.5.2022; -50;2;CCS;49;Lemminkäisenkatu 34;23461574;6703930;Veritas;Veritas;Veritas;Julkinen;Maksullinen;;;Virta app;;12.5.2022; -50;2;CHAdeMO;49;Lemminkäisenkatu 34;23461574;6703930;Veritas;Veritas;Veritas;Julkinen;Maksullinen;;;Virta app;;12.5.2022; -22;2;Type 2;51;Alakyläntie 2;23457435;6705351;Veritas;Veritas;Veritas;Asiakas/julkinen;Ilmainen;;;Avaus hallinnoijalta;;12.5.2022; -11;1;Type 2;5;Vanha tampereentie 186;23462473;6708481;Wurth oy;Wurth oy;Wurth oy;Asiakas/suljettu;Ilmainen;;;Avaus hallinnoijalta;;12.5.2022; -22;4;Type 2;9;Ratapihankatu 53;23458575;6704625;;;;Julkinen;;;;;;12.5.2022; -300;4;CCS;52;Biolinja 27;23463079;6702221;;;;;;;;;;12.5.2022; -22;2;Type 2;63;Tasalanaukio 1-9;23454143;6708392;;;;Julkinen;Maksullinen;;;Eparking;;12.5.2022; -;317;;;;;;;;;;;;;;;; +50;1;CCS;68;Varastomiehenkatu 4;23465795;6701265;Motonet;Motonet;Motonet;Asiakas/suljettu;Maksullinen;;;Motonet;;12.5.2022; +50;1;CHAdeMO;68;Varastomiehenkatu 4;23465795;6701265;Motonet;Motonet;Motonet;Asiakas/suljettu;Maksullinen;;;Motonet;;12.5.2022; +11;2;Type 2;68;Varastomiehenkatu 4;23465795;6701265;Motonet;Motonet;Motonet;Asiakas/suljettu;Maksullinen;;;Motonet;;12.5.2022; +22;1;Type 2;69;Krossinkatu 1;23465975;6700970;Kesko;Kesko;Kesko;Julkinen;Ilmainen;;;aikarajoittettu;;12.5.2022; +14;2;Type 2;70;Kuskinkatu 3;23465480;6699761;Koy Kaarinan City;Koy Kaarinan City;Koy Kaarinan City;Julkinen;Maksullinen;;;Virta app;;12.5.2022; +22;2;Type 2;71;Lautakunnankatu 1;23465113;6699763;Kaarinan Kaupunki;S:t Karins Stad;Kaarina;Julkinen;Maksullinen;;;Eparking;;12.5.2022; +250;4;CCS;72;Laarinkatu 1;23464830;6699783;Hesburger;Hesburger;Hesburger;Julkinen;Maksullinen;;;Virta app;;22.12.2023; +100;2;CCS;73;Hovirinnantie 2;23464778;6699659;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;22.12.2023; +22;4;Type 2;73;Hovirinnantie 2;23464778;6699659;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;22.12.2023; +11;4;Type 2;74;Päiväläisenkatu 1;23464912;6699191;Kaarinan Kaupunki;S:t Karins Stad;Kaarina;Julkinen;Maksullinen;;;Eparking;;22.12.2023; +22;2;Type 2;75;Taalintehtaankatu 1d;23463640;6702624;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;; +11;2;Type 2;76;Peltolantie 2;23461306,4;6702535,7;Turun ja Kaarinan seurakuntayhtymä;Åbo svenska församling;The Lutheran Church in Turku and Kaarina;Julkinen;Maksullinen;;;Plug-It;;; +100;2;CCS;77;Mustionkatu 1 ;23462635,9;6703025,4;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;; +22;4;Type 2;77;Mustionkatu 1 ;23462635,9;6703025,4;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;; +22;2;Type 2;78;Kalevantie 3;23461458;6704480;If;If;If;Julkinen;Maksullinen;;;Virta app;;; +22;20;Type 2;79;Joukahaisenkatu 8;23461329;6704346;Aimopark;Aimopark;Aimopark;Julkinen;Maksullinen;;;Taskuparkki;;; +22;10;Type 2;80;Vähäheikkiläntie 64;23458521,9;6702252,1;Boost Sport Club;Boost Sport Club;Boost Sport Club;Julkinen;Maksullinen;;;Parking Energy;;; +150;4;CCS;81;Kölikatu 2;23458111,8;6702210,9;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;; +22;2;Type 2;81;Kölikatu 2;23458111,8;6702210,9;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;; +11;4;Type 2;82;Tammistontie 258;23454962,1;6697586,1;S-marin;S-marin;S-marin;Asiakas/suljettu;Maksullinen;;;Muu;;; +22;20;Type 2;83;Linnankatu 90;23457163,7;6702791,5;Turun Satama;Åbo Hamn;The Port of Turku;Julkinen;Maksullinen;;;Eparking;;; +22;4;Type 2;84;Hevoskarintie 23;23456176,8;6702954,8;Ruissalon Telakka;Runsala Varvet;Ruissalon Dockyard;Julkinen;Maksullinen;;;Tesla;;; +150;4;CCS;85;Koulukatu 29;23458519,4;6704923,4;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;; +11;6;Type 2;86;Viilarinkatu 5;23457257,3;6707101,2;Länsi 1;Länsi 1;Länsi 1;Julkinen;Maksullinen;;;Eparking;;; +175;2;CCS;87;Rieskalähteentie 75;23459421;6706962;Keskusautohalli;Keskusautohalli;Keskusautohalli;Julkinen;Maksullinen;;;Greenflux;;; +200;2;CCS;88;Rieskalähteentie 89;23459265;6707177;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;; +22;7;Type 2;89;Kekkurintie 6;23459438,4;6707725,2;Jarkko Nieminen Areena;Jarkko Nieminen Areena;Jarkko Nieminen Areena;Julkinen;Maksullinen;;;Plug-It;;; +100;2;CCS;90;Korjasmäenkatu 1;23459988,4;6708205,5;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;; +150;6;CCS;91;Vanha Tampereentie 108;23460787,1;6707042,6;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;; +22;2;Type 2;91;Vanha Tampereentie 108;23460787,1;6707042,6;S-ryhmä;S-gruppen;S-group;Julkinen;Maksullinen;;;ABC-lataus;;; +11;3;Type 2;92;Telekatu 1;23460920,5;6709203,6;;;;Julkinen;Maksullinen;;;Virta app;;; +150;4;CCS;93;Kuninkaanväylä 22;23457657,1;6709898,3;Puuilo;Puuilo;Puuilo;Julkinen;Maksullinen;;;Virta app;;; +22;4;Type 2;94;Itäniityntie 15;23457571;6709388;Ikea;Ikea;Ikea;Julkinen;Maksullinen;;;Enersense;;; +22;4;Type 2;95;Itäniityntie 9;23457176,8;6709305,6;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;; +160;4;CCS;96;Itäniityntie 14;23457301,3;6709073,4;Gigantti;Gigantti;Gigantti;Julkinen;Maksullinen;;;Virta app;;; +11;2;Type 2;97;Kuloistentie 10;23456453;6708686,7;Laattapiste;Laattapiste;Laattapiste;Julkinen;Maksullinen;;;Virta app;;; +11;5;Type 2;98;Vesilaitoksentie 1;23455409,6;6709133,8;Veho;Veho;Veho;Julkinen;Maksullinen;;;Plug-It;;; +22;4;Type 2;99;Eeronkuja 2;23454037,9;6707865,2;Raision Kaupunki;Reso Stad;City of Raisio;Julkinen;Maksullinen;;;Plug-It;;; +17;4;Type 2;100;Kirkkoväärtinkuja 18;23454397,4;6707904,5;Raision Kaupunki;Reso Stad;City of Raisio;Julkinen;Maksullinen;;;Plug-It;;; +22;2;Type 2;101;Ihalantie 35;23454799,1;6706606,8;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;; +22;1;Type 2;102;Puutarhakatu 36;23458375,2;6704053,1;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;; +22;2;Type 2;103;Brahenkatu 13;23459705,7;6704976,7;Puutorin pysäköinti;Trätorget parkeringen;Puutorin parking;Julkinen;Sisältyy hintaan;;;Eparking;;; +200;6;CCS;104;Reivikatu 5-7;23464705;6706611;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;; +22;6;Type 2;104;Reivikatu 5-7;23464705;6706611;Kesko;Kesko;Kesko;Julkinen;Maksullinen;;;K-lataus;;; +22;4;Type 2;105;Eteläkaari 6;23466504,9;6706292,2;Scania;Scania;Scania;Julkinen;Maksullinen;;;Plug-It;;; +120;4;CCS;106;Uimarannantie 1;23467516,1;6705855;Puuilo;Puuilo;Puuilo;Julkinen;Maksullinen;;;Virta app;;; +22;2;Type 2;106;Uimarannantie 1;23467516,1;6705855;Puuilo;Puuilo;Puuilo;Julkinen;Maksullinen;;;Virta app;;; +11;5;Type 2;107;Loukinaistentie 10;23467530,2;6706589;Veho;Veho;Veho;Julkinen;Maksullinen;;;Plug-It;;; +22;2;Type 2;108;Detmarinkuja 1;23464479,3;6704453,4;;;;Julkinen;Maksullinen;;;Eparking;;; diff --git a/mobility_data/importers/data/content_types.yml b/mobility_data/importers/data/content_types.yml index 3e3c19558..9584a2aed 100644 --- a/mobility_data/importers/data/content_types.yml +++ b/mobility_data/importers/data/content_types.yml @@ -59,7 +59,7 @@ content_types: sv: Lastningsplats en: Loading place - # Content types from lounaistieto shapefiles importer + # Content types imported from lounaistieto shapefile importer - content_type_name: BusStopSouthwestFinland name: fi: Bussipysäkki @@ -138,6 +138,7 @@ content_types: en: Recreational routes in Southwest Finland # End of lounaistieto shapefile importer content types + # Content types importer from opaskarta.turku.fi - content_type_name: GuestMarina name: fi: Vierasvenesatama @@ -405,7 +406,14 @@ content_types: fi: Nopeusrajoitusalue sv: Hastighetsbegränsningszon en: Speed limit zone - # End of WFS importer content types + + - content_type_name: SchoolAndKindergartenAccessibilityArea + name: + fi: Koulu ja päiväkoti saavutettavuusalue + sv: Tillgänglighetsområde för skola och daghem + en: School and kindergarten accessibility area + + # End of content types importer from opaskarta.turku.fi - content_type_name: Underpass name: diff --git a/mobility_data/importers/data/wfs_importer_config.yml b/mobility_data/importers/data/wfs_importer_config.yml index 665e1db58..a5a71403f 100644 --- a/mobility_data/importers/data/wfs_importer_config.yml +++ b/mobility_data/importers/data/wfs_importer_config.yml @@ -514,3 +514,17 @@ features: speed_limit: wfs_field: rajoitus wfs_type: int + + - content_type_name: SchoolAndKindergartenAccessibilityArea + content_type_description: Accessibility area of schools and kindergartens + wfs_layer: GIS:Koulujen_Paivakotien_saavutettavuus_2023 + fields: + name: + fi: Kohde + extra_fields: + Minuutit: + wfs_field: Minuutit + wfs_type: int + Kulkumuoto: + wfs_field: Kulkumuoto + diff --git a/mobility_data/importers/data/wfs_importer_config_example.yml b/mobility_data/importers/data/wfs_importer_config_example.yml index a3fc5761e..ea98129bf 100644 --- a/mobility_data/importers/data/wfs_importer_config_example.yml +++ b/mobility_data/importers/data/wfs_importer_config_example.yml @@ -6,6 +6,8 @@ features: # Optional, the URL of the WFS, default=TURKU_WFS_URL. wfs_url: http://opaskartta.turku.fi/TeklaOGCWeb/WFS.ashx # Optional, Max number of features to be include when fetching data from WFS. + wfs_version: 1.0.0 + # Optional, version of the WFS, default=1.0.0 max_features: 10000 # Optional, define the SRID of the source data if not found in the source data. srid: 3067 diff --git a/mobility_data/importers/under_and_overpasses.py b/mobility_data/importers/under_and_overpasses.py index 35a7ef3b6..a16aa5cf5 100644 --- a/mobility_data/importers/under_and_overpasses.py +++ b/mobility_data/importers/under_and_overpasses.py @@ -10,7 +10,7 @@ from .utils import MobileUnitDataBase URL = ( - "https://julkinen.vayla.fi/inspirepalvelu/digiroad/wfs?service=WFS&request=GetFeature" + "https://avoinapi.vaylapilvi.fi/vaylatiedot/digiroad/ows?service=WFS&request=GetFeature" f"&typeName=dr_tielinkki_toim_lk&outputFormat=GML3&bbox={TURKU_BBOX},EPSG:4326&srsName=EPSG:4326" ) UNDERPASS_CONTENT_TYPE_NAME = "Underpass" @@ -33,7 +33,7 @@ def get_json_data(url): class Pass(MobileUnitDataBase): def __init__(self, feature): super().__init__() - coord_str = feature["digiroad:SHAPE"]["gml:LineString"]["gml:posList"] + coord_str = feature["digiroad:geom"]["gml:LineString"]["gml:posList"] coord_list = coord_str.split(" ") coords = () i = 0 @@ -54,11 +54,12 @@ def get_under_and_overpass_objects(): for feature in json_data["wfs:FeatureCollection"]["gml:featureMembers"][ "digiroad:dr_tielinkki_toim_lk" ]: + if ( - feature.get("digiroad:KUNTAKOODI", None) == KUNTAKOODI - and feature.get("digiroad:TOIMINN_LK", None) == TOIMINN_LK + feature.get("digiroad:kuntakoodi", None) == KUNTAKOODI + and feature.get("digiroad:toiminn_lk", None) == TOIMINN_LK ): - silta_alik = int(feature.get("digiroad:SILTA_ALIK", None)) + silta_alik = int(feature.get("digiroad:silta_alik", None)) match silta_alik: case PASS_TYPES.UNDERPASS: underpasses.append(Pass(feature)) diff --git a/mobility_data/importers/wfs.py b/mobility_data/importers/wfs.py index b2b04f2b9..137f36116 100644 --- a/mobility_data/importers/wfs.py +++ b/mobility_data/importers/wfs.py @@ -20,9 +20,13 @@ DEFAULT_SOURCE_DATA_SRID = 3877 DEFAULT_MAX_FEATURES = 1000 DEFAULT_WFS_TYPE = "string" +DEFAULT_WFS_VERSION = "1.0.0" logger = logging.getLogger("mobility_data") -WFS_URL = "{wfs_url}?service=WFS&request=GetFeature&typeName={wfs_layer}&outputFormat=GML3&maxFeatures={max_features}" +WFS_URL = ( + "{wfs_url}?service=WFS&version={wfs_version}&request=GetFeature&" + "typeName={wfs_layer}&outputFormat=GML3&maxFeatures={max_features}" +) @db.transaction.atomic @@ -134,8 +138,12 @@ def add_feature(self, feature, config): def get_data_source(config, max_features): wfs_url = config.get("wfs_url", settings.TURKU_WFS_URL) + wfs_version = config.get("wfs_version", DEFAULT_WFS_VERSION) url = WFS_URL.format( - wfs_url=wfs_url, wfs_layer=config["wfs_layer"], max_features=max_features + wfs_url=wfs_url, + wfs_version=wfs_version, + wfs_layer=config["wfs_layer"], + max_features=max_features, ) ds = DataSource(url) return ds diff --git a/mobility_data/tasks.py b/mobility_data/tasks.py index 7bc4a593e..6539ba71c 100644 --- a/mobility_data/tasks.py +++ b/mobility_data/tasks.py @@ -105,6 +105,13 @@ def import_paavonpolkus(name="import_paavonpolkus"): management.call_command("import_wfs", "PaavonPolku") +@shared_task_email +def import_school_and_kindergarten_accessibility_areas( + name="import_import_school_and_kindergarten_accessibility_areas", +): + management.call_command("import_wfs", "SchoolAndKindergartenAccessibilityArea") + + @shared_task_email def delete_mobility_data(args=None, name="delete_mobility_data"): management.call_command("delete_mobility_data", args) diff --git a/mobility_data/tests/data/under_and_overpasses.json b/mobility_data/tests/data/under_and_overpasses.json index c3a32970f..832012002 100644 --- a/mobility_data/tests/data/under_and_overpasses.json +++ b/mobility_data/tests/data/under_and_overpasses.json @@ -13,102 +13,102 @@ { "digiroad:dr_tielinkki_toim_lk": [{ "@gml:id": "dr_tielinkki_toim_lk.4364843", - "digiroad:LINK_ID": "4364843", - "digiroad:LINK_MMLID": "71182700", - "digiroad:HALLINN_LK": "3", - "digiroad:TOIMINN_LK": "7", - "digiroad:AJOSUUNTA": "2", - "digiroad:LINKKITYYP": "12", - "digiroad:SILTA_ALIK": "0", - "digiroad:TIENIMI_SU": "Nummen mets\u00e4tie", - "digiroad:ENS_TALO_V": "263", - "digiroad:ENS_TALO_O": "264", - "digiroad:VIIM_TAL_V": "251", - "digiroad:VIIM_TAL_O": "252", - "digiroad:KUNTAKOODI": "636", - "digiroad:MUOKKAUSPV": "25.12.2018 01:00:40", - "digiroad:SIJ_TARK": "3000", - "digiroad:KOR_TARK": "201", - "digiroad:GEOM_FLIP": "1", - "digiroad:ALKU_PAALU": "0", - "digiroad:LOPP_PAALU": "325.688", - "digiroad:GEOM_LAHDE": "1", - "digiroad:SHAPE": { + "digiroad:link_id": "4364843", + "digiroad:link_mmlid": "71182700", + "digiroad:hallinn_lk": "3", + "digiroad:toiminn_lk": "7", + "digiroad:ajosuunta": "2", + "digiroad:linkkityyp": "12", + "digiroad:silta_alik": "0", + "digiroad:tienimi_su": "Nummen mets\u00e4tie", + "digiroad:ens_talo_v": "263", + "digiroad:ens_talo_o": "264", + "digiroad:viim_tal_v": "251", + "digiroad:viim_tal_o": "252", + "digiroad:kuntakoodi": "636", + "digiroad:muokkauspv": "25.12.2018 01:00:40", + "digiroad:sij_tark": "3000", + "digiroad:kor_tark": "201", + "digiroad:geom_flip": "1", + "digiroad:alku_paalu": "0", + "digiroad:lopp_paalu": "325.688", + "digiroad:geom_lahde": "1", + "digiroad:geom": { "gml:LineString": { "@srsName": "http://www.opengis.net/gml/srs/epsg.xml#4326", "@srsDimension": "3", "gml:posList": "22.52851124 60.76362636 73.823 22.52807579 60.76374084 73.908 22.52768355 60.76380312 74.073 22.52719903 60.76398517 74.286 22.52638126 60.76421428 74.051 22.52563276 60.76444342 73.59 22.52485326 60.76468844 74.223 22.52381843 60.76500016 74.74 22.52340543 60.76512753 75.399" } }, - "digiroad:ALKUSOLMU": "9304724", - "digiroad:LOPPUSOLMU": "9298108", - "digiroad:MTK_TIE_LK": "12141" + "digiroad:alkusolmu": "9304724", + "digiroad:loppusolmu": "9298108", + "digiroad:mtk_tie_lk": "12141" }, { "@gml:id": "dr_tielinkki_toim_lk.4364843", - "digiroad:LINK_ID": "644680", - "digiroad:LINK_MMLID": "71182700", - "digiroad:HALLINN_LK": "99", - "digiroad:TOIMINN_LK": "8", - "digiroad:AJOSUUNTA": "2", - "digiroad:LINKKITYYP": "12", - "digiroad:SILTA_ALIK": "-1", - "digiroad:TIENIMI_SU": "Läntinen Rantakatu", - "digiroad:ENS_TALO_V": "263", - "digiroad:ENS_TALO_O": "264", - "digiroad:VIIM_TAL_V": "251", - "digiroad:VIIM_TAL_O": "252", - "digiroad:KUNTAKOODI": "853", - "digiroad:MUOKKAUSPV": "25.12.2018 01:00:40", - "digiroad:SIJ_TARK": "3000", - "digiroad:KOR_TARK": "201", - "digiroad:GEOM_FLIP": "1", - "digiroad:ALKU_PAALU": "0", - "digiroad:LOPP_PAALU": "325.688", - "digiroad:GEOM_LAHDE": "1", - "digiroad:SHAPE": { + "digiroad:link_id": "644680", + "digiroad:link_mmlid": "71182700", + "digiroad:hallinn_lk": "99", + "digiroad:toiminn_lk": "8", + "digiroad:ajosuunta": "2", + "digiroad:linkkityyp": "12", + "digiroad:silta_alik": "-1", + "digiroad:tienimi_su": "Läntinen Rantakatu", + "digiroad:ens_talo_v": "263", + "digiroad:ens_talo_o": "264", + "digiroad:viim_tal_v": "251", + "digiroad:viim_tal_o": "252", + "digiroad:kuntakoodi": "853", + "digiroad:muokkauspv": "25.12.2018 01:00:40", + "digiroad:sij_tark": "3000", + "digiroad:kor_tark": "201", + "digiroad:geom_flip": "1", + "digiroad:alku_paalu": "0", + "digiroad:lopp_paalu": "325.688", + "digiroad:geom_lahde": "1", + "digiroad:geom": { "gml:LineString": { "@srsName": "http://www.opengis.net/gml/srs/epsg.xml#4326", "@srsDimension": "3", "gml:posList": "22.32554152 60.53907714 41.976 22.32535151 60.53944881 41.501" } }, - "digiroad:ALKUSOLMU": "9304724", - "digiroad:LOPPUSOLMU": "9298108", - "digiroad:MTK_TIE_LK": "12141" + "digiroad:alkusolmu": "9304724", + "digiroad:loppusolmu": "9298108", + "digiroad:mtk_tie_lk": "12141" }, { "@gml:id": "dr_tielinkki_toim_lk.4364843", - "digiroad:LINK_ID": "644680", - "digiroad:LINK_MMLID": "71182700", - "digiroad:HALLINN_LK": "99", - "digiroad:TOIMINN_LK": "8", - "digiroad:AJOSUUNTA": "2", - "digiroad:LINKKITYYP": "12", - "digiroad:SILTA_ALIK": "1", - "digiroad:TIENIMI_SU": "Läntinen Testikatu", - "digiroad:ENS_TALO_V": "263", - "digiroad:ENS_TALO_O": "264", - "digiroad:VIIM_TAL_V": "251", - "digiroad:VIIM_TAL_O": "252", - "digiroad:KUNTAKOODI": "853", - "digiroad:MUOKKAUSPV": "25.12.2018 01:00:40", - "digiroad:SIJ_TARK": "3000", - "digiroad:KOR_TARK": "201", - "digiroad:GEOM_FLIP": "1", - "digiroad:ALKU_PAALU": "0", - "digiroad:LOPP_PAALU": "325.688", - "digiroad:GEOM_LAHDE": "1", - "digiroad:SHAPE": { + "digiroad:link_id": "644680", + "digiroad:link_mmlid": "71182700", + "digiroad:hallinn_lk": "99", + "digiroad:toiminn_lk": "8", + "digiroad:ajosuunta": "2", + "digiroad:linkkityyp": "12", + "digiroad:silta_alik": "1", + "digiroad:tienimi_su": "Läntinen Testikatu", + "digiroad:ens_talo_v": "263", + "digiroad:ens_talo_o": "264", + "digiroad:viim_tal_v": "251", + "digiroad:viim_tal_o": "252", + "digiroad:kuntakoodi": "853", + "digiroad:muokkauspv": "25.12.2018 01:00:40", + "digiroad:sij_tark": "3000", + "digiroad:kor_tark": "201", + "digiroad:geom_flip": "1", + "digiroad:alku_paalu": "0", + "digiroad:lopp_paalu": "325.688", + "digiroad:geom_lahde": "1", + "digiroad:geom": { "gml:LineString": { "@srsName": "http://www.opengis.net/gml/srs/epsg.xml#4326", "@srsDimension": "3", "gml:posList": "22.3295715 60.54484321 32.318 22.33085745 60.54539151 33.151" } }, - "digiroad:ALKUSOLMU": "9304724", - "digiroad:LOPPUSOLMU": "9298108", - "digiroad:MTK_TIE_LK": "12141" + "digiroad:alkusolmu": "9304724", + "digiroad:loppusolmu": "9298108", + "digiroad:mtk_tie_lk": "12141" } ] } diff --git a/requirements.txt b/requirements.txt index 71b681a26..b3cf0e537 100644 --- a/requirements.txt +++ b/requirements.txt @@ -53,7 +53,7 @@ coverage==5.5 # via pytest-cov cron-descriptor==1.2.35 # via django-celery-beat -django==4.1.10 +django==4.1.13 # via # -r requirements.in # django-celery-beat diff --git a/street_maintenance/api/views.py b/street_maintenance/api/views.py index df9c728c9..87f6f9cef 100644 --- a/street_maintenance/api/views.py +++ b/street_maintenance/api/views.py @@ -1,7 +1,8 @@ from datetime import datetime -from functools import lru_cache +from django.utils.decorators import method_decorator from django.utils.timezone import make_aware +from django.views.decorators.cache import cache_page from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter from rest_framework import mixins, viewsets from rest_framework.exceptions import ParseError @@ -187,7 +188,7 @@ def get_queryset(self): queryset = queryset.filter(timestamp__gte=make_aware(start_date_time)) return queryset - @lru_cache(maxsize=16) + @method_decorator(cache_page(60 * 15)) def list(self, request): queryset = self.get_queryset() page = self.paginate_queryset(queryset) diff --git a/street_maintenance/management/commands/constants.py b/street_maintenance/management/commands/constants.py index b82e03a97..beb9adde5 100644 --- a/street_maintenance/management/commands/constants.py +++ b/street_maintenance/management/commands/constants.py @@ -140,11 +140,15 @@ "siirtoajo": [MUUT], "Kelintarkastus": [MUUT], "Sulamisvesien hallinta / höyrytys": [MUUT], + "Sulamisveden haittojen torjunta": [MUUT], "Sorateiden kunnossapito": [MUUT], "Äkillinen hoitotyö": [MUUT], "KT-valu": [MUUT], "Pintakelirikko": [MUUT], "Liikennemerkkityö": [MUUT], + "Puiden hoito": [MUUT], + "Puistometsien hoito": [MUUT], + "Hiekkalaatikoiden täyttö": [MUUT], } TIMESTAMP_FORMATS = { INFRAROAD: "%Y-%m-%d %H:%M:%S", diff --git a/street_maintenance/tests/test_api.py b/street_maintenance/tests/test_api.py index e46fcd43c..b56048572 100644 --- a/street_maintenance/tests/test_api.py +++ b/street_maintenance/tests/test_api.py @@ -1,7 +1,7 @@ -from datetime import datetime, timedelta +from datetime import timedelta import pytest -import pytz +from django.utils import timezone from rest_framework.reverse import reverse from street_maintenance.management.commands.constants import ( @@ -12,33 +12,45 @@ START_DATE_TIME_FORMAT, ) -UTC_TIMEZONE = pytz.timezone("UTC") - @pytest.mark.django_db -def test_geometry_history(api_client, geometry_historys): +def test_geometry_history_list(api_client, geometry_historys): url = reverse("street_maintenance:geometry_history-list") response = api_client.get(url) assert response.json()["count"] == 5 - # Test provider parameter + + +@pytest.mark.django_db +def test_geometry_history_list_provider_parameter(api_client, geometry_historys): url = reverse("street_maintenance:geometry_history-list") + f"?provider={KUNTEC}" response = api_client.get(url) # Fixture data contains 2 KUNTEC GeometryHistroy rows assert response.json()["count"] == 2 - # Test event parameter + + +@pytest.mark.django_db +def test_geometry_history_list_event_parameter(api_client, geometry_historys): url = reverse("street_maintenance:geometry_history-list") + f"?event={AURAUS}" response = api_client.get(url) # 3 INFRAROAD AURAUS events and 1 KUNTEC assert response.json()["count"] == 4 - # Test event and provider parameter + + +@pytest.mark.django_db +def test_geometry_history_list_event_and_provider_parameter( + api_client, geometry_historys +): url = ( reverse("street_maintenance:geometry_history-list") + f"?provider={KUNTEC}&event={LIUKKAUDENTORJUNTA}" ) response = api_client.get(url) assert response.json()["count"] == 1 - # test start_date_time parameter - start_date_time = datetime.now(UTC_TIMEZONE) - timedelta(hours=1) + + +@pytest.mark.django_db +def test_geometry_history_list_start_date_time_parameter(api_client, geometry_historys): + start_date_time = timezone.now() - timedelta(hours=1) url = ( reverse("street_maintenance:geometry_history-list") + f"?start_date_time={start_date_time.strftime(START_DATE_TIME_FORMAT)}" @@ -48,7 +60,7 @@ def test_geometry_history(api_client, geometry_historys): geometry_history = response.json()["results"][0] assert geometry_history["geometry_type"] == "LineString" assert geometry_history["provider"] == INFRAROAD - start_date_time = datetime.now(UTC_TIMEZONE) - timedelta(days=1, hours=2) + start_date_time = timezone.now() - timedelta(days=1, hours=2) url = ( reverse("street_maintenance:geometry_history-list") + f"?start_date_time={start_date_time.strftime(START_DATE_TIME_FORMAT)}"