Coverage for middle_layer/common/__init__.py: 72.22%

54 statements  

« prev     ^ index     » next       coverage.py v7.10.5, created at 2026-04-13 06:13 +0000

1# Copyright 2024 Associated Universities, Inc. 

2# 

3# This file is part of Telescope Time Allocation Tools (TTAT). 

4# 

5# TTAT is free software: you can redistribute it and/or modify 

6# it under the terms of the GNU General Public License as published by 

7# the Free Software Foundation, either version 3 of the License, or 

8# any later version. 

9# 

10# TTAT is distributed in the hope that it will be useful, 

11# but WITHOUT ANY WARRANTY; without even the implied warranty of 

12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

13# GNU General Public License for more details. 

14# 

15# You should have received a copy of the GNU General Public License 

16# along with TTAT. If not, see <https://www.gnu.org/licenses/>. 

17# 

18"""Contain utility functions used across middle_layer packages""" 

19import os 

20from pathlib import Path 

21 

22import astropy.units as u 

23import pendulum 

24from astropy.coordinates import Angle 

25from astropy.units import Quantity, Unit 

26 

27ZERO_SECONDS = 0 * u.s 

28 

29 

30def stringify_declination(declination: Quantity) -> str: 

31 return Angle(declination).to_string(unit=Unit("degree"), sep=":") 

32 

33 

34def stringify_right_ascension(right_ascension: Quantity) -> str: 

35 return Angle(right_ascension).to_string(unit=Unit("degree"), sep=":") 

36 

37 

38def sum_quantities(quants: list[Quantity]) -> Quantity: 

39 """Preserve units even if sum() returns 0""" 

40 return sum(quants, start=ZERO_SECONDS) 

41 

42 

43def strtobool(a_string: str) -> bool: 

44 if a_string.lower() == "true": 

45 return True 

46 elif a_string.lower() == "false": 

47 return False 

48 else: 

49 raise ValueError("value must be a boolean. " + a_string) 

50 

51 

52def parse_iso_8601_strings(string: str) -> pendulum.DateTime: 

53 """Parse an ISO 8601 datetime string into a pendulum.DateTime 

54 

55 :param string: String to parse 

56 :return: Parsed DateTime 

57 :raises ValueError: If string is parsed successfully but isn't a DateTime 

58 :raises ParserError: If Pendulum fails to parse string 

59 """ 

60 result = pendulum.parse(string) 

61 if isinstance(result, pendulum.DateTime): 

62 return result 

63 raise ValueError(f"String {string} parsed into {result}, which does not have type pendulum.DateTime") 

64 

65 

66def get_parents_matching(directory: Path, query: str) -> list[Path]: 

67 return [par for par in directory.parents if par.match(query)] 

68 

69 

70def get_middle_layer() -> Path: 

71 """Find the middle_layer directory in an efficient-ish manner""" 

72 cwd = Path.cwd().resolve() 

73 query = "middle_layer" 

74 if cwd.match(query): 

75 middle_layer = cwd 

76 elif len(get_parents_matching(cwd, query)) == 1: 

77 # In a child 

78 middle_layer = get_parents_matching(cwd, query).pop() 

79 elif cwd.match("ttat") or get_parents_matching(cwd, "ttat"): 

80 # In a parent 

81 if cwd.match("ttat"): 

82 ttat = cwd 

83 else: 

84 # Don't restrict to one match since some people (like Sam) have `.../ttat/ttat` for their repo path 

85 ttat = get_parents_matching(cwd, "ttat").pop() 

86 middle_layer = [ml for ml in ttat.glob(f"**/{query}")][0] # Assume that all matches are parents of middle_layer 

87 else: 

88 # No idea where we are 

89 middle_layer = [ml for ml in Path("/").glob(f"**/{query}") if ml.parent.match("ttat")].pop() 

90 return middle_layer 

91 

92 

93def get_application_base_url(): 

94 # Read the APP_ENV variable set in the Dockerfile (and by GitLab CI) 

95 # Provide a default for local development if APP_ENV is not set 

96 current_env = os.getenv("APP_ENV", "local_development") 

97 

98 if current_env == "prod": 

99 return "https://tta.nrao.edu" 

100 elif current_env == "test": 

101 return "https://tta-test.nrao.edu" 

102 elif current_env == "dev": 

103 return "https://tta-dev.nrao.edu" 

104 elif current_env == "local_backend": # For local backend-focused dev 

105 return "http://localhost:5555" 

106 elif current_env == "local_frontend": # For local frontend-focused dev 

107 return "http://localhost:4200" 

108 else: # Default for local_development or any other unspecified value 

109 # You might want to log a warning if current_env is unexpected 

110 print(f"Warning: APP_ENV is '{current_env}'. Defaulting base_url to http://localhost:5555") 

111 return "http://localhost:5555" # Or any other suitable local default