We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Brazil solar data out of range
```
Traceback (most recent call last):
  File "feeder_electricity.py", line 148, in fetch_production
    objs = parser(country_code, session, logger=public_logger)
  File "/home/electricitymap/parsers/BR.py", line 131, in fetch_production
    generation = production_processor(gd, zone_key)
  File "/home/electricitymap/parsers/BR.py", line 81, in production_processor
    raise ValueError('the solar value is out of range')
ValueError: the solar value is out of range
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `parsers/BR.py`
Content:
```
1 #!/usr/bin/env python3
2 
3 import arrow
4 from collections import defaultdict
5 import requests
6 
7 url = 'http://tr.ons.org.br/Content/GetBalancoEnergetico/null'
8 
9 generation_mapping = {
10                       u'nuclear': 'nuclear',
11                       u'eolica': 'wind',
12                       u'termica': 'unknown',
13                       u'solar': 'solar',
14                       'hydro': 'hydro'
15                       }
16 
17 regions = {
18            'BR-NE': u'nordeste',
19            'BR-N': u'norte',
20            'BR-CS': u'sudesteECentroOeste',
21            'BR-S': u'sul'
22            }
23 
24 region_exchanges = {
25                     'BR-CS->BR-S': "sul_sudeste",
26                     'BR-CS->BR-NE': "sudeste_nordeste",
27                     'BR-CS->BR-N': "sudeste_norteFic",
28                     'BR-N->BR-NE': "norteFic_nordeste"
29                     }
30 
31 
32 region_exchanges_directions = {
33                     'BR-CS->BR-S': -1,
34                     'BR-CS->BR-NE': 1,
35                     'BR-CS->BR-N': 1,
36                     'BR-N->BR-NE': 1
37                     }
38 
39 countries_exchange = {
40     'UY': {
41         'name': u'uruguai',
42         'flow': 1
43     },
44     'AR': {
45         'name': u'argentina',
46         'flow': -1
47     },
48     'PY': {
49         'name': u'paraguai',
50         'flow': -1
51     }
52 }
53 
54 
55 def get_data(session, logger):
56     """Requests generation data in json format."""
57 
58     s = session or requests.session()
59     json_data = s.get(url).json()
60     return json_data
61 
62 
63 def production_processor(json_data, zone_key):
64     """
65     Extracts data timestamp and sums regional data into totals by key.
66     Maps keys to type and returns a tuple.
67     """
68 
69     dt = arrow.get(json_data['Data'])
70     totals = defaultdict(lambda: 0.0)
71 
72     region = regions[zone_key]
73     breakdown = json_data[region][u'geracao']
74     for generation, val in breakdown.items():
75         # tolerance range
76         if -1 <= totals['solar'] < 0:
77             totals['solar'] = 0.0
78 
79         # not tolerance range
80         if totals['solar'] < -1:
81             raise ValueError('the solar value is out of range')
82 
83         totals[generation] += val
84 
85     # BR_CS contains the Itaipu Dam.
86     # We merge the hydro keys into one, then remove unnecessary keys.
87     totals['hydro'] = totals.get(u'hidraulica', 0.0) + totals.get(u'itaipu50HzBrasil', 0.0) + totals.get(u'itaipu60Hz', 0.0)
88     entriesToRemove = (u'hidraulica', u'itaipu50HzBrasil', u'itaipu60Hz', u'total')
89     for k in entriesToRemove:
90         totals.pop(k, None)
91 
92     mapped_totals = {generation_mapping.get(name, 'unknown'): val for name, val
93                      in totals.items()}
94 
95     return dt, mapped_totals
96 
97 
98 def fetch_production(zone_key, session=None, target_datetime=None, logger=None):
99     """
100     Requests the last known production mix (in MW) of a given country
101     Arguments:
102     zone_key (optional) -- used in case a parser is able to fetch multiple countries
103     session (optional)      -- request session passed in order to re-use an existing session
104     Return:
105     A dictionary in the form:
106     {
107       'zoneKey': 'FR',
108       'datetime': '2017-01-01T00:00:00Z',
109       'production': {
110           'biomass': 0.0,
111           'coal': 0.0,
112           'gas': 0.0,
113           'hydro': 0.0,
114           'nuclear': null,
115           'oil': 0.0,
116           'solar': 0.0,
117           'wind': 0.0,
118           'geothermal': 0.0,
119           'unknown': 0.0
120       },
121       'storage': {
122           'hydro': -10.0,
123       },
124       'source': 'mysource.com'
125     }
126     """
127     if target_datetime:
128         raise NotImplementedError('This parser is not yet able to parse past dates')
129 
130     gd = get_data(session, logger)
131     generation = production_processor(gd, zone_key)
132 
133     datapoint = {
134       'zoneKey': zone_key,
135       'datetime': generation[0].datetime,
136       'production': generation[1],
137       'storage': {
138           'hydro': None,
139       },
140       'source': 'ons.org.br'
141     }
142 
143     return datapoint
144 
145 
146 def fetch_exchange(zone_key1, zone_key2, session=None, target_datetime=None, logger=None):
147     """Requests the last known power exchange (in MW) between two regions
148     Arguments:
149     zone_key1           -- the first country code
150     zone_key2           -- the second country code; order of the two codes in params doesn't matter
151     session (optional)      -- request session passed in order to re-use an existing session
152     Return:
153     A dictionary in the form:
154     {
155       'sortedZoneKeys': 'DK->NO',
156       'datetime': '2017-01-01T00:00:00Z',
157       'netFlow': 0.0,
158       'source': 'mysource.com'
159     }
160     where net flow is from DK into NO
161     """
162     if target_datetime:
163         raise NotImplementedError('This parser is not yet able to parse past dates')
164 
165     gd = get_data(session, logger)
166 
167     if zone_key1 in countries_exchange.keys():
168         country_exchange = countries_exchange[zone_key1]
169 
170     if zone_key2 in countries_exchange.keys():
171         country_exchange = countries_exchange[zone_key2]
172 
173     data = {
174         'datetime': arrow.get(gd['Data']).datetime,
175         'sortedZoneKeys': '->'.join(sorted([zone_key1, zone_key2])),
176         'netFlow': gd['internacional'][country_exchange['name']] * country_exchange['flow'],
177         'source': 'ons.org.br'
178     }
179 
180     return data
181 
182 
183 def fetch_region_exchange(region1, region2, session=None, target_datetime=None, logger=None):
184     """
185     Requests the last known power exchange (in MW) between two Brazilian regions.
186     Arguments:
187     region1           -- the first region
188     region2           -- the second region; order of the two codes in params doesn't matter
189     session (optional)      -- request session passed in order to re-use an existing session
190     Return:
191     A dictionary in the form:
192     {
193       'sortedZoneKeys': 'DK->NO',
194       'datetime': '2017-01-01T00:00:00Z',
195       'netFlow': 0.0,
196       'source': 'mysource.com'
197     }
198     where net flow is from DK into NO
199     """
200     if target_datetime:
201         raise NotImplementedError('This parser is not yet able to parse past dates')
202 
203     gd = get_data(session, logger)
204     dt = arrow.get(gd['Data']).datetime
205     scc = '->'.join(sorted([region1, region2]))
206 
207     exchange = region_exchanges[scc]
208     nf = gd['intercambio'][exchange] * region_exchanges_directions[scc]
209 
210     data = {
211         'datetime': dt,
212         'sortedZoneKeys': scc,
213         'netFlow': nf,
214         'source': 'ons.org.br'
215     }
216 
217     return data
218 
219 
220 if __name__ == '__main__':
221     """Main method, never used by the Electricity Map backend, but handy for testing."""
222 
223     print('fetch_production(BR-NE) ->')
224     print(fetch_production('BR-NE'))
225 
226     print('fetch_production(BR-N) ->')
227     print(fetch_production('BR-N'))
228 
229     print('fetch_production(BR-CS) ->')
230     print(fetch_production('BR-CS'))
231 
232     print('fetch_production(BR-S) ->')
233     print(fetch_production('BR-S'))
234 
235     print('fetch_exchange(BR-S, UY) ->')
236     print(fetch_exchange('BR-S', 'UY'))
237 
238     print('fetch_exchange(BR-S, AR) ->')
239     print(fetch_exchange('BR-S', 'AR'))
240 
241     print('fetch_region_exchange(BR-CS->BR-S)')
242     print(fetch_region_exchange('BR-CS', 'BR-S'))
243 
244     print('fetch_region_exchange(BR-CS->BR-NE)')
245     print(fetch_region_exchange('BR-CS', 'BR-NE'))
246 
247     print('fetch_region_exchange(BR-CS->BR-N)')
248     print(fetch_region_exchange('BR-CS', 'BR-N'))
249 
250     print('fetch_region_exchange(BR-N->BR-NE)')
251     print(fetch_region_exchange('BR-N', 'BR-NE'))
252
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/parsers/BR.py b/parsers/BR.py
--- a/parsers/BR.py
+++ b/parsers/BR.py
@@ -1,9 +1,13 @@
 #!/usr/bin/env python3
 
-import arrow
 from collections import defaultdict
+
+import arrow
 import requests
 
+from .lib.validation import validate
+
+
 url = 'http://tr.ons.org.br/Content/GetBalancoEnergetico/null'
 
 generation_mapping = {
@@ -72,14 +76,6 @@
     region = regions[zone_key]
     breakdown = json_data[region][u'geracao']
     for generation, val in breakdown.items():
-        # tolerance range
-        if -1 <= totals['solar'] < 0:
-            totals['solar'] = 0.0
-
-        # not tolerance range
-        if totals['solar'] < -1:
-            raise ValueError('the solar value is out of range')
-
         totals[generation] += val
 
     # BR_CS contains the Itaipu Dam.
@@ -140,6 +136,9 @@
       'source': 'ons.org.br'
     }
 
+    datapoint = validate(datapoint, logger,
+                         remove_negative=True, required=['hydro'], floor=1000)
+
     return datapoint

{"golden_diff": "diff --git a/parsers/BR.py b/parsers/BR.py\n--- a/parsers/BR.py\n+++ b/parsers/BR.py\n@@ -1,9 +1,13 @@\n #!/usr/bin/env python3\n \n-import arrow\n from collections import defaultdict\n+\n+import arrow\n import requests\n \n+from .lib.validation import validate\n+\n+\n url = 'http://tr.ons.org.br/Content/GetBalancoEnergetico/null'\n \n generation_mapping = {\n@@ -72,14 +76,6 @@\n     region = regions[zone_key]\n     breakdown = json_data[region][u'geracao']\n     for generation, val in breakdown.items():\n-        # tolerance range\n-        if -1 <= totals['solar'] < 0:\n-            totals['solar'] = 0.0\n-\n-        # not tolerance range\n-        if totals['solar'] < -1:\n-            raise ValueError('the solar value is out of range')\n-\n         totals[generation] += val\n \n     # BR_CS contains the Itaipu Dam.\n@@ -140,6 +136,9 @@\n       'source': 'ons.org.br'\n     }\n \n+    datapoint = validate(datapoint, logger,\n+                         remove_negative=True, required=['hydro'], floor=1000)\n+\n     return datapoint\n", "issue": "Brazil solar data out of range\n```\r\nTraceback (most recent call last):\r\n  File \"feeder_electricity.py\", line 148, in fetch_production\r\n    objs = parser(country_code, session, logger=public_logger)\r\n  File \"/home/electricitymap/parsers/BR.py\", line 131, in fetch_production\r\n    generation = production_processor(gd, zone_key)\r\n  File \"/home/electricitymap/parsers/BR.py\", line 81, in production_processor\r\n    raise ValueError('the solar value is out of range')\r\nValueError: the solar value is out of range\r\n```\n", "before_files": [{"content": "#!/usr/bin/env python3\n\nimport arrow\nfrom collections import defaultdict\nimport requests\n\nurl = 'http://tr.ons.org.br/Content/GetBalancoEnergetico/null'\n\ngeneration_mapping = {\n                      u'nuclear': 'nuclear',\n                      u'eolica': 'wind',\n                      u'termica': 'unknown',\n                      u'solar': 'solar',\n                      'hydro': 'hydro'\n                      }\n\nregions = {\n           'BR-NE': u'nordeste',\n           'BR-N': u'norte',\n           'BR-CS': u'sudesteECentroOeste',\n           'BR-S': u'sul'\n           }\n\nregion_exchanges = {\n                    'BR-CS->BR-S': \"sul_sudeste\",\n                    'BR-CS->BR-NE': \"sudeste_nordeste\",\n                    'BR-CS->BR-N': \"sudeste_norteFic\",\n                    'BR-N->BR-NE': \"norteFic_nordeste\"\n                    }\n\n\nregion_exchanges_directions = {\n                    'BR-CS->BR-S': -1,\n                    'BR-CS->BR-NE': 1,\n                    'BR-CS->BR-N': 1,\n                    'BR-N->BR-NE': 1\n                    }\n\ncountries_exchange = {\n    'UY': {\n        'name': u'uruguai',\n        'flow': 1\n    },\n    'AR': {\n        'name': u'argentina',\n        'flow': -1\n    },\n    'PY': {\n        'name': u'paraguai',\n        'flow': -1\n    }\n}\n\n\ndef get_data(session, logger):\n    \"\"\"Requests generation data in json format.\"\"\"\n\n    s = session or requests.session()\n    json_data = s.get(url).json()\n    return json_data\n\n\ndef production_processor(json_data, zone_key):\n    \"\"\"\n    Extracts data timestamp and sums regional data into totals by key.\n    Maps keys to type and returns a tuple.\n    \"\"\"\n\n    dt = arrow.get(json_data['Data'])\n    totals = defaultdict(lambda: 0.0)\n\n    region = regions[zone_key]\n    breakdown = json_data[region][u'geracao']\n    for generation, val in breakdown.items():\n        # tolerance range\n        if -1 <= totals['solar'] < 0:\n            totals['solar'] = 0.0\n\n        # not tolerance range\n        if totals['solar'] < -1:\n            raise ValueError('the solar value is out of range')\n\n        totals[generation] += val\n\n    # BR_CS contains the Itaipu Dam.\n    # We merge the hydro keys into one, then remove unnecessary keys.\n    totals['hydro'] = totals.get(u'hidraulica', 0.0) + totals.get(u'itaipu50HzBrasil', 0.0) + totals.get(u'itaipu60Hz', 0.0)\n    entriesToRemove = (u'hidraulica', u'itaipu50HzBrasil', u'itaipu60Hz', u'total')\n    for k in entriesToRemove:\n        totals.pop(k, None)\n\n    mapped_totals = {generation_mapping.get(name, 'unknown'): val for name, val\n                     in totals.items()}\n\n    return dt, mapped_totals\n\n\ndef fetch_production(zone_key, session=None, target_datetime=None, logger=None):\n    \"\"\"\n    Requests the last known production mix (in MW) of a given country\n    Arguments:\n    zone_key (optional) -- used in case a parser is able to fetch multiple countries\n    session (optional)      -- request session passed in order to re-use an existing session\n    Return:\n    A dictionary in the form:\n    {\n      'zoneKey': 'FR',\n      'datetime': '2017-01-01T00:00:00Z',\n      'production': {\n          'biomass': 0.0,\n          'coal': 0.0,\n          'gas': 0.0,\n          'hydro': 0.0,\n          'nuclear': null,\n          'oil': 0.0,\n          'solar': 0.0,\n          'wind': 0.0,\n          'geothermal': 0.0,\n          'unknown': 0.0\n      },\n      'storage': {\n          'hydro': -10.0,\n      },\n      'source': 'mysource.com'\n    }\n    \"\"\"\n    if target_datetime:\n        raise NotImplementedError('This parser is not yet able to parse past dates')\n\n    gd = get_data(session, logger)\n    generation = production_processor(gd, zone_key)\n\n    datapoint = {\n      'zoneKey': zone_key,\n      'datetime': generation[0].datetime,\n      'production': generation[1],\n      'storage': {\n          'hydro': None,\n      },\n      'source': 'ons.org.br'\n    }\n\n    return datapoint\n\n\ndef fetch_exchange(zone_key1, zone_key2, session=None, target_datetime=None, logger=None):\n    \"\"\"Requests the last known power exchange (in MW) between two regions\n    Arguments:\n    zone_key1           -- the first country code\n    zone_key2           -- the second country code; order of the two codes in params doesn't matter\n    session (optional)      -- request session passed in order to re-use an existing session\n    Return:\n    A dictionary in the form:\n    {\n      'sortedZoneKeys': 'DK->NO',\n      'datetime': '2017-01-01T00:00:00Z',\n      'netFlow': 0.0,\n      'source': 'mysource.com'\n    }\n    where net flow is from DK into NO\n    \"\"\"\n    if target_datetime:\n        raise NotImplementedError('This parser is not yet able to parse past dates')\n\n    gd = get_data(session, logger)\n\n    if zone_key1 in countries_exchange.keys():\n        country_exchange = countries_exchange[zone_key1]\n\n    if zone_key2 in countries_exchange.keys():\n        country_exchange = countries_exchange[zone_key2]\n\n    data = {\n        'datetime': arrow.get(gd['Data']).datetime,\n        'sortedZoneKeys': '->'.join(sorted([zone_key1, zone_key2])),\n        'netFlow': gd['internacional'][country_exchange['name']] * country_exchange['flow'],\n        'source': 'ons.org.br'\n    }\n\n    return data\n\n\ndef fetch_region_exchange(region1, region2, session=None, target_datetime=None, logger=None):\n    \"\"\"\n    Requests the last known power exchange (in MW) between two Brazilian regions.\n    Arguments:\n    region1           -- the first region\n    region2           -- the second region; order of the two codes in params doesn't matter\n    session (optional)      -- request session passed in order to re-use an existing session\n    Return:\n    A dictionary in the form:\n    {\n      'sortedZoneKeys': 'DK->NO',\n      'datetime': '2017-01-01T00:00:00Z',\n      'netFlow': 0.0,\n      'source': 'mysource.com'\n    }\n    where net flow is from DK into NO\n    \"\"\"\n    if target_datetime:\n        raise NotImplementedError('This parser is not yet able to parse past dates')\n\n    gd = get_data(session, logger)\n    dt = arrow.get(gd['Data']).datetime\n    scc = '->'.join(sorted([region1, region2]))\n\n    exchange = region_exchanges[scc]\n    nf = gd['intercambio'][exchange] * region_exchanges_directions[scc]\n\n    data = {\n        'datetime': dt,\n        'sortedZoneKeys': scc,\n        'netFlow': nf,\n        'source': 'ons.org.br'\n    }\n\n    return data\n\n\nif __name__ == '__main__':\n    \"\"\"Main method, never used by the Electricity Map backend, but handy for testing.\"\"\"\n\n    print('fetch_production(BR-NE) ->')\n    print(fetch_production('BR-NE'))\n\n    print('fetch_production(BR-N) ->')\n    print(fetch_production('BR-N'))\n\n    print('fetch_production(BR-CS) ->')\n    print(fetch_production('BR-CS'))\n\n    print('fetch_production(BR-S) ->')\n    print(fetch_production('BR-S'))\n\n    print('fetch_exchange(BR-S, UY) ->')\n    print(fetch_exchange('BR-S', 'UY'))\n\n    print('fetch_exchange(BR-S, AR) ->')\n    print(fetch_exchange('BR-S', 'AR'))\n\n    print('fetch_region_exchange(BR-CS->BR-S)')\n    print(fetch_region_exchange('BR-CS', 'BR-S'))\n\n    print('fetch_region_exchange(BR-CS->BR-NE)')\n    print(fetch_region_exchange('BR-CS', 'BR-NE'))\n\n    print('fetch_region_exchange(BR-CS->BR-N)')\n    print(fetch_region_exchange('BR-CS', 'BR-N'))\n\n    print('fetch_region_exchange(BR-N->BR-NE)')\n    print(fetch_region_exchange('BR-N', 'BR-NE'))\n", "path": "parsers/BR.py"}], "after_files": [{"content": "#!/usr/bin/env python3\n\nfrom collections import defaultdict\n\nimport arrow\nimport requests\n\nfrom .lib.validation import validate\n\n\nurl = 'http://tr.ons.org.br/Content/GetBalancoEnergetico/null'\n\ngeneration_mapping = {\n                      u'nuclear': 'nuclear',\n                      u'eolica': 'wind',\n                      u'termica': 'unknown',\n                      u'solar': 'solar',\n                      'hydro': 'hydro'\n                      }\n\nregions = {\n           'BR-NE': u'nordeste',\n           'BR-N': u'norte',\n           'BR-CS': u'sudesteECentroOeste',\n           'BR-S': u'sul'\n           }\n\nregion_exchanges = {\n                    'BR-CS->BR-S': \"sul_sudeste\",\n                    'BR-CS->BR-NE': \"sudeste_nordeste\",\n                    'BR-CS->BR-N': \"sudeste_norteFic\",\n                    'BR-N->BR-NE': \"norteFic_nordeste\"\n                    }\n\n\nregion_exchanges_directions = {\n                    'BR-CS->BR-S': -1,\n                    'BR-CS->BR-NE': 1,\n                    'BR-CS->BR-N': 1,\n                    'BR-N->BR-NE': 1\n                    }\n\ncountries_exchange = {\n    'UY': {\n        'name': u'uruguai',\n        'flow': 1\n    },\n    'AR': {\n        'name': u'argentina',\n        'flow': -1\n    },\n    'PY': {\n        'name': u'paraguai',\n        'flow': -1\n    }\n}\n\n\ndef get_data(session, logger):\n    \"\"\"Requests generation data in json format.\"\"\"\n\n    s = session or requests.session()\n    json_data = s.get(url).json()\n    return json_data\n\n\ndef production_processor(json_data, zone_key):\n    \"\"\"\n    Extracts data timestamp and sums regional data into totals by key.\n    Maps keys to type and returns a tuple.\n    \"\"\"\n\n    dt = arrow.get(json_data['Data'])\n    totals = defaultdict(lambda: 0.0)\n\n    region = regions[zone_key]\n    breakdown = json_data[region][u'geracao']\n    for generation, val in breakdown.items():\n        totals[generation] += val\n\n    # BR_CS contains the Itaipu Dam.\n    # We merge the hydro keys into one, then remove unnecessary keys.\n    totals['hydro'] = totals.get(u'hidraulica', 0.0) + totals.get(u'itaipu50HzBrasil', 0.0) + totals.get(u'itaipu60Hz', 0.0)\n    entriesToRemove = (u'hidraulica', u'itaipu50HzBrasil', u'itaipu60Hz', u'total')\n    for k in entriesToRemove:\n        totals.pop(k, None)\n\n    mapped_totals = {generation_mapping.get(name, 'unknown'): val for name, val\n                     in totals.items()}\n\n    return dt, mapped_totals\n\n\ndef fetch_production(zone_key, session=None, target_datetime=None, logger=None):\n    \"\"\"\n    Requests the last known production mix (in MW) of a given country\n    Arguments:\n    zone_key (optional) -- used in case a parser is able to fetch multiple countries\n    session (optional)      -- request session passed in order to re-use an existing session\n    Return:\n    A dictionary in the form:\n    {\n      'zoneKey': 'FR',\n      'datetime': '2017-01-01T00:00:00Z',\n      'production': {\n          'biomass': 0.0,\n          'coal': 0.0,\n          'gas': 0.0,\n          'hydro': 0.0,\n          'nuclear': null,\n          'oil': 0.0,\n          'solar': 0.0,\n          'wind': 0.0,\n          'geothermal': 0.0,\n          'unknown': 0.0\n      },\n      'storage': {\n          'hydro': -10.0,\n      },\n      'source': 'mysource.com'\n    }\n    \"\"\"\n    if target_datetime:\n        raise NotImplementedError('This parser is not yet able to parse past dates')\n\n    gd = get_data(session, logger)\n    generation = production_processor(gd, zone_key)\n\n    datapoint = {\n      'zoneKey': zone_key,\n      'datetime': generation[0].datetime,\n      'production': generation[1],\n      'storage': {\n          'hydro': None,\n      },\n      'source': 'ons.org.br'\n    }\n\n    datapoint = validate(datapoint, logger,\n                         remove_negative=True, required=['hydro'], floor=1000)\n\n    return datapoint\n\n\ndef fetch_exchange(zone_key1, zone_key2, session=None, target_datetime=None, logger=None):\n    \"\"\"Requests the last known power exchange (in MW) between two regions\n    Arguments:\n    zone_key1           -- the first country code\n    zone_key2           -- the second country code; order of the two codes in params doesn't matter\n    session (optional)      -- request session passed in order to re-use an existing session\n    Return:\n    A dictionary in the form:\n    {\n      'sortedZoneKeys': 'DK->NO',\n      'datetime': '2017-01-01T00:00:00Z',\n      'netFlow': 0.0,\n      'source': 'mysource.com'\n    }\n    where net flow is from DK into NO\n    \"\"\"\n    if target_datetime:\n        raise NotImplementedError('This parser is not yet able to parse past dates')\n\n    gd = get_data(session, logger)\n\n    if zone_key1 in countries_exchange.keys():\n        country_exchange = countries_exchange[zone_key1]\n\n    if zone_key2 in countries_exchange.keys():\n        country_exchange = countries_exchange[zone_key2]\n\n    data = {\n        'datetime': arrow.get(gd['Data']).datetime,\n        'sortedZoneKeys': '->'.join(sorted([zone_key1, zone_key2])),\n        'netFlow': gd['internacional'][country_exchange['name']] * country_exchange['flow'],\n        'source': 'ons.org.br'\n    }\n\n    return data\n\n\ndef fetch_region_exchange(region1, region2, session=None, target_datetime=None, logger=None):\n    \"\"\"\n    Requests the last known power exchange (in MW) between two Brazilian regions.\n    Arguments:\n    region1           -- the first region\n    region2           -- the second region; order of the two codes in params doesn't matter\n    session (optional)      -- request session passed in order to re-use an existing session\n    Return:\n    A dictionary in the form:\n    {\n      'sortedZoneKeys': 'DK->NO',\n      'datetime': '2017-01-01T00:00:00Z',\n      'netFlow': 0.0,\n      'source': 'mysource.com'\n    }\n    where net flow is from DK into NO\n    \"\"\"\n    if target_datetime:\n        raise NotImplementedError('This parser is not yet able to parse past dates')\n\n    gd = get_data(session, logger)\n    dt = arrow.get(gd['Data']).datetime\n    scc = '->'.join(sorted([region1, region2]))\n\n    exchange = region_exchanges[scc]\n    nf = gd['intercambio'][exchange] * region_exchanges_directions[scc]\n\n    data = {\n        'datetime': dt,\n        'sortedZoneKeys': scc,\n        'netFlow': nf,\n        'source': 'ons.org.br'\n    }\n\n    return data\n\n\nif __name__ == '__main__':\n    \"\"\"Main method, never used by the Electricity Map backend, but handy for testing.\"\"\"\n\n    print('fetch_production(BR-NE) ->')\n    print(fetch_production('BR-NE'))\n\n    print('fetch_production(BR-N) ->')\n    print(fetch_production('BR-N'))\n\n    print('fetch_production(BR-CS) ->')\n    print(fetch_production('BR-CS'))\n\n    print('fetch_production(BR-S) ->')\n    print(fetch_production('BR-S'))\n\n    print('fetch_exchange(BR-S, UY) ->')\n    print(fetch_exchange('BR-S', 'UY'))\n\n    print('fetch_exchange(BR-S, AR) ->')\n    print(fetch_exchange('BR-S', 'AR'))\n\n    print('fetch_region_exchange(BR-CS->BR-S)')\n    print(fetch_region_exchange('BR-CS', 'BR-S'))\n\n    print('fetch_region_exchange(BR-CS->BR-NE)')\n    print(fetch_region_exchange('BR-CS', 'BR-NE'))\n\n    print('fetch_region_exchange(BR-CS->BR-N)')\n    print(fetch_region_exchange('BR-CS', 'BR-N'))\n\n    print('fetch_region_exchange(BR-N->BR-NE)')\n    print(fetch_region_exchange('BR-N', 'BR-NE'))\n", "path": "parsers/BR.py"}]}