Coverage for middle_layer/allocate/application_layer/rest_api/views/timebin.py: 95.24%

42 statements  

« prev     ^ index     » next       coverage.py v7.10.5, created at 2026-03-09 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 

18from http import HTTPStatus 

19 

20from pyramid.httpexceptions import HTTPBadRequest 

21from pyramid.request import Request 

22from pyramid.response import Response 

23from pyramid.view import view_config 

24 

25from allocate.domain_layer.entities.allocation_version import AllocationVersion 

26from allocate.domain_layer.entities.available_time_model_version import AvailableTimeModelVersion 

27from allocate.domain_layer.entities.time_bins import TimeCalculationException 

28from allocate.domain_layer.services.pressure_cooker_service import pressure_cooker 

29from allocate.domain_layer.services.serialize_timebins_to_csv_service import serialize_timebins_to_csv 

30 

31 

32def common_timebin_api_handler(request): 

33 available_time_model_version_id = request.params.get("availableTimeModelVersionId") 

34 allocation_version_id = request.params.get("allocationVersionId") 

35 

36 # Default to True if not specified 

37 apply_time_reservations = request.params.get("apply_time_reservations", "true").lower() not in ("false", "0") 

38 

39 if not available_time_model_version_id: 

40 raise HTTPBadRequest(body=f"timebins requires available_time_model_version_id to be specified.") 

41 

42 av: AllocationVersion = None 

43 atmv: AvailableTimeModelVersion = None 

44 

45 if available_time_model_version_id: 

46 atmv: AvailableTimeModelVersion = request.lookup(available_time_model_version_id, AvailableTimeModelVersion) 

47 

48 if allocation_version_id: 

49 av: AllocationVersion = request.lookup(allocation_version_id, AllocationVersion) 

50 try: 

51 timebins = pressure_cooker( 

52 available_time_model_version=atmv, 

53 allocation_version=av, 

54 apply_time_reservations=apply_time_reservations, 

55 ) 

56 except TimeCalculationException as e: 

57 raise HTTPBadRequest(body=str(e)) 

58 return atmv, av, timebins 

59 

60 

61@view_config(route_name="timebins", renderer="json", permission="timebins") 

62def timebins(request: Request) -> Response: 

63 """Return the data required to generate pressure plots for the specified AvailableTimeModelVersion and 

64 AllocationVersion (if specified) 

65 

66 Note: available_time_model_version_id is required, allocation_version_id can be optionally specififed 

67 apply_time_reservations is optional and defaults to True, if set to False, the timebins will be generated 

68 without time reservations 

69 

70 URL: timebins?available_time_model_version_id={available_time_model_version_id} 

71 OR timebins?available_time_model_version_id={ 

72 available_time_model_version_id}&allocation_version_id={allocation_version_id} 

73 

74 Optionally, the above with: &apply_time_reservations={True|False} 

75 

76 :param request: GET Request 

77 :return: Response with the list of JSON formatted pressure plot data per ATM 

78 or 404 response (HTTPNotFound) if no AvailableTimeModelVersion or AllocationVersion exists with the given ids, 

79 or 400 response (HTTPBadRequest) if available_time_model_version_id is not provided or is malformed 

80 or allocation_version_id is also provided but malformed 

81 or 401 response (HTTPUnauthorized) if the requesting user is not a TTA Member 

82 """ 

83 atmv, av, timebins = common_timebin_api_handler(request) 

84 return Response(json_body=timebins) 

85 

86 

87@view_config(route_name="timebins_export", renderer="json", permission="timebins") 

88def timebins_export(request: Request) -> Response: 

89 """Export the data required to generate pressure plots for the specified AvailableTimeModelVersion and 

90 AllocationVersion (if specified). Time Reservations are applied by default but can be disabled by setting 

91 the optional apply_time_reservations to false. 

92 

93 URL: timebins_export?available_time_model_version_id={available_time_model_version_id} 

94 OR timebins_export?available_time_model_version_id={ 

95 available_time_model_version_id}&allocation_version_id={allocation_version_id} 

96 

97 Optionally, the above with: &apply_time_reservations={True|False} 

98 

99 :param request: GET Request 

100 :return: Response with the list of JSON formatted pressure plot data per ATM 

101 or 404 response (HTTPNotFound) if no AllocationVersion or AvailableTimeModelVersion exists with the given ids, 

102 or 400 response (HTTPBadRequest) if available_time_model_version_id is not provided or is malformed 

103 or allocation_version_id is also provided but malformed 

104 or 401 response (HTTPUnauthorized) if the requesting user is not a TTA Member 

105 """ 

106 atmv, av, timebins = common_timebin_api_handler(request) 

107 

108 timebins_csv = serialize_timebins_to_csv(timebins) 

109 

110 if atmv and av: 

111 filename = ( 

112 f"pressure_plot_data_for_available_time_model_version_" 

113 f"{atmv.available_time_model_version_id}_and_allocation_version" 

114 f"_{av.allocation_version_id}.csv" 

115 ) 

116 else: 

117 filename = f"pressure_plot_data_for_available_time_model_version_{atmv.available_time_model_version_id}.csv" 

118 response = Response(status_code=HTTPStatus.OK, body=timebins_csv.encode()) 

119 response.headers["Content-Disposition"] = "attachment;filename=" + filename 

120 response.headers["Access-Control-Expose-Headers"] = "Content-Disposition" 

121 return response