Repository: electricitymaps/electricitymaps-contrib

Issue:
Countries offline: AG, PE, RU-1+RU-2, AU-WA
Several countries are / were offline for a while now or have reoccurring issues.
Creating a checklist below so everyone can see what the issue could be and which parser had been fixed already. [Kibana-logger](https://kibana.electricitymap.org/app/kibana#/discover/10af54f0-0c4a-11e9-85c1-1d63df8c862c?_g=()&_a=(columns:!(message,extra.key,level),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'96f67170-0c49-11e9-85c1-1d63df8c862c',key:level,negate:!f,params:(query:ERROR,type:phrase),type:phrase,value:ERROR),query:(match:(level:(query:ERROR,type:phrase))))),index:'96f67170-0c49-11e9-85c1-1d63df8c862c',interval:auto,query:(language:lucene,query:''),sort:!('@timestamp',asc))) error output below.
Should you work on fixing one of the parsers, please comment below and then tag this issue in the PR. :)

- [x] Argentina (AR.py)
Could fail due to a html-format issue:
'NoneType' object has no attribute 'getText'
fixed by #2139 

- [x] Peru (PE.py)
Could fail due to a faulty/outdated date-time format:
Failed to match '(?P<M>\d{1,2})/(?P<D>\d{1,2})/(?P<YYYY>\d{4}) (?P<h>\d{1,2}):(?P<mm>\d{2}):(?P<ss>\d{2}) (?P<A>(am|pm|AM|PM))' when parsing '2020/01/03 01:00:00'
partly fixed in #2135 

Quoting Alex
> Data for 3rd of January up until last hour. So we should rather find a way to check for
start "today-1" and end "today"
and also
start "today" and end "today + 1" and merge the two datasets when both are returned.

> This way we will get the most recent stuff. This could be why the parser failed in the past for a couple of hours each day, from what I can remember.

- [x] Russia West + Russia East (RU.py) + Exchanges of Russia with neighbouring states
Maybe there was a change in the source data format? But running it locally works fine for me. Maybe too many requests?
Expecting value: line 1 column 1 (char 0)

- [x] Western Australia (AU_WA.py or /lib/AU_solar.py)
Maybe there was a change in the source data format?
Expecting value: line 1 column 1 (char 0)
fixed by #2140 


Discussion:
Hey @alixunderplatz, happy new year to you! :tada: 

I ran the AR parser locally and got this back. Initially I thought the service was down (very common as it's a big pile of spaghetti) but it looks like the HTMl has changed.

```shell
(EM) chris@ThinkPad:~/electricitymap$ python parsers/AR.py 
fetch_production() ->
Traceback (most recent call last):
  File "parsers/AR.py", line 964, in <module>
    print(fetch_production())
  File "parsers/AR.py", line 832, in fetch_production
    gdt = get_datetime(session=None)
  File "parsers/AR.py", line 640, in get_datetime
    find_hour = timesoup.find("option", selected="selected", value="1").getText()
AttributeError: 'NoneType' object has no attribute 'getText'
```

I'll send a PR for this.
Hey @systemcatch , thanks, happy new year to you, too!!!
One thing I could recently find for Argentina is the breakdown of the renewables in one diagram:
https://despachorenovables.cammesa.com/renovables/
No need for a mapping for these. I feel like "hidraulico" might only include small hydro which needs to be added to large hydro.
@alixunderplatz that's useful, I can take a look at adding that to the parser.
Hi, I've worked on fixing Western Australia, just putting up a PR now.
@robertahunt any chance you could check the EM server to see why the RU parser is failing? I remember when I ran it locally everything was fine.
Hey @systemcatch! I am having no luck with tracking down the error on the server-side on my own. It looks like the data is fine and the feeder processes it properly in my tests. I will catch up with oli on this tomorrow.
@systemcatch , Unfortunately our server is getting a 404 response from the data source, which is why our parser is failing. @corradio suspects this is because our server's IP address has been blocked by the Russion TSO due to a high volume of requests. I have it on my to-do list to ask them to whitelist us, but I am not sure what else to do. Any other suggestions?
@robertahunt hmmm that's annoying, as to what to do asking them to whitelist us is a good idea. If they refuse maybe using a proxy is possible ( if Tomorrow is comfortable with that).

Maybe the EM needs to have a review of how often they are requesting data in general, some of the sources aren't really designed to be used by outsiders and too many requests might result in being blocked.
Just to keep this up to date, I sent a mail to our contact at the russian tso. I will let you know when he gets back to me.
RU parsers look to be working again!
I noticed that the Western Australia parser was offline again with a different issue - I've put up another PR.

Resolving patch:
diff --git a/parsers/PE.py b/parsers/PE.py
--- a/parsers/PE.py
+++ b/parsers/PE.py
@@ -3,8 +3,9 @@
 
 import arrow
 import dateutil
-from .lib.validation import validate
 import requests
+from .lib.validation import validate
+from logging import getLogger
 
 tz = 'America/Lima'
 
@@ -25,40 +26,89 @@ def parse_date(item):
     return arrow.get(item['Nombre'], 'YYYY/MM/DD hh:mm:ss').replace(tzinfo=dateutil.tz.gettz(tz))
 
 
-def fetch_production(zone_key='PE', session=None, target_datetime=None, logger=None):
+def fetch_production(zone_key='PE', session=None, target_datetime=None, logger=getLogger(__name__)):
+    """
+    Requests the last known production mix (in MW) of a given country
+    Arguments:
+    zone_key (optional) -- used in case a parser is able to fetch multiple countries
+    session (optional) -- request session passed in order to re-use an existing session
+    target_datetime (optional) -- used if parser can fetch data for a specific day
+    logger (optional) -- handles logging when parser is run as main
+    Return:
+    A list of dictionaries in the form:
+    {
+      'zoneKey': 'FR',
+      'dt': '2017-01-01T00:00:00Z',
+      'production': {
+          'biomass': 0.0,
+          'coal': 0.0,
+          'gas': 0.0,
+          'hydro': 0.0,
+          'nuclear': null,
+          'oil': 0.0,
+          'solar': 0.0,
+          'wind': 0.0,
+          'geothermal': 0.0,
+          'unknown': 0.0
+      },
+      'storage': {
+          'hydro': -10.0,
+      },
+      'source': 'mysource.com'
+    }
+    """
     if target_datetime:
         raise NotImplementedError('This parser is not yet able to parse past dates')
     
     r = session or requests.session()
     url = 'http://www.coes.org.pe/Portal/portalinformacion/Generacion'
-    response = r.post(url, data={
-        'fechaInicial': arrow.now(tz=tz).shift(days=-1).format('DD/MM/YYYY'),
-        'fechaFinal': arrow.now(tz=tz).format('DD/MM/YYYY'),
+
+    current_date = arrow.now(tz=tz)
+
+    today = current_date.format('DD/MM/YYYY')
+    yesterday = current_date.shift(days=-1).format('DD/MM/YYYY')
+    end_date = current_date.shift(days=+1).format('DD/MM/YYYY')
+
+    # To guarantee a full 24 hours of data we must make 2 requests.
+
+    response_today = r.post(url, data={
+        'fechaInicial': today,
+        'fechaFinal': end_date,
         'indicador': 0
     })
-    obj = response.json()['GraficoTipoCombustible']['Series']
+
+    response_yesterday = r.post(url, data={
+        'fechaInicial': yesterday,
+        'fechaFinal': today,
+        'indicador': 0
+    })
+
+    data_today = response_today.json()['GraficoTipoCombustible']['Series']
+    data_yesterday = response_yesterday.json()['GraficoTipoCombustible']['Series']
+    raw_data = data_today + data_yesterday
 
     # Note: We receive MWh values between two intervals!
-    interval_hours = (parse_date(obj[0]['Data'][1]) - parse_date(
-        obj[0]['Data'][0])).total_seconds() / 3600
+    interval_hours = (parse_date(raw_data[0]['Data'][1]) - parse_date(
+        raw_data[0]['Data'][0])).total_seconds() / 3600
 
     data = []
     datetimes = []
 
-    for serie in obj:
-        k = serie['Name']
-        if not k in MAP_GENERATION:
-            raise Exception('Unknown production type %s' % k)
-        for v in serie['Data']:
-            datetime = parse_date(v)
+    for series in raw_data:
+        k = series['Name']
+        if k not in MAP_GENERATION:
+            logger.warning(f'Unknown production type "{k}" for Peru')
+            continue
+        for v in series['Data']:
+            dt = parse_date(v)
             try:
-                i = datetimes.index(datetime)
+                i = datetimes.index(dt)
             except ValueError:
                 i = len(datetimes)
-                datetimes.append(datetime)
+                datetimes.append(dt)
                 data.append({
                     'zoneKey': zone_key,
-                    'datetime': datetime.datetime,
+                    'dt': dt.datetime,
                     'production': {},
                     'source': 'coes.org.pe'
                 })


Test patch: