Commit 15bceff00febc860db45c15bb819c60ef38806a2

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

Merge branch 'UnitTesting' into dev

DEV.md
... ... @@ -5,4 +5,15 @@
5 5 * go to config.yml
6 6 1. add new entry to 'inputs' section
7 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 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 @@
  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 @@
  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 11 import urllib.request as urllib_request
12 12 import requests
13 13 import re # regex
14   -import numpy
  14 +import numpy as np
  15 +import sys
15 16  
16 17 from csv import writer as csv_writer, DictWriter as csv_dict_writer
17 18 from math import sqrt, isnan
... ... @@ -79,7 +80,6 @@ logHandler.setFormatter(logging.Formatter(
79 80 ))
80 81 log.addHandler(logHandler)
81 82  
82   -
83 83 # HARDCODED CONFIGURATION #####################################################
84 84  
85 85 ASTRONOMICAL_UNIT_IN_KM = 1.496e8
... ... @@ -333,6 +333,40 @@ def datetime_from_list(time_list):
333 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 371 def get_local_filename(url):
338 372 """
... ... @@ -483,10 +517,6 @@ ORDER BY time_min, granule_gid
483 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 521 return rows
492 522 except Exception as e:
... ... @@ -654,38 +684,6 @@ def get_data_for_target(target_config, input_slug,
654 684 # abort(500, "Invalid orbit configuration for '%s' : %s"
655 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 688 precision = "%Y-%m-%dT%H" # model and orbits times are only equal-ish
691 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 912 if isfile(local_csv_file):
915 913 log.warning("Removing failed CSV '%s'..." % local_csv_file)
916 914 removefile(local_csv_file)
917   - # pprint(e)
918 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 1520  
1524 1521 for param in params:
1525 1522 k = "%s_%s" % (target_slug, param)
1526   - # print("PARAM %s" % k)
1527 1523 values = []
1528 1524 i = available_params.index(param)
1529 1525 has_nones = False
... ... @@ -1603,7 +1599,6 @@ def download_auroral_catalog_csv(target):
1603 1599 # Filter the emissions
1604 1600 def _keep_emission(emission):
1605 1601 ok = thumbnail_url_filter.match(emission['thumbnail_url'])
1606   - # print("ok", ok, emission['thumbnail_url'])
1607 1602 return bool(ok)
1608 1603  
1609 1604 emissions = [e for e in emissions if _keep_emission(e)]
... ...