Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 63 additions & 6 deletions electricitymap/contrib/parsers/FR_O.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"eolien": "wind",
"hydraulique": "hydro",
"moteurs_diesels": "oil",
"photovoltaique": "solar",
"turbines_combustion": "gas",
},
"FR-COR": {
Expand Down Expand Up @@ -106,7 +107,49 @@
for API_TYPE in groups
}

IGNORED_VALUES = ["jour", "total", "statut", "date", "heure", "liaisons", "tac"]
IGNORED_VALUES = [
"jour",
"total",
"statut",
"date",
"date_jour",
"heure",
"liaisons",
"tac",
]

# The API exposes aggregate sub-totals (``filiere_*``) and percentage shares
# (``part_*``) alongside the per-mode generation values. These must be ignored
# to avoid double counting.
IGNORED_PREFIXES = ("part_", "filiere_")

# Historical data is served by a different, national dataset
# (``courbe-de-charge-de-la-production-delectricite-par-filiere``) which uses a
# more aggregated schema (``*_mw`` suffixes) than the per-territory live feeds.
# Thermal generation (oil/gas) and bagasse/coal are each reported as a single
# lumped value that cannot be split into individual modes, so both are reported
# as ``unknown``.
HISTORICAL_GENERATION_MAPPING = {
"photovoltaique_mw": "solar",
"eolien_mw": "wind",
"hydraulique_mw": "hydro",
"micro_hydraulique_mw": "hydro",
"bioenergies_mw": "biomass",
"geothermie_mw": "geothermal",
"thermique_mw": "unknown",
"bagasse_charbon_mw": "unknown",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think bagasse_charbon is a type of biomass. "dry pulpy fibrous material that remains after crushing sugarcane or sorghum stalks to extract their juice. It is used as a biofuel for the production of heat, energy, and electricity, and in the manufacture of pulp and building materials."

}

HISTORICAL_STORAGE_MAPPING = {"stockage_mw": "battery"}

HISTORICAL_IGNORED_VALUES = [
"date_heure",
"territoire",
"statut",
"production_totale_mw",
"importations_mw",
"cout_moyen_de_production_eur_mwh",
]


def generate_url(zone_key, target_datetime):
Expand Down Expand Up @@ -195,22 +238,36 @@ def fetch_production(
zone_key, session, target_datetime
)

# Historical and live data come from different datasets with different
# schemas, so the mappings used to interpret each field differ accordingly.
is_historical = target_datetime is not None
generation_mapping = (
HISTORICAL_GENERATION_MAPPING
if is_historical
else API_PARAMETER_GROUPS["production"][zone_key]
)
storage_mapping = HISTORICAL_STORAGE_MAPPING if is_historical else STORAGE_MAPPING
ignored_values = HISTORICAL_IGNORED_VALUES if is_historical else IGNORED_VALUES
ignored_prefixes = () if is_historical else IGNORED_PREFIXES

production_breakdown_list = ProductionBreakdownList(logger=logger)
for production_object in production_objects:
production = ProductionMix()
storage = StorageMix()
for mode_key in production_object:
if mode_key in API_PARAMETER_GROUPS["production"][zone_key]:
if mode_key in generation_mapping:
production.add_value(
API_PARAMETER_GROUPS["production"][zone_key][mode_key],
generation_mapping[mode_key],
production_object[mode_key],
correct_negative_with_zero=True,
)
elif mode_key in STORAGE_MAPPING:
elif mode_key in storage_mapping:
value = production_object[mode_key]
storage.add_value(
STORAGE_MAPPING[mode_key], -production_object[mode_key]
storage_mapping[mode_key],
-value if value is not None else None,
)
elif mode_key in IGNORED_VALUES:
elif mode_key in ignored_values or mode_key.startswith(ignored_prefixes):
pass
else:
logger.warning(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# serializer version: 1
# name: test_fetch_price
list([
dict({
'currency': 'EUR',
'datetime': datetime.datetime(2018, 1, 1, 0, 0, tzinfo=datetime.timezone.utc),
'price': 193.7,
'source': 'opendata.edf.fr',
'sourceType': <EventSourceType.measured: 'measured'>,
'zoneKey': 'RE',
}),
dict({
'currency': 'EUR',
'datetime': datetime.datetime(2018, 1, 1, 1, 0, tzinfo=datetime.timezone.utc),
'price': 195.8,
'source': 'opendata.edf.fr',
'sourceType': <EventSourceType.measured: 'measured'>,
'zoneKey': 'RE',
}),
])
# ---
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# serializer version: 1
# name: test_fetch_production[FR-COR]
list([
dict({
'correctedModes': list([
]),
'datetime': datetime.datetime(2026, 5, 13, 9, 0, tzinfo=datetime.timezone.utc),
'production': dict({
'biomass': 0.6,
'gas': 11.2,
'hydro': 37.8,
'oil': 57,
'solar': 109,
'wind': 9.5,
}),
'source': 'opendata-corse.edf.fr',
'sourceType': <EventSourceType.estimated: 'estimated'>,
'storage': dict({
'battery': 0,
}),
'zoneKey': 'FR-COR',
}),
dict({
'correctedModes': list([
]),
'datetime': datetime.datetime(2026, 5, 13, 9, 15, tzinfo=datetime.timezone.utc),
'production': dict({
'biomass': 0.6,
'gas': 9.3,
'hydro': 59.7,
'oil': 50.3,
'solar': 91.4,
'wind': 9.3,
}),
'source': 'opendata-corse.edf.fr',
'sourceType': <EventSourceType.estimated: 'estimated'>,
'storage': dict({
'battery': 0,
}),
'zoneKey': 'FR-COR',
}),
dict({
'correctedModes': list([
]),
'datetime': datetime.datetime(2026, 5, 13, 9, 30, tzinfo=datetime.timezone.utc),
'production': dict({
'biomass': 0.6,
'gas': 4.9,
'hydro': 61.1,
'oil': 50.7,
'solar': 93.4,
'wind': 7.5,
}),
'source': 'opendata-corse.edf.fr',
'sourceType': <EventSourceType.estimated: 'estimated'>,
'storage': dict({
'battery': 0,
}),
'zoneKey': 'FR-COR',
}),
])
# ---
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# serializer version: 1
# name: test_fetch_production[GF]
list([
dict({
'correctedModes': list([
]),
'datetime': datetime.datetime(2026, 5, 26, 10, 1, tzinfo=datetime.timezone.utc),
'production': dict({
'biomass': 10.91,
'hydro': 131.962333,
'oil': 170.52,
'solar': 0.0593,
}),
'source': 'opendata-guyane.edf.fr',
'sourceType': <EventSourceType.measured: 'measured'>,
'storage': dict({
}),
'zoneKey': 'GF',
}),
dict({
'correctedModes': list([
]),
'datetime': datetime.datetime(2026, 5, 26, 10, 16, tzinfo=datetime.timezone.utc),
'production': dict({
'biomass': 10.817,
'hydro': 135.6173,
'oil': 174.027667,
'solar': 0.250633,
}),
'source': 'opendata-guyane.edf.fr',
'sourceType': <EventSourceType.measured: 'measured'>,
'storage': dict({
}),
'zoneKey': 'GF',
}),
dict({
'correctedModes': list([
]),
'datetime': datetime.datetime(2026, 5, 26, 10, 31, tzinfo=datetime.timezone.utc),
'production': dict({
'biomass': 10.5813,
'hydro': 134.420667,
'oil': 215.396967,
'solar': 0.498333,
}),
'source': 'opendata-guyane.edf.fr',
'sourceType': <EventSourceType.measured: 'measured'>,
'storage': dict({
}),
'zoneKey': 'GF',
}),
])
# ---
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# serializer version: 1
# name: test_fetch_production[GP]
list([
dict({
'correctedModes': list([
'geothermal',
]),
'datetime': datetime.datetime(2026, 5, 26, 9, 21, tzinfo=datetime.timezone.utc),
'production': dict({
'biomass': 21.762,
'coal': 21.792,
'gas': 0,
'geothermal': 0,
'hydro': 3.2298,
'oil': 78.548,
'solar': 0,
'wind': 18.03,
}),
'source': 'opendata-guadeloupe.edf.fr',
'sourceType': <EventSourceType.estimated: 'estimated'>,
'storage': dict({
}),
'zoneKey': 'GP',
}),
dict({
'correctedModes': list([
'geothermal',
]),
'datetime': datetime.datetime(2026, 5, 26, 9, 36, tzinfo=datetime.timezone.utc),
'production': dict({
'biomass': 22.716,
'coal': 22.092,
'gas': 0,
'geothermal': 0,
'hydro': 3.2298,
'oil': 76.63,
'solar': 0,
'wind': 17.684,
}),
'source': 'opendata-guadeloupe.edf.fr',
'sourceType': <EventSourceType.estimated: 'estimated'>,
'storage': dict({
}),
'zoneKey': 'GP',
}),
dict({
'correctedModes': list([
'geothermal',
]),
'datetime': datetime.datetime(2026, 5, 26, 9, 51, tzinfo=datetime.timezone.utc),
'production': dict({
'biomass': 22.576,
'coal': 21.996,
'gas': 0,
'geothermal': 0,
'hydro': 3.2298,
'oil': 76.4518,
'solar': 0.03,
'wind': 18.2,
}),
'source': 'opendata-guadeloupe.edf.fr',
'sourceType': <EventSourceType.estimated: 'estimated'>,
'storage': dict({
}),
'zoneKey': 'GP',
}),
])
# ---
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# serializer version: 1
# name: test_fetch_production[MQ]
list([
dict({
'correctedModes': list([
]),
'datetime': datetime.datetime(2026, 5, 26, 9, 23, tzinfo=datetime.timezone.utc),
'production': dict({
'biomass': 41.142,
'gas': 5.41,
'hydro': 0,
'oil': 178.0158,
'solar': 0,
'wind': 0,
}),
'source': 'opendata-martinique.edf.fr',
'sourceType': <EventSourceType.estimated: 'estimated'>,
'storage': dict({
}),
'zoneKey': 'MQ',
}),
dict({
'correctedModes': list([
]),
'datetime': datetime.datetime(2026, 5, 26, 9, 38, tzinfo=datetime.timezone.utc),
'production': dict({
'biomass': 40.744,
'gas': 5.41,
'hydro': 0,
'oil': 186.2398,
'solar': 0,
'wind': 0,
}),
'source': 'opendata-martinique.edf.fr',
'sourceType': <EventSourceType.estimated: 'estimated'>,
'storage': dict({
}),
'zoneKey': 'MQ',
}),
dict({
'correctedModes': list([
]),
'datetime': datetime.datetime(2026, 5, 26, 9, 53, tzinfo=datetime.timezone.utc),
'production': dict({
'biomass': 40.644,
'gas': 5.41,
'hydro': 0,
'oil': 189.306,
'solar': 0.1258,
'wind': 0,
}),
'source': 'opendata-martinique.edf.fr',
'sourceType': <EventSourceType.estimated: 'estimated'>,
'storage': dict({
}),
'zoneKey': 'MQ',
}),
])
# ---
Loading
Loading