Commit 15bceff00febc860db45c15bb819c60ef38806a2

Authored by hitier
2 parents c074c029 aca1013e
Exists in rhitier-dev and in 1 other branch DEV

Merge branch 'UnitTesting' into dev

@@ -5,4 +5,15 @@ @@ -5,4 +5,15 @@
5 * go to config.yml 5 * go to config.yml
6 1. add new entry to 'inputs' section 6 1. add new entry to 'inputs' section
7 2. inside each target, add entry in the 'models' sub-section 7 2. inside each target, add entry in the 'models' sub-section
8 -* possible to set default input in the defaults.input_slug parameter  
9 \ No newline at end of file 8 \ No newline at end of file
  9 +* possible to set default input in the defaults.input_slug parameter
  10 +
  11 +## Testing
  12 +
  13 + # basic
  14 + PYTHONPATH=. pytest
  15 +
  16 + # debug messages enabled
  17 + DEBUG=true PYTHONPATH=. pytest
  18 +
  19 + # output to stderr activated
  20 + DEBUG=true PYTHONPATH=. pytest -rP
pytest.ini 0 → 100644
@@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
  1 +[pytest]
  2 +testpaths = tests
  3 +python_files = *Tests.py
  4 +log_level = INFO
  5 +log_cli = True
  6 +console_output_style = classic
tests/BackendTests.py 0 → 100644
@@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
  1 +import gzip
  2 +import os.path
  3 +import sys
  4 +import unittest
  5 +from datetime import datetime as ddatetime, datetime
  6 +from pprint import pprint
  7 +
  8 +import numpy.ma.core
  9 +from netCDF4 import Dataset
  10 +
  11 +from web.run import retrieve_amda_netcdf, get_data_for_target, _sta_sto, _read_var, default_nc_keys
  12 +
  13 +FILE_DATE_FMT = "%Y-%m-%d %H:%M:%S"
  14 +
  15 +
  16 +class BaseTestCase(unittest.TestCase):
  17 + def setUp(self):
  18 + # initialise app
  19 + # self.app = create_app(TestConfig)
  20 +
  21 + # update flask context
  22 + # self.app_context = self.app.app_context()
  23 + # self.app_context.push()
  24 + pass
  25 +
  26 + def tearDown(self):
  27 + # if os.path.isfile(self.db_path):
  28 + # os.remove(self.db_path)
  29 + # self.app_context.pop()
  30 + pass
  31 +
  32 + def test_always_true(self):
  33 + self.assertTrue(True)
  34 +
  35 +
  36 +class AmdaTestCase(BaseTestCase):
  37 +
  38 + def test_amda_retrieve(self):
  39 + started_at = ddatetime.strptime("2021-12-17 00:00:00", FILE_DATE_FMT)
  40 + stopped_at = ddatetime.strptime("2021-12-18 00:00:00", FILE_DATE_FMT)
  41 + amda_list = retrieve_amda_netcdf("earth", "omni_hour_all", started_at, stopped_at)
  42 + self.assertEqual(1, len(amda_list))
  43 + self.assertIn('amda-irap-omp-eu-ddservice-base-data-omni-hour-omni202107010-nc', amda_list[0])
  44 +
  45 + def test_get_data_for_target(self):
  46 + target_config = {'type': 'planet', 'slug': 'earth', 'name': 'Earth', 'title': 'Earth', 'orbit': {'models': []},
  47 + 'models': {
  48 + 'om': [{'slug': 'omni_hour_all', 'parameters': {'pdyn': 'RamP'}},
  49 + {'slug': 'ace_swepam_real_1h',
  50 + 'parameters': {'dens': 'Dens', 'vtot': 'Vel', 'temp': 'Temp'}}],
  51 + 'sa': [{'slug': 'omni_hour_all', 'parameters': {'pdyn': 'RamP'}},
  52 + {'slug': 'ace_swepam_real_1h',
  53 + 'parameters': {'dens': 'Dens', 'vtot': 'Vel', 'temp': 'Temp'}}],
  54 + 'sb': [{'slug': 'omni_hour_all', 'parameters': {'pdyn': 'RamP'}},
  55 + {'slug': 'ace_swepam_real_1h',
  56 + 'parameters': {'dens': 'Dens', 'vtot': 'Vel', 'temp': 'Temp'}}]},
  57 + 'locked': False, 'default': True, 'catalog_layers': {'cmecatalogs': []}}
  58 +
  59 + started_at = ddatetime.strptime("2021-12-17 00:00:00", FILE_DATE_FMT)
  60 + stopped_at = ddatetime.strptime("2021-12-18 00:00:00", FILE_DATE_FMT)
  61 + model = {'parameters': {'dens': 'Dens', 'temp': 'Temp', 'vtot': 'Vel'}, 'slug': 'ace_swepam_real_1h'}
  62 + s0, s1 = _sta_sto(model, started_at, stopped_at)
  63 + all_data = get_data_for_target(target_config, 'om', s0, s1)
  64 + self.assertEqual(25, len(all_data))
  65 +
  66 + def test_sta_sto(self):
  67 + started_at = ddatetime.strptime("2021-12-17 00:00:00", FILE_DATE_FMT)
  68 + stopped_at = ddatetime.strptime("2021-12-18 00:00:00", FILE_DATE_FMT)
  69 + model = {'parameters': {'dens': 'Dens', 'temp': 'Temp', 'vtot': 'Vel'}, 'slug': 'ace_swepam_real_1h'}
  70 +
  71 + s0, s1 = _sta_sto(model, started_at, stopped_at)
  72 +
  73 + self.assertIsInstance(s0, datetime)
  74 + self.assertIsInstance(s1, datetime)
  75 +
  76 + def test_var_read_nc_float32(self):
  77 + SCRIPT_PATH = os.path.dirname(__file__)
  78 + PROJECT_DIR = os.path.abspath(os.path.join(SCRIPT_PATH, os.pardir))
  79 + local_netc_file = os.path.join(PROJECT_DIR,'tests-resources', 'amda-irap-omp-eu-ddservice-base-data-omni-hour-omni202107010-nc')
  80 + cdf_handle = Dataset(local_netc_file, "r", format="NETCDF4")
  81 + nc_keys = default_nc_keys.copy()
  82 + data_v = _read_var(cdf_handle, nc_keys, 'vtot')
  83 + self.assertIsInstance(data_v[0], numpy.float32)
tests/FrontEndTests.py
1 -import urllib  
2 -  
3 -from selenium import webdriver  
4 -from flask_testing import LiveServerTestCase  
5 -from tryflask import app  
6 -  
7 -_SERVER_PORT = 8943  
8 -_ROOT_URL = "http://localhost:{}".format(_SERVER_PORT)  
9 -  
10 -  
11 -class BaseTestCase(LiveServerTestCase):  
12 - def create_app(self):  
13 - # # remove logging lines on test output  
14 - import logging  
15 - log = logging.getLogger('werkzeug')  
16 - log.setLevel(logging.INFO)  
17 - log.disabled = True  
18 - # os.environ['WERKZEUG_RUN_MAIN'] = 'true'  
19 -  
20 - # pass in test configurations  
21 - app.config.update(  
22 - # Change the port that the liveserver listens on as we dont want to conflict with running:5000  
23 - LIVESERVER_PORT=_SERVER_PORT  
24 - )  
25 -  
26 - self.app_context = app.app_context()  
27 - self.app_context.push()  
28 - return app  
29 -  
30 - def setUp(self):  
31 - # os.environ["PYTHONTRACEMALLOC"] = '1'  
32 -  
33 - self.driver = self.create_chrome_driver()  
34 -  
35 - def tearDown(self):  
36 - self.app_context.pop()  
37 - self.driver.close()  
38 - self.driver.quit()  
39 -  
40 - def create_chrome_driver(self):  
41 - """  
42 - Create then return the chrome driver.  
43 -  
44 - :return: the chrome driver.  
45 - """  
46 - from selenium.webdriver.chrome.options import Options  
47 - options = Options()  
48 - options.add_argument('--headless')  
49 -  
50 - return webdriver.Chrome(options=options)  
51 -  
52 -  
53 -class AccessTestCase(BaseTestCase):  
54 - def test_ping(self):  
55 - self.assertEqual(_ROOT_URL, self.get_server_url())  
56 -  
57 - def test_selenium_access(self):  
58 - # open browser on servers adress  
59 - self.driver.get(self.get_server_url())  
60 - # L'adresse dans l'url doit être celle que l'on attend.  
61 - self.assertEqual(_ROOT_URL+'/', self.driver.current_url)  
62 -  
63 - def test_server_is_up_and_running(self):  
64 - response = urllib.request.urlopen(self.get_server_url())  
65 - self.assertEqual(200, response.code) 1 +# import urllib
  2 +#
  3 +# from selenium import webdriver
  4 +# from flask_testing import LiveServerTestCase
  5 +#
  6 +# _SERVER_PORT = 8943
  7 +# _ROOT_URL = "http://localhost:{}".format(_SERVER_PORT)
  8 +#
  9 +#
  10 +# class BaseTestCase(LiveServerTestCase):
  11 +# def create_app(self):
  12 +# # # remove logging lines on test output
  13 +# import logging
  14 +# log = logging.getLogger('werkzeug')
  15 +# log.setLevel(logging.INFO)
  16 +# log.disabled = True
  17 +# # os.environ['WERKZEUG_RUN_MAIN'] = 'true'
  18 +#
  19 +# # pass in test configurations
  20 +# app.config.update(
  21 +# # Change the port that the liveserver listens on as we dont want to conflict with running:5000
  22 +# LIVESERVER_PORT=_SERVER_PORT
  23 +# )
  24 +#
  25 +# self.app_context = app.app_context()
  26 +# self.app_context.push()
  27 +# return app
  28 +#
  29 +# def setUp(self):
  30 +# # os.environ["PYTHONTRACEMALLOC"] = '1'
  31 +#
  32 +# self.driver = self.create_chrome_driver()
  33 +#
  34 +# def tearDown(self):
  35 +# self.app_context.pop()
  36 +# self.driver.close()
  37 +# self.driver.quit()
  38 +#
  39 +# def create_chrome_driver(self):
  40 +# """
  41 +# Create then return the chrome driver.
  42 +#
  43 +# :return: the chrome driver.
  44 +# """
  45 +# from selenium.webdriver.chrome.options import Options
  46 +# options = Options()
  47 +# options.add_argument('--headless')
  48 +#
  49 +# return webdriver.Chrome(options=options)
  50 +#
  51 +#
  52 +# class AccessTestCase(BaseTestCase):
  53 +# def test_ping(self):
  54 +# self.assertEqual(_ROOT_URL, self.get_server_url())
  55 +#
  56 +# def test_selenium_access(self):
  57 +# # open browser on servers adress
  58 +# self.driver.get(self.get_server_url())
  59 +# # L'adresse dans l'url doit être celle que l'on attend.
  60 +# self.assertEqual(_ROOT_URL+'/', self.driver.current_url)
  61 +#
  62 +# def test_server_is_up_and_running(self):
  63 +# response = urllib.request.urlopen(self.get_server_url())
  64 +# self.assertEqual(200, response.code)
web/run.py 100644 → 100755
@@ -11,7 +11,8 @@ import time @@ -11,7 +11,8 @@ import time
11 import urllib.request as urllib_request 11 import urllib.request as urllib_request
12 import requests 12 import requests
13 import re # regex 13 import re # regex
14 -import numpy 14 +import numpy as np
  15 +import sys
15 16
16 from csv import writer as csv_writer, DictWriter as csv_dict_writer 17 from csv import writer as csv_writer, DictWriter as csv_dict_writer
17 from math import sqrt, isnan 18 from math import sqrt, isnan
@@ -79,7 +80,6 @@ logHandler.setFormatter(logging.Formatter( @@ -79,7 +80,6 @@ logHandler.setFormatter(logging.Formatter(
79 )) 80 ))
80 log.addHandler(logHandler) 81 log.addHandler(logHandler)
81 82
82 -  
83 # HARDCODED CONFIGURATION ##################################################### 83 # HARDCODED CONFIGURATION #####################################################
84 84
85 ASTRONOMICAL_UNIT_IN_KM = 1.496e8 85 ASTRONOMICAL_UNIT_IN_KM = 1.496e8
@@ -333,6 +333,40 @@ def datetime_from_list(time_list): @@ -333,6 +333,40 @@ def datetime_from_list(time_list):
333 "%Y%j%H%M%S%f" 333 "%Y%j%H%M%S%f"
334 ) 334 )
335 335
  336 +# Override these using the model configuration in config.yml
  337 +default_nc_keys = {
  338 + 'hee': 'HEE',
  339 + 'vtot': 'V',
  340 + 'magn': 'B',
  341 + 'temp': 'T',
  342 + 'dens': 'N',
  343 + 'pdyn': 'P_dyn',
  344 + 'atse': 'Delta_angle',
  345 +}
  346 +
  347 +def _sta_sto(_cnf, _sta, _sto):
  348 + if 'started_at' in _cnf:
  349 + _s0 = datetime.datetime.strptime(_cnf['started_at'], FILE_DATE_FMT)
  350 + _s0 = max(_s0, _sta)
  351 + else:
  352 + _s0 = _sta
  353 + if 'stopped_at' in _cnf:
  354 + _s1 = datetime.datetime.strptime(_cnf['stopped_at'], FILE_DATE_FMT)
  355 + _s1 = min(_s1, _sto)
  356 + else:
  357 + _s1 = _sto
  358 + return _s0, _s1
  359 +
  360 +
  361 +def _read_var(_nc, _keys, _key, mandatory=False):
  362 + try:
  363 + return _nc.variables[_keys[_key]][:]
  364 + except KeyError:
  365 + pass
  366 + if mandatory:
  367 + raise Exception("No variable '%s' found in NetCDF." % _keys[_key])
  368 + return [None] * len(_nc.variables['Time']) # slow -- use numpy!
  369 +
336 370
337 def get_local_filename(url): 371 def get_local_filename(url):
338 """ 372 """
@@ -483,10 +517,6 @@ ORDER BY time_min, granule_gid @@ -483,10 +517,6 @@ ORDER BY time_min, granule_gid
483 'external_link': row[3].text, 517 'external_link': row[3].text,
484 }) 518 })
485 519
486 - # print("Auroral emission query")  
487 - # print(query)  
488 - # print("Auroral emission rows")  
489 - # print(rows)  
490 520
491 return rows 521 return rows
492 except Exception as e: 522 except Exception as e:
@@ -654,38 +684,6 @@ def get_data_for_target(target_config, input_slug, @@ -654,38 +684,6 @@ def get_data_for_target(target_config, input_slug,
654 # abort(500, "Invalid orbit configuration for '%s' : %s" 684 # abort(500, "Invalid orbit configuration for '%s' : %s"
655 # % (target_config['slug'], str(e))) 685 # % (target_config['slug'], str(e)))
656 686
657 - def _sta_sto(_cnf, _sta, _sto):  
658 - if 'started_at' in _cnf:  
659 - _s0 = datetime.datetime.strptime(_cnf['started_at'], FILE_DATE_FMT)  
660 - _s0 = max(_s0, _sta)  
661 - else:  
662 - _s0 = _sta  
663 - if 'stopped_at' in _cnf:  
664 - _s1 = datetime.datetime.strptime(_cnf['stopped_at'], FILE_DATE_FMT)  
665 - _s1 = min(_s1, _sto)  
666 - else:  
667 - _s1 = _sto  
668 - return _s0, _s1  
669 -  
670 - def _read_var(_nc, _keys, _key, mandatory=False):  
671 - try:  
672 - return _nc.variables[_keys[_key]]  
673 - except KeyError:  
674 - pass  
675 - if mandatory:  
676 - raise Exception("No variable '%s' found in NetCDF." % _keys[_key])  
677 - return [None] * len(_nc.variables['Time']) # slow -- use numpy!  
678 -  
679 - # Override these using the model configuration in config.yml  
680 - default_nc_keys = {  
681 - 'hee': 'HEE',  
682 - 'vtot': 'V',  
683 - 'magn': 'B',  
684 - 'temp': 'T',  
685 - 'dens': 'N',  
686 - 'pdyn': 'P_dyn',  
687 - 'atse': 'Delta_angle',  
688 - }  
689 687
690 precision = "%Y-%m-%dT%H" # model and orbits times are only equal-ish 688 precision = "%Y-%m-%dT%H" # model and orbits times are only equal-ish
691 orbit_data = {} # keys are datetime as str, values arrays of XY 689 orbit_data = {} # keys are datetime as str, values arrays of XY
@@ -914,7 +912,6 @@ def generate_csv_file_if_needed(target_slug, input_slug, @@ -914,7 +912,6 @@ def generate_csv_file_if_needed(target_slug, input_slug,
914 if isfile(local_csv_file): 912 if isfile(local_csv_file):
915 log.warning("Removing failed CSV '%s'..." % local_csv_file) 913 log.warning("Removing failed CSV '%s'..." % local_csv_file)
916 removefile(local_csv_file) 914 removefile(local_csv_file)
917 - # pprint(e)  
918 abort(500, "Failed creating CSV '%s' : %s" % (filename, e)) 915 abort(500, "Failed creating CSV '%s' : %s" % (filename, e))
919 916
920 917
@@ -1523,7 +1520,6 @@ def download_targets_cdf(targets, inp, started_at, stopped_at): @@ -1523,7 +1520,6 @@ def download_targets_cdf(targets, inp, started_at, stopped_at):
1523 1520
1524 for param in params: 1521 for param in params:
1525 k = "%s_%s" % (target_slug, param) 1522 k = "%s_%s" % (target_slug, param)
1526 - # print("PARAM %s" % k)  
1527 values = [] 1523 values = []
1528 i = available_params.index(param) 1524 i = available_params.index(param)
1529 has_nones = False 1525 has_nones = False
@@ -1603,7 +1599,6 @@ def download_auroral_catalog_csv(target): @@ -1603,7 +1599,6 @@ def download_auroral_catalog_csv(target):
1603 # Filter the emissions 1599 # Filter the emissions
1604 def _keep_emission(emission): 1600 def _keep_emission(emission):
1605 ok = thumbnail_url_filter.match(emission['thumbnail_url']) 1601 ok = thumbnail_url_filter.match(emission['thumbnail_url'])
1606 - # print("ok", ok, emission['thumbnail_url'])  
1607 return bool(ok) 1602 return bool(ok)
1608 1603
1609 emissions = [e for e in emissions if _keep_emission(e)] 1604 emissions = [e for e in emissions if _keep_emission(e)]