Skip to content

Commit e9822a1

Browse files
committed
Nobil data source
1 parent b3728d4 commit e9822a1

File tree

25 files changed

+676
-521
lines changed

25 files changed

+676
-521
lines changed

src/evmap_backend/api.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from ninja.orm import register_field
88

99
from evmap_backend.chargers.models import ChargingSite
10+
from evmap_backend.data_sources import UpdateMethod
11+
from evmap_backend.data_sources.models import UpdateState
1012
from evmap_backend.data_sources.registry import get_data_source
1113

1214
api = NinjaAPI(urls_namespace="evmap")
@@ -35,7 +37,7 @@ def sites(request, sw_lat: float, sw_lng: float, ne_lat: float, ne_lng: float):
3537
@api.post("/push/{data_source}")
3638
def push(request, data_source: str):
3739
data_source = get_data_source(data_source)
38-
if not data_source.supports_push:
40+
if not UpdateMethod.HTTP_PUSH in data_source.supported_update_methods:
3941
raise ValueError("Data source does not support push")
4042

4143
data_source.verify_push(request)
@@ -47,4 +49,6 @@ def push(request, data_source: str):
4749
body = gzip.decompress(body)
4850

4951
data_source.process_push(body)
52+
53+
UpdateState(data_source=data_source.id, push=True).save()
5054
logging.info(f"Successfully processed push for {data_source}")

src/evmap_backend/chargers/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class ConnectorTypes(models.TextChoices):
7575
TYPE_3A = "Type 3A", "Type 3A"
7676
TYPE_3C = "Type 3C", "Type 3C"
7777
CHADEMO = "CHAdeMO", "CHAdeMO"
78+
MCS = "MCS", "MCS"
7879

7980
SCHUKO = "Schuko", "Schuko (Type F)"
8081
# TODO: add other household plugs

src/evmap_backend/data_sources/__init__.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,20 @@ class DataType(Enum):
2525
"""
2626

2727

28+
class UpdateMethod(Enum):
29+
PULL = 1
30+
"""Data can be retrieved by calling load_data from a cron job"""
31+
32+
HTTP_PUSH = 2
33+
"""
34+
Data source an call our server at /push/{data_source_id} to push new data.
35+
verify_push and process_push have to be implemented.
36+
"""
37+
38+
BACKGROUND_SERVICE = 3
39+
"""A background service needs to run continuously (e.g., with an MQTT connection) to receive updates"""
40+
41+
2842
class DataSource(ABC):
2943
@property
3044
@abstractmethod
@@ -38,7 +52,7 @@ def supported_data_types(self) -> List[DataType]:
3852

3953
@property
4054
@abstractmethod
41-
def supports_push(self) -> bool:
55+
def supported_update_methods(self) -> List[UpdateMethod]:
4256
pass
4357

4458
@abstractmethod
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from django.contrib import admin
2+
3+
from evmap_backend.data_sources.models import UpdateState
4+
5+
6+
class UpdateStateAdmin(admin.ModelAdmin):
7+
readonly_fields = ["data_source", "last_update", "push"]
8+
list_display = ["data_source", "last_update", "push"]
9+
list_filter = ["push"]
10+
11+
12+
# Register your models here.
13+
admin.site.register(UpdateState, UpdateStateAdmin)

src/evmap_backend/data_sources/datex2/source.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from cryptography.x509.verification import PolicyBuilder, Store
1010
from django.http import HttpRequest
1111

12-
from evmap_backend.data_sources import DataSource, DataType
12+
from evmap_backend.data_sources import DataSource, DataType, UpdateMethod
1313
from evmap_backend.data_sources.datex2.parser.json import Datex2JsonParser
1414
from evmap_backend.data_sources.datex2.parser.xml import Datex2XmlParser
1515
from evmap_backend.settings import BASE_DIR
@@ -22,8 +22,8 @@ def supported_data_types(self) -> List[DataType]:
2222
return [DataType.STATIC]
2323

2424
@property
25-
def supports_push(self) -> bool:
26-
return False
25+
def supported_update_methods(self) -> List[UpdateMethod]:
26+
return [UpdateMethod.PULL]
2727

2828
@abstractmethod
2929
def get_data(self) -> str:
@@ -56,8 +56,8 @@ def _parse_data(self, root: str):
5656

5757
class BaseMobilithekDatex2DataSource(BaseDatex2DataSource):
5858
@property
59-
def supports_push(self) -> bool:
60-
return True
59+
def supported_update_methods(self) -> List[UpdateMethod]:
60+
return [UpdateMethod.PULL, UpdateMethod.HTTP_PUSH]
6161

6262
def verify_push(self, request: HttpRequest):
6363
if "X-Forwarded-Client-Cert" not in request.headers:

src/evmap_backend/data_sources/eliso/source.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from tqdm import tqdm
77

88
from evmap_backend.chargers.models import Chargepoint, ChargingSite, Connector
9-
from evmap_backend.data_sources import DataSource, DataType
9+
from evmap_backend.data_sources import DataSource, DataType, UpdateMethod
1010
from evmap_backend.sync import sync_chargers
1111

1212
API_URL = "https://mobilithek.info:8443/mobilithek/api/v1.0/subscription"
@@ -77,8 +77,8 @@ def supported_data_types(self) -> List[DataType]:
7777
return [DataType.STATIC]
7878

7979
@property
80-
def supports_push(self) -> bool:
81-
return False
80+
def supported_update_methods(self) -> List[UpdateMethod]:
81+
return [UpdateMethod.PULL]
8282

8383
def load_data(self):
8484
root = get_mobilithek_data()

src/evmap_backend/data_sources/management/commands/load_data_source.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from django.core.management import BaseCommand
22

3+
from evmap_backend.data_sources.models import UpdateState
34
from evmap_backend.data_sources.registry import get_data_source, list_available_sources
45

56

@@ -22,6 +23,7 @@ def handle(self, *args, **options):
2223
)
2324

2425
data_source.load_data()
26+
UpdateState(data_source=data_source.id, push=False).save()
2527

2628
self.stdout.write(
2729
self.style.SUCCESS(f"Successfully loaded data from source: {source_id}")
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Generated by Django 5.2.6 on 2025-12-13 23:19
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
initial = True
9+
10+
dependencies = []
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name="UpdateState",
15+
fields=[
16+
(
17+
"data_source",
18+
models.CharField(max_length=255, primary_key=True, serialize=False),
19+
),
20+
("last_update", models.DateTimeField(auto_now=True)),
21+
("push", models.BooleanField()),
22+
],
23+
),
24+
]

src/evmap_backend/data_sources/migrations/__init__.py

Whitespace-only changes.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from django.db import models
2+
3+
4+
class UpdateState(models.Model):
5+
data_source = models.CharField(
6+
max_length=255, primary_key=True, blank=False, null=False
7+
)
8+
last_update = models.DateTimeField(auto_now=True)
9+
push = models.BooleanField(blank=False, null=False)

0 commit comments

Comments
 (0)