Skip to content

Commit 307037f

Browse files
committed
Merge branch 'main' of https://github.com/nirupama-dev/scheduler-jupyter-plugin into bug-vertex-listing-pagination
2 parents f53630f + 4d41989 commit 307037f

File tree

17 files changed

+571
-224
lines changed

17 files changed

+571
-224
lines changed

scheduler_jupyter_plugin/commons/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
TAGS = "scheduler_jupyter_plugin"
3333
VERTEX_STORAGE_BUCKET = "vertex-schedules"
3434
UTF8 = "utf-8"
35+
PAYLOAD_JSON_FILE_PATH = "payload.json"
3536

3637
# Composer environment name restrictions are documented here:
3738
# https://cloud.google.com/composer/docs/reference/rest/v1/projects.locations.environments#resource:-environment

scheduler_jupyter_plugin/services/executor.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from google.api_core.exceptions import NotFound
2222
import google.oauth2.credentials as oauth2
2323
import aiofiles
24+
import json
2425

2526
import aiohttp
2627
import pendulum
@@ -35,6 +36,7 @@
3536
PACKAGE_NAME,
3637
WRAPPER_PAPPERMILL_FILE,
3738
UTF8,
39+
PAYLOAD_JSON_FILE_PATH,
3840
)
3941
from scheduler_jupyter_plugin.models.models import DescribeJob
4042
from scheduler_jupyter_plugin.services import airflow
@@ -290,8 +292,7 @@ async def check_package_in_env(self, composer_environment_name):
290292
else:
291293
decoded_output = stdout.decode(UTF8)
292294
installed_packages = set(
293-
line.split()[0].lower()
294-
for line in decoded_output.splitlines()[2:]
295+
line.split()[0].lower() for line in decoded_output.splitlines()[2:]
295296
)
296297
for package in packages:
297298
if package.lower() not in installed_packages:
@@ -303,7 +304,6 @@ async def check_package_in_env(self, composer_environment_name):
303304
self.log.exception(f"Error checking packages: {error}")
304305
raise IOError(f"Error checking packages: {error}")
305306

306-
307307
async def install_to_composer_environment(
308308
self, local_kernel, composer_environment_name, packages_to_install
309309
):
@@ -320,9 +320,7 @@ async def install_to_composer_environment(
320320
stderr=subprocess.PIPE,
321321
shell=True,
322322
)
323-
install_stdout, install_stderr = (
324-
install_process.communicate()
325-
)
323+
install_stdout, install_stderr = install_process.communicate()
326324
if install_process.returncode == 0:
327325
self.log.info(f"{package} installed successfully.")
328326
else:
@@ -338,6 +336,12 @@ async def install_to_composer_environment(
338336
self.log.exception(f"error installing {package}: {install_stderr}")
339337
return {"error": str(e)}
340338

339+
def create_payload(self, file_path, project_id, region, input_data):
340+
payload = {"projectId": project_id, "region": region, "job": input_data}
341+
342+
with open(file_path, "w") as f:
343+
json.dump(payload, f, indent=4)
344+
341345
async def execute(self, input_data, project_id=None, region_id=None):
342346
try:
343347
job = DescribeJob(**input_data)
@@ -354,7 +358,9 @@ async def execute(self, input_data, project_id=None, region_id=None):
354358

355359
if job.packages_to_install != None:
356360
install_packages = await self.install_to_composer_environment(
357-
job.local_kernel, job.composer_environment_name, job.packages_to_install
361+
job.local_kernel,
362+
job.composer_environment_name,
363+
job.packages_to_install,
358364
)
359365
if install_packages and install_packages.get("error"):
360366
raise Exception(install_packages)
@@ -374,12 +380,25 @@ async def execute(self, input_data, project_id=None, region_id=None):
374380
print(
375381
f"The file gs://{gcs_dag_bucket}/{wrapper_pappermill_file_path} does not exist."
376382
)
383+
# uploading input file while creating the job
377384
if not job.input_filename.startswith(GCS):
378385
await self.upload_to_gcs(
379386
gcs_dag_bucket,
380387
file_path=f"./{job.input_filename}",
381388
destination_dir=f"dataproc-notebooks/{job_name}/input_notebooks",
382389
)
390+
# creating a json file for payload
391+
self.create_payload(
392+
PAYLOAD_JSON_FILE_PATH, project_id, region_id, input_data
393+
)
394+
395+
# uploading payload JSON file to GCS
396+
await self.upload_to_gcs(
397+
gcs_dag_bucket,
398+
file_path=PAYLOAD_JSON_FILE_PATH,
399+
destination_dir=f"dataproc-notebooks/{job_name}/dag_details",
400+
)
401+
383402
file_path = self.prepare_dag(job, gcs_dag_bucket, dag_file)
384403
await self.upload_to_gcs(
385404
gcs_dag_bucket, file_path=file_path, destination_dir="dags"

scheduler_jupyter_plugin/services/vertex.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515

1616
import aiohttp
17+
import json
1718
from cron_descriptor import get_description
1819

1920
import google.oauth2.credentials as oauth2
@@ -88,10 +89,17 @@ async def upload_to_gcs(self, bucket_name, file_path, job_name):
8889
blob = bucket.blob(blob_name)
8990
blob.upload_from_filename(file_path)
9091

92+
# creating json file containing the input file path
93+
metadata = {"inputFilePath": f"gs://{bucket_name}/{blob_name}"}
94+
json_file_name = f"{job_name}.json"
95+
96+
with open(json_file_name, "w") as f:
97+
json.dump(metadata, f, indent=4)
98+
9199
# uploading json file containing the input file path
92-
json_blob_name = f"{job_name}/{job_name}.json"
100+
json_blob_name = f"{job_name}/{json_file_name}"
93101
json_blob = bucket.blob(json_blob_name)
94-
json_blob.upload_from_string(f"gs://{bucket_name}/{blob_name}")
102+
json_blob.upload_from_filename(json_file_name)
95103

96104
self.log.info(f"File {input_notebook} uploaded to gcs successfully")
97105
return blob_name
@@ -248,10 +256,10 @@ async def list_uiconfig(self, region_id):
248256
async def list_schedules(self, region_id, page_size=100, next_page_token=None):
249257
try:
250258
result = {}
251-
259+
252260
if next_page_token:
253261
api_endpoint = f"https://{region_id}-aiplatform.googleapis.com/v1/projects/{self.project_id}/locations/{region_id}/schedules?orderBy=createTime desc&pageToken={next_page_token}&pageSize={page_size}"
254-
262+
255263
else:
256264
api_endpoint = f"https://{region_id}-aiplatform.googleapis.com/v1/projects/{self.project_id}/locations/{region_id}/schedules?orderBy=createTime desc&pageSize={page_size}"
257265

src/controls/RegionDropdown.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,22 @@ export function RegionDropdown(props: Props) {
4848
loaderRegion,
4949
regionsList
5050
} = props;
51-
const regions = useRegion(projectId);
51+
let regionStrList: string[] = [];
5252

53-
const regionStrList = useMemo(
54-
() => regions.map(region => region.name),
55-
[regions]
56-
);
53+
if (!regionsList) {
54+
const regions = useRegion(projectId);
55+
56+
regionStrList = useMemo(
57+
() => regions.map(region => region.name),
58+
[regions]
59+
);
60+
}
5761

5862
return (
5963
<Autocomplete
60-
value={region}
64+
value={
65+
regionsList ? (regionsList?.includes(region) ? region : '') : region
66+
}
6167
options={regionsList ? regionsList : regionStrList}
6268
onChange={(_, value) => onRegionChange(value ?? '')}
6369
PaperComponent={(props: PaperProps) => <Paper elevation={8} {...props} />}

src/jobs/LabelProperties.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,9 @@ function LabelProperties({
162162
} else if (
163163
buttonText !== 'ADD LABEL' &&
164164
(labelDetail.length === 0 ||
165-
labelDetail[labelDetail.length - 1].split(':')[0].length > 0)
165+
(labelDetail[labelDetail.length - 1].split(':')[0].length > 0 &&
166+
labelDetail[labelDetail.length - 1].split(':')[1].length > 0)) &&
167+
duplicateKeyError === -1
166168
) {
167169
return 'job-add-property-button';
168170
} else {
@@ -334,7 +336,8 @@ function LabelProperties({
334336
if (!isDisabled) {
335337
if (
336338
labelDetail.length === 0 ||
337-
labelDetail[labelDetail.length - 1].split(':')[0].length > 0
339+
(labelDetail[labelDetail.length - 1].split(':')[0].length > 0 &&
340+
labelDetail[labelDetail.length - 1].split(':')[1].length > 0)
338341
) {
339342
handleAddLabel(e);
340343
}
@@ -344,7 +347,8 @@ function LabelProperties({
344347
}}
345348
>
346349
{labelDetail.length === 0 ||
347-
labelDetail[labelDetail.length - 1].split(':')[0].length > 0 ? (
350+
(labelDetail[labelDetail.length - 1].split(':')[0].length > 0 &&
351+
labelDetail[labelDetail.length - 1].split(':')[1].length > 0) ? (
348352
<iconPlus.react
349353
tag="div"
350354
className="icon-black logo-alignment-style"
@@ -358,7 +362,8 @@ function LabelProperties({
358362
<span
359363
className={
360364
labelDetail.length === 0 ||
361-
labelDetail[labelDetail.length - 1].split(':')[0].length > 0
365+
(labelDetail[labelDetail.length - 1].split(':')[0].length > 0 &&
366+
labelDetail[labelDetail.length - 1].split(':')[1].length > 0)
362367
? 'job-edit-text'
363368
: 'job-edit-text-disabled'
364369
}

src/scheduler/NotebookScheduler.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,13 +182,22 @@ const NotebookSchedulerComponent = ({
182182
/>
183183
</div>
184184
{jobNameSelected === '' && !editMode && (
185-
<ErrorMessage message="Job name is required" />
185+
<ErrorMessage
186+
message="Job name is required"
187+
showIcon={notebookSelector === 'composer' ? true : false}
188+
/>
186189
)}
187190
{jobNameSpecialValidation && jobNameValidation && !editMode && (
188-
<ErrorMessage message="Name must contain only letters, numbers, hyphens, and underscores" />
191+
<ErrorMessage
192+
message="Name must contain only letters, numbers, hyphens, and underscores"
193+
showIcon={notebookSelector === 'composer' ? true : false}
194+
/>
189195
)}
190196
{!jobNameUniqueValidation && !editMode && (
191-
<ErrorMessage message="Job name must be unique for the selected environment" />
197+
<ErrorMessage
198+
message="Job name must be unique for the selected environment"
199+
showIcon={notebookSelector === 'composer' ? true : false}
200+
/>
192201
)}
193202

194203
<div className="create-scheduler-form-element-input-file">

src/scheduler/composer/ListNotebookScheduler.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -487,12 +487,14 @@ function listNotebookScheduler({
487487
);
488488
} else if (cell.column.Header === 'Job Name') {
489489
return (
490-
<td
491-
{...cell.getCellProps()}
492-
className="clusters-table-data"
493-
onClick={() => handleDagIdSelection(composerSelectedList, cell.value)}
494-
>
495-
{cell.value}
490+
<td {...cell.getCellProps()} className="clusters-table-data">
491+
<span
492+
onClick={() =>
493+
handleDagIdSelection(composerSelectedList, cell.value)
494+
}
495+
>
496+
{cell.value}
497+
</span>
496498
</td>
497499
);
498500
} else {
@@ -551,6 +553,10 @@ function listNotebookScheduler({
551553
}, []);
552554

553555
useEffect(() => {
556+
if (composerList.length === 0) {
557+
setComposerSelectedList('');
558+
setDagList([]);
559+
}
554560
if (
555561
composerList.length > 0 &&
556562
backselectedEnvironment === '' &&

0 commit comments

Comments
 (0)