Coverage for middle_layer/testdata/application_layer/services/proposal_generator_demo.py: 79.41%
68 statements
« prev ^ index » next coverage.py v7.10.5, created at 2026-03-09 06:13 +0000
« 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/>.
18import json
19from datetime import datetime, timezone
20from random import choice, choices, randint, randrange, sample, seed
22from allocate.domain_layer.entities.proposal_disposition import ProposalDisposition
23from auth.auth import get_non_tta_members
24from common import get_middle_layer
25from propose.application_layer.services.proposal_state_change_service import create_osr_proposal_review
26from propose.domain_layer.entities.proposal import Author, Proposal, ProposalCopy
27from solicit.domain_layer.entities.solicitation import Solicitation
28from testdata.application_layer.services.allocation_request_generator import generate_allocation_requests
29from testdata.application_layer.services.proposal_generator_abc import ProposalGenerator
32class DemoProposalGenerator(ProposalGenerator):
33 def __init__(self, repo):
34 self.repo = repo
35 self.all_scan_intents = {si.name: si for si in repo.scan_intent_repo.list_all()}
36 self.all_subscan_intents = {ssi.name: ssi for ssi in repo.subscan_intent_repo.list_all()}
37 with open((get_middle_layer() / f"testdata/config/calibratorList.json").resolve()) as f:
38 self.calibrator_list = json.load(f)
39 self.pdf = open(
40 (get_middle_layer() / "propose/application_layer/rest_api/test/files" / "sample1.pdf").resolve(), "rb"
41 ).read()
43 def generate_proposals(
44 self, solicitation: Solicitation, do_submit: bool, num_proposals=8, make_obspecs=True, random_seed=None
45 ) -> list[Proposal]:
46 if random_seed:
47 seed(random_seed) # set the given random seed
49 proposals: list[Proposal] = list()
50 proposal_titles = sample(PROPOSAL_TITLES, k=len(PROPOSAL_TITLES)) # Shuffle without affecting original list
52 for i in range(num_proposals):
53 proposal_number = str(solicitation.current_suffix)
54 if i < len(proposal_titles):
55 title = proposal_titles[i]
56 else:
57 num_words = randint(5, 8)
58 indices = choices(range(0, len(proposal_titles)), k=num_words)
59 split_titles = [proposal_titles[j].split() for j in indices]
60 title_indices = [randrange(0, len(real_title)) for real_title in split_titles]
61 title_words = [real_title[j] for (real_title, j) in zip(split_titles, title_indices)]
62 title = " ".join(title_words)
64 if do_submit:
65 proposal_code = f"{solicitation.proposal_code_prefix}-{proposal_number.zfill(4)}"
66 solicitation.current_suffix = solicitation.current_suffix + 1
67 else:
68 proposal_code = None
69 prop = self.generate_proposal(
70 title=title,
71 abstract=ABSTRACT,
72 solicitation=solicitation,
73 make_obspecs=make_obspecs,
74 do_submit=do_submit,
75 proposal_code=proposal_code,
76 )
77 proposals.append(prop)
78 return proposals
80 def generate_proposal(
81 self,
82 title: str,
83 abstract: str,
84 solicitation: Solicitation,
85 make_obspecs: bool,
86 do_submit: bool,
87 proposal_code: str,
88 ) -> Proposal:
89 prop = Proposal(solicitation, proposal_code=proposal_code, state="Submitted" if do_submit else "Draft")
90 prop.is_triggered = choice([True, False])
91 prop.proposal_id = self.repo.proposal_repo.add(prop)
93 user = choice(get_non_tta_members())
94 author = Author(user.first_name, user.last_name, True, user.user_id, prop)
95 prop.authors = [author]
97 prop.author_copy = ProposalCopy(
98 title,
99 abstract,
100 self.pdf,
101 science_category=choice(solicitation.science_categories),
102 proposal=prop,
103 )
104 prop.author_copy.proposal_copy_id = self.repo.proposal_copy_repo.add(prop.author_copy)
105 prop.author_copy.proposal_class = choice(solicitation.proposal_process.proposal_classes)
106 if do_submit:
107 prop.observatory_copy = ProposalCopy(
108 title,
109 abstract,
110 self.pdf,
111 science_category=prop.author_copy.science_category,
112 proposal=prop,
113 )
114 prop.observatory_copy.proposal_class = prop.author_copy.proposal_class
115 prop.submitted_timestamp = datetime.now(timezone.utc)
117 # make ARs, CRs, and obspecs for prop
118 generate_allocation_requests(
119 repo=self.repo,
120 proposal=prop,
121 test_type="Demo",
122 make_obspecs=make_obspecs,
123 do_submit=do_submit,
124 all_scan_intents=self.all_scan_intents,
125 all_subscan_intents=self.all_subscan_intents,
126 calibrator_list=self.calibrator_list,
127 sfcs=solicitation.solicitation_facility_capabilities,
128 )
130 if do_submit and solicitation.proposal_process.proposal_process_name == "Observatory Site Review":
131 # move the proposal forward to 'In Review' and create reviews and disposition for it.
132 # It's risky to not do this through the state change service, but we avoid it here for efficiency.
133 prop.state = "In Review"
134 create_osr_proposal_review(prop, self.repo)
135 try:
136 pd = self.repo.proposal_disposition_repo.by_id(prop.proposal_id)
137 except ValueError as e:
138 pd = ProposalDisposition(prop)
139 self.repo.proposal_disposition_repo.add(pd)
140 prop.vetted_science_category = prop.author_copy.science_category
141 return prop
144PROPOSAL_TITLES = [
145 "Formaldehyde Densitometry of IR-Bright Galaxies",
146 "Cold Gas in Isolated Elliptical Galaxies",
147 "A New H2CO Flare in IRAS 18566+0408",
148 "Observations with MUSTANG on the GBT at 3.3mm",
149 "GBT OH Observations at high galactic latitudes",
150 "Shocks Revealed by THOR in Young High-Mass Star-Forming Regions",
151 "A Rapidly Evolving Protostar?",
152 "Mapping the Milky Way: A Outreach Project for Highschool Students",
153 "Radiative and mechanical feedback in regions of massive star formation",
154 "The First Galactic Quadrant Supernova Remnant Population",
155 "MUSTANG Galactic Plane survey: The inner Galaxy",
156 "GBT confirmation of Interstellar CH2D+",
157 "Gas Kinematics in Dwarf-Dwarf Interactions: Fueling Hierarchical Assembly",
158 "Magnetic Fields in Heavenly Bridges.",
159 "Detailed Magnetic Field Mapping of 3C 58",
160 "Search for Molecular Outflows from KIC 12557548b",
161 "Sensitive Radio Observations of Relics in Galaxy Clusters",
162 "SETI Observations of Exoplanet Conjunctions",
163 "Multifrequency Monitoring of Radio-Loud Narrow-line Seyfert 1 Galaxies",
164 "Q-band imaging of the remarkable blue-shift dominated jet in G353.273+0.641",
165 "Morphology of Star Formation in Luminous, z ~ 2 - 4 Strongly Lensed Galaxies",
166 "The Evolution of Neutral hydrogen: measuring Omega-HI out to redshift z~0.4",
167 "VLA survey of low-z H-ATLAS/GAMA galaxies",
168 "Probing the Detailed Magnetic Fields in SNRs With Zeeman Splitting",
169 "GCMS on GBT: Thermal Conditions for Star Formation in the Central Molecular Zone",
170 "Broadening the GBT/Zpectrometer Census of z = 2-3.5 Dusty Star Forming Galaxies",
171]
174ABSTRACT = """
175Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temporincididunt ut labore et dolore magna
176aliqua. Dolor purus non enim praesent elementum facilisis leo. Sit amet nulla facilisi morbi tempus iaculis urna id.
177Tempor nec feugiat nisl pretium fusce id. Pellentesque elit ullamcorper dignissim cras. Lectus mauris ultrices eros
178in cursus turpis. Viverra adipiscing at in tellus integer feugiat. Nisi quis eleifend quam adipiscing vitae proin
179sagittis nisl rhoncus. Vel facilisis volutpat est velit egestas. Risus commodo viverra maecenas accumsan lacus vel.
180""".strip()