From fde0c274cefd6364320f1f05c07ee1346aec954e Mon Sep 17 00:00:00 2001 From: Mike <45373284+munkhuushmgl@users.noreply.github.com> Date: Wed, 20 May 2020 19:59:47 -0700 Subject: [PATCH 01/12] Talent v4beta1 samples [Restoring deleted branch samples] [(#3273)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/3273) * restored deleted samples * fixed lint issues * added tests and formatted code * corrected test req.txt file * made requested changes and added return val for samples * added secrets.txt to bash, script * fixed the lint * moved to requirements-test.txt * delete resources in teardown just in case * restored deleted samples fixed lint issues added tests and formatted code corrected test req.txt file made requested changes and added return val for samples added secrets.txt to bash, script fixed the lint * added some spacing * restored deleted samples fixed lint issues added tests and formatted code corrected test req.txt file made requested changes and added return val for samples added secrets.txt to bash, script fixed the lint restored deleted samples fixed lint issues added tests and formatted code corrected test req.txt file made requested changes and added return val for samples added secrets.txt to bash, script fixed the lint moved to requirements-test.txt delete resources in teardown just in case added some spacing * fixed merge conflicts * added conftest.py and refactored samples with loop * removed talent secret.txt from bash script * fixed the lint issue * removed unnecessary env vars * deleted global random ids, deleted unnecessary setup code from delete tests * removed unused imports * removed pytest * removed unused IDs * deleted unused uuid imports Co-authored-by: Takashi Matsuo --- samples/snippets/conftest.py | 87 +++++++++++ .../job_search_autocomplete_job_title.py | 56 +++++++ .../job_search_autocomplete_job_title_test.py | 25 +++ .../snippets/job_search_batch_create_jobs.py | 133 ++++++++++++++++ .../snippets/job_search_batch_update_jobs.py | 143 ++++++++++++++++++ samples/snippets/job_search_commute_search.py | 66 ++++++++ .../job_search_commute_search_test.py | 25 +++ .../job_search_create_client_event.py | 77 ++++++++++ samples/snippets/job_search_create_company.py | 50 ++++++ .../job_search_create_company_test.py | 48 ++++++ samples/snippets/job_search_create_job.py | 71 +++++++++ ...job_search_create_job_custom_attributes.py | 63 ++++++++ ...earch_create_job_custom_attributes_test.py | 46 ++++++ .../snippets/job_search_create_job_test.py | 47 ++++++ samples/snippets/job_search_create_tenant.py | 43 ++++++ .../snippets/job_search_create_tenant_test.py | 48 ++++++ .../job_search_custom_ranking_search.py | 64 ++++++++ .../job_search_custom_ranking_search_test.py | 25 +++ samples/snippets/job_search_delete_company.py | 42 +++++ .../job_search_delete_company_test.py | 27 ++++ samples/snippets/job_search_delete_job.py | 42 +++++ .../snippets/job_search_delete_job_test.py | 25 +++ samples/snippets/job_search_delete_tenant.py | 39 +++++ .../snippets/job_search_delete_tenant_test.py | 25 +++ samples/snippets/job_search_get_company.py | 43 ++++++ .../snippets/job_search_get_company_test.py | 25 +++ samples/snippets/job_search_get_job.py | 52 +++++++ samples/snippets/job_search_get_job_test.py | 25 +++ samples/snippets/job_search_get_tenant.py | 40 +++++ .../snippets/job_search_get_tenant_test.py | 26 ++++ .../snippets/job_search_histogram_search.py | 65 ++++++++ .../job_search_histogram_search_test.py | 26 ++++ samples/snippets/job_search_list_companies.py | 45 ++++++ .../job_search_list_companies_test.py | 25 +++ samples/snippets/job_search_list_jobs.py | 56 +++++++ samples/snippets/job_search_list_jobs_test.py | 26 ++++ samples/snippets/job_search_list_tenants.py | 38 +++++ .../snippets/job_search_list_tenants_test.py | 25 +++ samples/snippets/requirements-test.txt | 1 + samples/snippets/requirements.txt | 1 + 40 files changed, 1836 insertions(+) create mode 100644 samples/snippets/conftest.py create mode 100644 samples/snippets/job_search_autocomplete_job_title.py create mode 100644 samples/snippets/job_search_autocomplete_job_title_test.py create mode 100644 samples/snippets/job_search_batch_create_jobs.py create mode 100644 samples/snippets/job_search_batch_update_jobs.py create mode 100644 samples/snippets/job_search_commute_search.py create mode 100644 samples/snippets/job_search_commute_search_test.py create mode 100644 samples/snippets/job_search_create_client_event.py create mode 100644 samples/snippets/job_search_create_company.py create mode 100644 samples/snippets/job_search_create_company_test.py create mode 100644 samples/snippets/job_search_create_job.py create mode 100644 samples/snippets/job_search_create_job_custom_attributes.py create mode 100644 samples/snippets/job_search_create_job_custom_attributes_test.py create mode 100644 samples/snippets/job_search_create_job_test.py create mode 100644 samples/snippets/job_search_create_tenant.py create mode 100644 samples/snippets/job_search_create_tenant_test.py create mode 100644 samples/snippets/job_search_custom_ranking_search.py create mode 100644 samples/snippets/job_search_custom_ranking_search_test.py create mode 100644 samples/snippets/job_search_delete_company.py create mode 100644 samples/snippets/job_search_delete_company_test.py create mode 100644 samples/snippets/job_search_delete_job.py create mode 100644 samples/snippets/job_search_delete_job_test.py create mode 100644 samples/snippets/job_search_delete_tenant.py create mode 100644 samples/snippets/job_search_delete_tenant_test.py create mode 100644 samples/snippets/job_search_get_company.py create mode 100644 samples/snippets/job_search_get_company_test.py create mode 100644 samples/snippets/job_search_get_job.py create mode 100644 samples/snippets/job_search_get_job_test.py create mode 100644 samples/snippets/job_search_get_tenant.py create mode 100644 samples/snippets/job_search_get_tenant_test.py create mode 100644 samples/snippets/job_search_histogram_search.py create mode 100644 samples/snippets/job_search_histogram_search_test.py create mode 100644 samples/snippets/job_search_list_companies.py create mode 100644 samples/snippets/job_search_list_companies_test.py create mode 100644 samples/snippets/job_search_list_jobs.py create mode 100644 samples/snippets/job_search_list_jobs_test.py create mode 100644 samples/snippets/job_search_list_tenants.py create mode 100644 samples/snippets/job_search_list_tenants_test.py create mode 100644 samples/snippets/requirements-test.txt create mode 100755 samples/snippets/requirements.txt diff --git a/samples/snippets/conftest.py b/samples/snippets/conftest.py new file mode 100644 index 00000000..169a401b --- /dev/null +++ b/samples/snippets/conftest.py @@ -0,0 +1,87 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import uuid + +from google.api_core.exceptions import NotFound + +import pytest + +import job_search_create_company +import job_search_create_job +import job_search_create_tenant +import job_search_delete_company +import job_search_delete_job +import job_search_delete_tenant + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] + + +@pytest.fixture(scope="module") +def tenant(): + tenant_ext_unique_id = "TEST_TENANT_{}".format(uuid.uuid4()) + # create a temporary tenant + tenant_name = job_search_create_tenant.create_tenant( + PROJECT_ID, tenant_ext_unique_id + ) + + # extract company id + tenant_id = tenant_name.split("/")[-1] + + yield tenant_id + + try: + job_search_delete_tenant.delete_tenant(PROJECT_ID, tenant_id) + except NotFound as e: + print("Ignoring NotFound upon cleanup, details: {}".format(e)) + + +@pytest.fixture(scope="module") +def company(tenant): + company_ext_id = "COMPANY_EXT_ID_{}".format(uuid.uuid4()) + + # create a temporary company + company_name = job_search_create_company.create_company( + PROJECT_ID, tenant, "Test Company Name", company_ext_id + ) + + # extract company id + company_id = company_name.split("/")[-1] + + yield company_id + + try: + job_search_delete_company.delete_company(PROJECT_ID, tenant, company_id) + except NotFound as e: + print("Ignoring NotFound upon cleanup, details: {}".format(e)) + + +@pytest.fixture(scope="module") +def job(tenant, company): + post_unique_id = "TEST_POST_{}".format(uuid.uuid4().hex)[:20] + # create a temporary job + job_name = job_search_create_job.create_job( + PROJECT_ID, tenant, company, post_unique_id, "www.jobUrl.com" + ) + + # extract company id + job_id = job_name.split("/")[-1] + + yield job_id + + try: + job_search_delete_job.delete_job(PROJECT_ID, tenant, job_id) + except NotFound as e: + print("Ignoring NotFound upon cleanup, details: {}".format(e)) diff --git a/samples/snippets/job_search_autocomplete_job_title.py b/samples/snippets/job_search_autocomplete_job_title.py new file mode 100644 index 00000000..af817ec1 --- /dev/null +++ b/samples/snippets/job_search_autocomplete_job_title.py @@ -0,0 +1,56 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_autocomplete_job_title] + +from google.cloud import talent_v4beta1 +from google.cloud.talent import enums +import six + + +def complete_query(project_id, tenant_id, query): + """Complete job title given partial text (autocomplete)""" + + client = talent_v4beta1.CompletionClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + # query = '[partially typed job title]' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + if isinstance(query, six.binary_type): + query = query.decode("utf-8") + + parent = client.tenant_path(project_id, tenant_id) + + response = client.complete_query( + parent, + query, + page_size=5, # limit for number of results + language_codes=["en-US"], # language code + ) + for result in response.completion_results: + print("Suggested title: {}".format(result.suggestion)) + # Suggestion type is JOB_TITLE or COMPANY_TITLE + print( + "Suggestion type: {}".format( + enums.CompleteQueryRequest.CompletionType(result.type).name + ) + ) + + +# [END job_search_autocomplete_job_title] diff --git a/samples/snippets/job_search_autocomplete_job_title_test.py b/samples/snippets/job_search_autocomplete_job_title_test.py new file mode 100644 index 00000000..4fe24984 --- /dev/null +++ b/samples/snippets/job_search_autocomplete_job_title_test.py @@ -0,0 +1,25 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import job_search_autocomplete_job_title + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] + + +def test_autocomplete_job_title(capsys, tenant): + job_search_autocomplete_job_title.complete_query(PROJECT_ID, tenant, "Software") + out, _ = capsys.readouterr() + assert "Suggested title:" in out diff --git a/samples/snippets/job_search_batch_create_jobs.py b/samples/snippets/job_search_batch_create_jobs.py new file mode 100644 index 00000000..a221cc57 --- /dev/null +++ b/samples/snippets/job_search_batch_create_jobs.py @@ -0,0 +1,133 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_batch_create_jobs] + +from google.cloud import talent +import six + + +def batch_create_jobs( + project_id, + tenant_id, + company_name_one, + requisition_id_one, + title_one, + description_one, + job_application_url_one, + address_one, + language_code_one, + company_name_two, + requisition_id_two, + title_two, + description_two, + job_application_url_two, + address_two, + language_code_two, +): + """ + Batch Create Jobs + + Args: + project_id Your Google Cloud Project ID + tenant_id Identifier of the Tenant + """ + + client = talent.JobServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + # company_name_one = 'Company name, e.g. projects/your-project/companies/company-id' + # requisition_id_one = 'Job requisition ID, aka Posting ID. Unique per job.' + # title_one = 'Software Engineer' + # description_one = 'This is a description of this wonderful job!' + # job_application_url_one = 'https://www.example.org/job-posting/123' + # address_one = '1600 Amphitheatre Parkway, Mountain View, CA 94043' + # language_code_one = 'en-US' + # company_name_two = 'Company name, e.g. projects/your-project/companies/company-id' + # requisition_id_two = 'Job requisition ID, aka Posting ID. Unique per job.' + # title_two = 'Quality Assurance' + # description_two = 'This is a description of this wonderful job!' + # job_application_url_two = 'https://www.example.org/job-posting/123' + # address_two = '111 8th Avenue, New York, NY 10011' + # language_code_two = 'en-US' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + if isinstance(company_name_one, six.binary_type): + company_name_one = company_name_one.decode("utf-8") + if isinstance(requisition_id_one, six.binary_type): + requisition_id_one = requisition_id_one.decode("utf-8") + if isinstance(title_one, six.binary_type): + title_one = title_one.decode("utf-8") + if isinstance(description_one, six.binary_type): + description_one = description_one.decode("utf-8") + if isinstance(job_application_url_one, six.binary_type): + job_application_url_one = job_application_url_one.decode("utf-8") + if isinstance(address_one, six.binary_type): + address_one = address_one.decode("utf-8") + if isinstance(language_code_one, six.binary_type): + language_code_one = language_code_one.decode("utf-8") + if isinstance(company_name_two, six.binary_type): + company_name_two = company_name_two.decode("utf-8") + if isinstance(requisition_id_two, six.binary_type): + requisition_id_two = requisition_id_two.decode("utf-8") + if isinstance(title_two, six.binary_type): + title_two = title_two.decode("utf-8") + if isinstance(description_two, six.binary_type): + description_two = description_two.decode("utf-8") + if isinstance(job_application_url_two, six.binary_type): + job_application_url_two = job_application_url_two.decode("utf-8") + if isinstance(address_two, six.binary_type): + address_two = address_two.decode("utf-8") + if isinstance(language_code_two, six.binary_type): + language_code_two = language_code_two.decode("utf-8") + parent = client.tenant_path(project_id, tenant_id) + uris = [job_application_url_one] + application_info = {"uris": uris} + addresses = [address_one] + jobs_element = { + "company": company_name_one, + "requisition_id": requisition_id_one, + "title": title_one, + "description": description_one, + "application_info": application_info, + "addresses": addresses, + "language_code": language_code_one, + } + uris_2 = [job_application_url_two] + application_info_2 = {"uris": uris_2} + addresses_2 = [address_two] + jobs_element_2 = { + "company": company_name_two, + "requisition_id": requisition_id_two, + "title": title_two, + "description": description_two, + "application_info": application_info_2, + "addresses": addresses_2, + "language_code": language_code_two, + } + jobs = [jobs_element, jobs_element_2] + + operation = client.batch_create_jobs(parent, jobs) + + print("Waiting for operation to complete...") + response = operation.result(90) + + print("Batch response: {}".format(response)) + + +# [END job_search_batch_create_jobs] diff --git a/samples/snippets/job_search_batch_update_jobs.py b/samples/snippets/job_search_batch_update_jobs.py new file mode 100644 index 00000000..bac4232e --- /dev/null +++ b/samples/snippets/job_search_batch_update_jobs.py @@ -0,0 +1,143 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_batch_update_jobs] + +from google.cloud import talent +import six + + +def batch_update_jobs( + project_id, + tenant_id, + job_name_one, + company_name_one, + requisition_id_one, + title_one, + description_one, + job_application_url_one, + address_one, + language_code_one, + job_name_two, + company_name_two, + requisition_id_two, + title_two, + description_two, + job_application_url_two, + address_two, + language_code_two, +): + """ + Batch Update Jobs + + Args: + project_id Your Google Cloud Project ID + tenant_id Identifier of the Tenant + """ + + client = talent.JobServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + # job_name_one = 'job name, projects/your-project/tenants/tenant-id/jobs/job-id' + # company_name_one = 'Company name, e.g. projects/your-project/companies/company-id' + # requisition_id_one = 'Job requisition ID, aka Posting ID. Unique per job.' + # title_one = 'Software Engineer' + # description_one = 'This is a description of this wonderful job!' + # job_application_url_one = 'https://www.example.org/job-posting/123' + # address_one = '1600 Amphitheatre Parkway, Mountain View, CA 94043' + # language_code_one = 'en-US' + # job_name_two = 'job name, projects/your-project/tenants/tenant-id/jobs/job-id' + # company_name_two = 'Company name, e.g. projects/your-project/companies/company-id' + # requisition_id_two = 'Job requisition ID, aka Posting ID. Unique per job.' + # title_two = 'Quality Assurance' + # description_two = 'This is a description of this wonderful job!' + # job_application_url_two = 'https://www.example.org/job-posting/123' + # address_two = '111 8th Avenue, New York, NY 10011' + # language_code_two = 'en-US' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + if isinstance(job_name_one, six.binary_type): + job_name_one = job_name_one.decode("utf-8") + if isinstance(company_name_one, six.binary_type): + company_name_one = company_name_one.decode("utf-8") + if isinstance(requisition_id_one, six.binary_type): + requisition_id_one = requisition_id_one.decode("utf-8") + if isinstance(title_one, six.binary_type): + title_one = title_one.decode("utf-8") + if isinstance(description_one, six.binary_type): + description_one = description_one.decode("utf-8") + if isinstance(job_application_url_one, six.binary_type): + job_application_url_one = job_application_url_one.decode("utf-8") + if isinstance(address_one, six.binary_type): + address_one = address_one.decode("utf-8") + if isinstance(language_code_one, six.binary_type): + language_code_one = language_code_one.decode("utf-8") + if isinstance(job_name_two, six.binary_type): + job_name_two = job_name_two.decode("utf-8") + if isinstance(company_name_two, six.binary_type): + company_name_two = company_name_two.decode("utf-8") + if isinstance(requisition_id_two, six.binary_type): + requisition_id_two = requisition_id_two.decode("utf-8") + if isinstance(title_two, six.binary_type): + title_two = title_two.decode("utf-8") + if isinstance(description_two, six.binary_type): + description_two = description_two.decode("utf-8") + if isinstance(job_application_url_two, six.binary_type): + job_application_url_two = job_application_url_two.decode("utf-8") + if isinstance(address_two, six.binary_type): + address_two = address_two.decode("utf-8") + if isinstance(language_code_two, six.binary_type): + language_code_two = language_code_two.decode("utf-8") + parent = client.tenant_path(project_id, tenant_id) + uris = [job_application_url_one] + application_info = {"uris": uris} + addresses = [address_one] + jobs_element = { + "name": job_name_one, + "company": company_name_one, + "requisition_id": requisition_id_one, + "title": title_one, + "description": description_one, + "application_info": application_info, + "addresses": addresses, + "language_code": language_code_one, + } + uris_2 = [job_application_url_two] + application_info_2 = {"uris": uris_2} + addresses_2 = [address_two] + jobs_element_2 = { + "name": job_name_two, + "company": company_name_two, + "requisition_id": requisition_id_two, + "title": title_two, + "description": description_two, + "application_info": application_info_2, + "addresses": addresses_2, + "language_code": language_code_two, + } + jobs = [jobs_element, jobs_element_2] + + operation = client.batch_update_jobs(parent, jobs) + + print("Waiting for operation to complete...") + response = operation.result(90) + + print("Batch response: {}".format(response)) + + +# [END job_search_batch_update_jobs] diff --git a/samples/snippets/job_search_commute_search.py b/samples/snippets/job_search_commute_search.py new file mode 100644 index 00000000..1a5df79f --- /dev/null +++ b/samples/snippets/job_search_commute_search.py @@ -0,0 +1,66 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_commute_search] + +from google.cloud import talent +from google.cloud.talent import enums +import six + + +def search_jobs(project_id, tenant_id): + """Search Jobs using commute distance""" + + client = talent.JobServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + parent = client.tenant_path(project_id, tenant_id) + domain = "www.example.com" + session_id = "Hashed session identifier" + user_id = "Hashed user identifier" + request_metadata = {"domain": domain, "session_id": session_id, "user_id": user_id} + commute_method = enums.CommuteMethod.TRANSIT + seconds = 1800 + travel_duration = {"seconds": seconds} + latitude = 37.422408 + longitude = -122.084068 + start_coordinates = {"latitude": latitude, "longitude": longitude} + commute_filter = { + "commute_method": commute_method, + "travel_duration": travel_duration, + "start_coordinates": start_coordinates, + } + job_query = {"commute_filter": commute_filter} + + # Iterate over all results + results = [] + for response_item in client.search_jobs( + parent, request_metadata, job_query=job_query + ): + print("Job summary: {}".format(response_item.job_summary)) + print("Job title snippet: {}".format(response_item.job_title_snippet)) + job = response_item.job + results.append(job.name) + print("Job name: {}".format(job.name)) + print("Job title: {}".format(job.title)) + return results + + +# [END job_search_commute_search] diff --git a/samples/snippets/job_search_commute_search_test.py b/samples/snippets/job_search_commute_search_test.py new file mode 100644 index 00000000..91187501 --- /dev/null +++ b/samples/snippets/job_search_commute_search_test.py @@ -0,0 +1,25 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import job_search_commute_search + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] + + +def test_commute_search(tenant): + jobs = job_search_commute_search.search_jobs(PROJECT_ID, tenant) + for job in jobs: + assert "projects/" in job diff --git a/samples/snippets/job_search_create_client_event.py b/samples/snippets/job_search_create_client_event.py new file mode 100644 index 00000000..c5bb1b48 --- /dev/null +++ b/samples/snippets/job_search_create_client_event.py @@ -0,0 +1,77 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_create_client_event] + +from google.cloud import talent +from google.cloud.talent import enums +import six + + +def create_client_event(project_id, tenant_id, request_id, event_id): + """ + Creates a client event + + Args: + project_id Your Google Cloud Project ID + tenant_id Identifier of the Tenant + request_id A unique ID generated in the API responses. + Value should be set to the request_id from an API response. + event_id A unique identifier, generated by the client application + """ + + client = talent.EventServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + # request_id = '[request_id from ResponseMetadata]' + # event_id = '[Set this to a unique identifier]' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + if isinstance(request_id, six.binary_type): + request_id = request_id.decode("utf-8") + if isinstance(event_id, six.binary_type): + event_id = event_id.decode("utf-8") + parent = client.tenant_path(project_id, tenant_id) + + # The timestamp of the event as seconds of UTC time since Unix epoch + # For more information on how to create google.protobuf.Timestamps + # See: + # https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/timestamp.proto + seconds = 0 + create_time = {"seconds": seconds} + + # The type of event attributed to the behavior of the end user + type_ = enums.JobEvent.JobEventType.VIEW + + # List of job names associated with this event + jobs_element = "projects/[Project ID]/tenants/[Tenant ID]/jobs/[Job ID]" + jobs_element_2 = "projects/[Project ID]/tenants/[Tenant ID]/jobs/[Job ID]" + jobs = [jobs_element, jobs_element_2] + job_event = {"type": type_, "jobs": jobs} + client_event = { + "request_id": request_id, + "event_id": event_id, + "create_time": create_time, + "job_event": job_event, + } + + response = client.create_client_event(parent, client_event) + print(response) + + +# [END job_search_create_client_event] diff --git a/samples/snippets/job_search_create_company.py b/samples/snippets/job_search_create_company.py new file mode 100644 index 00000000..828fb0e4 --- /dev/null +++ b/samples/snippets/job_search_create_company.py @@ -0,0 +1,50 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_create_company] + +from google.cloud import talent +import six + + +def create_company(project_id, tenant_id, display_name, external_id): + """Create Company""" + + client = talent.CompanyServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + # display_name = 'My Company Name' + # external_id = 'Identifier of this company in my system' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + if isinstance(display_name, six.binary_type): + display_name = display_name.decode("utf-8") + if isinstance(external_id, six.binary_type): + external_id = external_id.decode("utf-8") + parent = client.tenant_path(project_id, tenant_id) + company = {"display_name": display_name, "external_id": external_id} + + response = client.create_company(parent, company) + print("Created Company") + print("Name: {}".format(response.name)) + print("Display Name: {}".format(response.display_name)) + print("External ID: {}".format(response.external_id)) + return response.name + + +# [END job_search_create_company] diff --git a/samples/snippets/job_search_create_company_test.py b/samples/snippets/job_search_create_company_test.py new file mode 100644 index 00000000..e78f6090 --- /dev/null +++ b/samples/snippets/job_search_create_company_test.py @@ -0,0 +1,48 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import uuid + +import pytest + +import job_search_create_company +import job_search_delete_company + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] +COMPANY_EXT_ID = "COMPANY_EXT_ID_{}".format(uuid.uuid4()) + + +def test_create_company(capsys, tenant, cleaner): + # create company + company_name = job_search_create_company.create_company( + PROJECT_ID, tenant, "Test Company Name", COMPANY_EXT_ID + ) + out, _ = capsys.readouterr() + assert "Created" in out + assert "Name:" in out + + # extract id + company_id = company_name.split("/")[-1] + cleaner.append(company_id) + + +@pytest.fixture(scope="module") +def cleaner(tenant): + companies = [] + + yield companies + + for company_id in companies: + job_search_delete_company.delete_company(PROJECT_ID, tenant, company_id) diff --git a/samples/snippets/job_search_create_job.py b/samples/snippets/job_search_create_job.py new file mode 100644 index 00000000..ed065d24 --- /dev/null +++ b/samples/snippets/job_search_create_job.py @@ -0,0 +1,71 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_create_job] + +from google.cloud import talent +import six + + +def create_job( + project_id, tenant_id, company_id, requisition_id, job_application_url, +): + """Create Job""" + + client = talent.JobServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + # company_id = 'Company name, e.g. projects/your-project/companies/company-id' + # requisition_id = 'Job requisition ID, aka Posting ID. Unique per job.' + # title = 'Software Engineer' + # description = 'This is a description of this wonderful job!' + # job_application_url = 'https://www.example.org/job-posting/123' + # address_one = '1600 Amphitheatre Parkway, Mountain View, CA 94043' + # address_two = '111 8th Avenue, New York, NY 10011' + # language_code = 'en-US' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + if isinstance(company_id, six.binary_type): + company_id = company_id.decode("utf-8") + if isinstance(requisition_id, six.binary_type): + requisition_id = requisition_id.decode("utf-8") + if isinstance(job_application_url, six.binary_type): + job_application_url = job_application_url.decode("utf-8") + parent = client.tenant_path(project_id, tenant_id) + uris = [job_application_url] + application_info = {"uris": uris} + addresses = [ + "1600 Amphitheatre Parkway, Mountain View, CA 94043", + "111 8th Avenue, New York, NY 10011", + ] + job = { + "company": company_id, + "requisition_id": requisition_id, + "title": "Software Developer", + "description": "Develop, maintain the software solutions.", + "application_info": application_info, + "addresses": addresses, + "language_code": "en-US", + } + + response = client.create_job(parent, job) + print("Created job: {}".format(response.name)) + return response.name + + +# [END job_search_create_job] diff --git a/samples/snippets/job_search_create_job_custom_attributes.py b/samples/snippets/job_search_create_job_custom_attributes.py new file mode 100644 index 00000000..de8aa40e --- /dev/null +++ b/samples/snippets/job_search_create_job_custom_attributes.py @@ -0,0 +1,63 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_create_job_custom_attributes] + +from google.cloud import talent +import six + + +def create_job(project_id, tenant_id, company_id, requisition_id): + """Create Job with Custom Attributes""" + + client = talent.JobServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + # company_id = 'Company name, e.g. projects/your-project/companies/company-id' + # requisition_id = 'Job requisition ID, aka Posting ID. Unique per job.' + # language_code = 'en-US' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + if isinstance(company_id, six.binary_type): + company_id = company_id.decode("utf-8") + + # Custom attribute can be string or numeric value, + # and can be filtered in search queries. + # https://cloud.google.com/talent-solution/job-search/docs/custom-attributes + custom_attribute = talent.types.CustomAttribute() + custom_attribute.filterable = True + custom_attribute.string_values.append("Intern") + custom_attribute.string_values.append("Apprenticeship") + + parent = client.tenant_path(project_id, tenant_id) + + job = { + "company": company_id, + "title": "Software Engineer", + "requisition_id": requisition_id, + "description": "This is a description of this job", + "language_code": "en-US", + "custom_attributes": {"FOR_STUDENTS": custom_attribute}, + } + + response = client.create_job(parent, job) + print("Created job: {}".format(response.name)) + return response.name + + +# [END job_search_create_job_custom_attributes] diff --git a/samples/snippets/job_search_create_job_custom_attributes_test.py b/samples/snippets/job_search_create_job_custom_attributes_test.py new file mode 100644 index 00000000..028fedb7 --- /dev/null +++ b/samples/snippets/job_search_create_job_custom_attributes_test.py @@ -0,0 +1,46 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import uuid + +import pytest + +import job_search_create_job_custom_attributes +import job_search_delete_job + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] +JOB_EXT_UNIQUE_ID = "TEST_JOB_{}".format(uuid.uuid4()) + + +def test_create_job_with_attributes(capsys, tenant, company, cleaner): + job_name = job_search_create_job_custom_attributes.create_job( + PROJECT_ID, tenant, company, JOB_EXT_UNIQUE_ID + ) + out, _ = capsys.readouterr() + assert "Created job:" in out + + # extract job id + job_id = job_name.split("/")[-1] + cleaner.append(job_id) + + +@pytest.fixture(scope="module") +def cleaner(tenant): + jobs = [] + + yield jobs + + for job_id in jobs: + job_search_delete_job.delete_job(PROJECT_ID, tenant, job_id) diff --git a/samples/snippets/job_search_create_job_test.py b/samples/snippets/job_search_create_job_test.py new file mode 100644 index 00000000..505d27f6 --- /dev/null +++ b/samples/snippets/job_search_create_job_test.py @@ -0,0 +1,47 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import uuid + +import pytest + +import job_search_create_job +import job_search_delete_job + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] +JOB_EXT_UNIQUE_ID = "TEST_JOB_{}".format(uuid.uuid4()) + + +def test_create_job(capsys, tenant, company, cleaner): + # create a job + job_name = job_search_create_job.create_job( + PROJECT_ID, tenant, company, JOB_EXT_UNIQUE_ID, "www.example.com" + ) + out, _ = capsys.readouterr() + assert "Created job:" in out + + # extract job id + job_id = job_name.split("/")[-1] + cleaner.append(job_id) + + +@pytest.fixture(scope="module") +def cleaner(tenant): + jobs = [] + + yield jobs + + for job_id in jobs: + job_search_delete_job.delete_job(PROJECT_ID, tenant, job_id) diff --git a/samples/snippets/job_search_create_tenant.py b/samples/snippets/job_search_create_tenant.py new file mode 100644 index 00000000..8d4fde32 --- /dev/null +++ b/samples/snippets/job_search_create_tenant.py @@ -0,0 +1,43 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_create_tenant] + +from google.cloud import talent +import six + + +def create_tenant(project_id, external_id): + """Create Tenant for scoping resources, e.g. companies and jobs""" + + client = talent.TenantServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # external_id = 'Your Unique Identifier for Tenant' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(external_id, six.binary_type): + external_id = external_id.decode("utf-8") + parent = client.project_path(project_id) + tenant = {"external_id": external_id} + + response = client.create_tenant(parent, tenant) + print("Created Tenant") + print("Name: {}".format(response.name)) + print("External ID: {}".format(response.external_id)) + return response.name + + +# [END job_search_create_tenant] diff --git a/samples/snippets/job_search_create_tenant_test.py b/samples/snippets/job_search_create_tenant_test.py new file mode 100644 index 00000000..e8c9c049 --- /dev/null +++ b/samples/snippets/job_search_create_tenant_test.py @@ -0,0 +1,48 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import uuid + +import pytest + +import job_search_create_tenant +import job_search_delete_tenant + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] +TENANT_EXT_UNIQUE_ID = "TEST_TENANT_{}".format(uuid.uuid4()) + + +def test_create_tenant(capsys, cleaner): + # create tenant + tenant_name = job_search_create_tenant.create_tenant( + PROJECT_ID, TENANT_EXT_UNIQUE_ID + ) + out, _ = capsys.readouterr() + assert "Created Tenant" in out + assert "Name:" in out + + # extract tenant id + tenant_id = tenant_name.split("/")[-1] + cleaner.append(tenant_id) + + +@pytest.fixture(scope="module") +def cleaner(): + tenants = [] + + yield tenants + + for tenant_id in tenants: + job_search_delete_tenant.delete_tenant(PROJECT_ID, tenant_id) diff --git a/samples/snippets/job_search_custom_ranking_search.py b/samples/snippets/job_search_custom_ranking_search.py new file mode 100644 index 00000000..1b33aab3 --- /dev/null +++ b/samples/snippets/job_search_custom_ranking_search.py @@ -0,0 +1,64 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_custom_ranking_search] + +from google.cloud import talent +from google.cloud.talent import enums +import six + + +def search_jobs(project_id, tenant_id): + """Search Jobs using custom rankings""" + + client = talent.JobServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + parent = client.tenant_path(project_id, tenant_id) + domain = "www.example.com" + session_id = "Hashed session identifier" + user_id = "Hashed user identifier" + request_metadata = {"domain": domain, "session_id": session_id, "user_id": user_id} + importance_level = enums.SearchJobsRequest.CustomRankingInfo.ImportanceLevel.EXTREME + ranking_expression = "(someFieldLong + 25) * 0.25" + custom_ranking_info = { + "importance_level": importance_level, + "ranking_expression": ranking_expression, + } + order_by = "custom_ranking desc" + + # Iterate over all results + results = [] + for response_item in client.search_jobs( + parent, + request_metadata, + custom_ranking_info=custom_ranking_info, + order_by=order_by, + ): + print("Job summary: {}".format(response_item.job_summary)) + print("Job title snippet: {}".format(response_item.job_title_snippet)) + job = response_item.job + results.append(job.name) + print("Job name: {}".format(job.name)) + print("Job title: {}".format(job.title)) + return results + + +# [END job_search_custom_ranking_search] diff --git a/samples/snippets/job_search_custom_ranking_search_test.py b/samples/snippets/job_search_custom_ranking_search_test.py new file mode 100644 index 00000000..3a8e80bc --- /dev/null +++ b/samples/snippets/job_search_custom_ranking_search_test.py @@ -0,0 +1,25 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import job_search_custom_ranking_search + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] + + +def test_search_jobs_custom_ranking(tenant): + jobs = job_search_custom_ranking_search.search_jobs(PROJECT_ID, tenant) + for job in jobs: + assert "projects/" in job diff --git a/samples/snippets/job_search_delete_company.py b/samples/snippets/job_search_delete_company.py new file mode 100644 index 00000000..1eb1ed19 --- /dev/null +++ b/samples/snippets/job_search_delete_company.py @@ -0,0 +1,42 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_delete_company] + +from google.cloud import talent +import six + + +def delete_company(project_id, tenant_id, company_id): + """Delete Company""" + + client = talent.CompanyServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + # company_id = 'ID of the company to delete' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + if isinstance(company_id, six.binary_type): + company_id = company_id.decode("utf-8") + name = client.company_path(project_id, tenant_id, company_id) + + client.delete_company(name) + print("Deleted company") + + +# [END job_search_delete_company] diff --git a/samples/snippets/job_search_delete_company_test.py b/samples/snippets/job_search_delete_company_test.py new file mode 100644 index 00000000..f581935b --- /dev/null +++ b/samples/snippets/job_search_delete_company_test.py @@ -0,0 +1,27 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import job_search_delete_company + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] + + +def test_delete_company(capsys, tenant, company): + out, _ = capsys.readouterr() + + job_search_delete_company.delete_company(PROJECT_ID, tenant, company) + out, _ = capsys.readouterr() + assert "Deleted" in out diff --git a/samples/snippets/job_search_delete_job.py b/samples/snippets/job_search_delete_job.py new file mode 100644 index 00000000..f6dd0cf3 --- /dev/null +++ b/samples/snippets/job_search_delete_job.py @@ -0,0 +1,42 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_delete_job] + +from google.cloud import talent +import six + + +def delete_job(project_id, tenant_id, job_id): + """Delete Job""" + + client = talent.JobServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + # job_id = 'Company ID' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + if isinstance(job_id, six.binary_type): + job_id = job_id.decode("utf-8") + name = client.job_path(project_id, tenant_id, job_id) + + client.delete_job(name) + print("Deleted job.") + + +# [END job_search_delete_job] diff --git a/samples/snippets/job_search_delete_job_test.py b/samples/snippets/job_search_delete_job_test.py new file mode 100644 index 00000000..ef151a2b --- /dev/null +++ b/samples/snippets/job_search_delete_job_test.py @@ -0,0 +1,25 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import job_search_delete_job + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] + + +def test_delete_job(capsys, tenant, job): + job_search_delete_job.delete_job(PROJECT_ID, tenant, job) + out, _ = capsys.readouterr() + assert "Deleted" in out diff --git a/samples/snippets/job_search_delete_tenant.py b/samples/snippets/job_search_delete_tenant.py new file mode 100644 index 00000000..9d95498b --- /dev/null +++ b/samples/snippets/job_search_delete_tenant.py @@ -0,0 +1,39 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_delete_tenant] + +from google.cloud import talent +import six + + +def delete_tenant(project_id, tenant_id): + """Delete Tenant""" + + client = talent.TenantServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID)' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + name = client.tenant_path(project_id, tenant_id) + + client.delete_tenant(name) + print("Deleted Tenant.") + + +# [END job_search_delete_tenant] diff --git a/samples/snippets/job_search_delete_tenant_test.py b/samples/snippets/job_search_delete_tenant_test.py new file mode 100644 index 00000000..a2fc490b --- /dev/null +++ b/samples/snippets/job_search_delete_tenant_test.py @@ -0,0 +1,25 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import job_search_delete_tenant + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] + + +def test_delete_tenant(capsys, tenant): + job_search_delete_tenant.delete_tenant(PROJECT_ID, tenant) + out, _ = capsys.readouterr() + assert "Deleted" in out diff --git a/samples/snippets/job_search_get_company.py b/samples/snippets/job_search_get_company.py new file mode 100644 index 00000000..ceac6e89 --- /dev/null +++ b/samples/snippets/job_search_get_company.py @@ -0,0 +1,43 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_get_company] + +from google.cloud import talent +import six + + +def get_company(project_id, tenant_id, company_id): + """Get Company""" + + client = talent.CompanyServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + # company_id = 'Company ID' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + if isinstance(company_id, six.binary_type): + company_id = company_id.decode("utf-8") + name = client.company_path(project_id, tenant_id, company_id) + + response = client.get_company(name) + print("Company name: {}".format(response.name)) + print("Display name: {}".format(response.display_name)) + + +# [END job_search_get_company] diff --git a/samples/snippets/job_search_get_company_test.py b/samples/snippets/job_search_get_company_test.py new file mode 100644 index 00000000..f1126330 --- /dev/null +++ b/samples/snippets/job_search_get_company_test.py @@ -0,0 +1,25 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import job_search_get_company + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] + + +def test_job_search_get_company(capsys, tenant, company): + job_search_get_company.get_company(PROJECT_ID, tenant, company) + out, _ = capsys.readouterr() + assert "Company name:" in out diff --git a/samples/snippets/job_search_get_job.py b/samples/snippets/job_search_get_job.py new file mode 100644 index 00000000..f311202b --- /dev/null +++ b/samples/snippets/job_search_get_job.py @@ -0,0 +1,52 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_get_job] + +from google.cloud import talent +import six + + +def get_job(project_id, tenant_id, job_id): + """Get Job""" + + client = talent.JobServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + # job_id = 'Job ID' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + if isinstance(job_id, six.binary_type): + job_id = job_id.decode("utf-8") + name = client.job_path(project_id, tenant_id, job_id) + + response = client.get_job(name) + print("Job name: {}".format(response.name)) + print("Requisition ID: {}".format(response.requisition_id)) + print("Title: {}".format(response.title)) + print("Description: {}".format(response.description)) + print("Posting language: {}".format(response.language_code)) + for address in response.addresses: + print("Address: {}".format(address)) + for email in response.application_info.emails: + print("Email: {}".format(email)) + for website_uri in response.application_info.uris: + print("Website: {}".format(website_uri)) + + +# [END job_search_get_job] diff --git a/samples/snippets/job_search_get_job_test.py b/samples/snippets/job_search_get_job_test.py new file mode 100644 index 00000000..264c5772 --- /dev/null +++ b/samples/snippets/job_search_get_job_test.py @@ -0,0 +1,25 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import job_search_get_job + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] + + +def test_job_search_get_job(capsys, tenant, job): + job_search_get_job.get_job(PROJECT_ID, tenant, job) + out, _ = capsys.readouterr() + assert "Job name:" in out diff --git a/samples/snippets/job_search_get_tenant.py b/samples/snippets/job_search_get_tenant.py new file mode 100644 index 00000000..484c44d6 --- /dev/null +++ b/samples/snippets/job_search_get_tenant.py @@ -0,0 +1,40 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_get_tenant] + +from google.cloud import talent +import six + + +def get_tenant(project_id, tenant_id): + """Get Tenant by name""" + + client = talent.TenantServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + name = client.tenant_path(project_id, tenant_id) + + response = client.get_tenant(name) + print("Name: {}".format(response.name)) + print("External ID: {}".format(response.external_id)) + + +# [END job_search_get_tenant] diff --git a/samples/snippets/job_search_get_tenant_test.py b/samples/snippets/job_search_get_tenant_test.py new file mode 100644 index 00000000..d1f8c1cd --- /dev/null +++ b/samples/snippets/job_search_get_tenant_test.py @@ -0,0 +1,26 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os + +import job_search_get_tenant + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] + + +def test_get_tenant(capsys, tenant): + job_search_get_tenant.get_tenant(PROJECT_ID, tenant) + out, _ = capsys.readouterr() + assert "Name: " in out diff --git a/samples/snippets/job_search_histogram_search.py b/samples/snippets/job_search_histogram_search.py new file mode 100644 index 00000000..f238e6a6 --- /dev/null +++ b/samples/snippets/job_search_histogram_search.py @@ -0,0 +1,65 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_histogram_search] + +from google.cloud import talent +import six + + +def search_jobs(project_id, tenant_id, query): + """ + Search Jobs with histogram queries + + Args: + query Histogram query + More info on histogram facets, constants, and built-in functions: + https://godoc.org/google.golang.org/genproto/googleapis/cloud/talent/v4beta1#SearchJobsRequest + """ + + client = talent.JobServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + # query = 'count(base_compensation, [bucket(12, 20)])' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + if isinstance(query, six.binary_type): + query = query.decode("utf-8") + parent = client.tenant_path(project_id, tenant_id) + domain = "www.example.com" + session_id = "Hashed session identifier" + user_id = "Hashed user identifier" + request_metadata = {"domain": domain, "session_id": session_id, "user_id": user_id} + histogram_queries_element = {"histogram_query": query} + histogram_queries = [histogram_queries_element] + + # Iterate over all results + results = [] + for response_item in client.search_jobs( + parent, request_metadata, histogram_queries=histogram_queries + ): + print("Job summary: {}".format(response_item.job_summary)) + print("Job title snippet: {}".format(response_item.job_title_snippet)) + job = response_item.job + results.append(job) + print("Job name: {}".format(job.name)) + print("Job title: {}".format(job.title)) + return results + + +# [END job_search_histogram_search] diff --git a/samples/snippets/job_search_histogram_search_test.py b/samples/snippets/job_search_histogram_search_test.py new file mode 100644 index 00000000..1f6594de --- /dev/null +++ b/samples/snippets/job_search_histogram_search_test.py @@ -0,0 +1,26 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import job_search_histogram_search + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] + + +def test_search_jobs_histogram(tenant): + query = "count(base_compensation, [bucket(12, 20)])" + jobs = job_search_histogram_search.search_jobs(PROJECT_ID, tenant, query) + for job in jobs: + assert "projects/" in job diff --git a/samples/snippets/job_search_list_companies.py b/samples/snippets/job_search_list_companies.py new file mode 100644 index 00000000..bdef589c --- /dev/null +++ b/samples/snippets/job_search_list_companies.py @@ -0,0 +1,45 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_list_companies] + +from google.cloud import talent +import six + + +def list_companies(project_id, tenant_id): + """List Companies""" + + client = talent.CompanyServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + parent = client.tenant_path(project_id, tenant_id) + + # Iterate over all results + results = [] + for company in client.list_companies(parent): + results.append(company.name) + print("Company Name: {}".format(company.name)) + print("Display Name: {}".format(company.display_name)) + print("External ID: {}".format(company.external_id)) + return results + + +# [END job_search_list_companies] diff --git a/samples/snippets/job_search_list_companies_test.py b/samples/snippets/job_search_list_companies_test.py new file mode 100644 index 00000000..c8d71a85 --- /dev/null +++ b/samples/snippets/job_search_list_companies_test.py @@ -0,0 +1,25 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import job_search_list_companies + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] + + +def test_list_companies(tenant): + results = job_search_list_companies.list_companies(PROJECT_ID, tenant) + for company in results: + assert "projects/" in company.name diff --git a/samples/snippets/job_search_list_jobs.py b/samples/snippets/job_search_list_jobs.py new file mode 100644 index 00000000..0ae52dc1 --- /dev/null +++ b/samples/snippets/job_search_list_jobs.py @@ -0,0 +1,56 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_list_jobs] + +from google.cloud import talent +import six + + +def list_jobs(project_id, tenant_id, filter_): + """List Jobs""" + + client = talent.JobServiceClient() + + # project_id = 'Your Google Cloud Project ID' + # tenant_id = 'Your Tenant ID (using tenancy is optional)' + # filter_ = 'companyName=projects/my-project/companies/company-id' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + if isinstance(tenant_id, six.binary_type): + tenant_id = tenant_id.decode("utf-8") + if isinstance(filter_, six.binary_type): + filter_ = filter_.decode("utf-8") + parent = client.tenant_path(project_id, tenant_id) + + # Iterate over all results + results = [] + for job in client.list_jobs(parent, filter_): + results.append(job.name) + print("Job name: {}".format(job.name)) + print("Job requisition ID: {}".format(job.requisition_id)) + print("Job title: {}".format(job.title)) + print("Job description: {}".format(job.description)) + return results + + +# [END job_search_list_jobs] +list_jobs( + "python-docs-samples-tests", + "b603d325-3fb5-4979-8994-eba4ecf726f4", + 'companyName="projects/{}/companies/{}"'.format( + "python-docs-samples-tests", "4c0b9887-8f69-429b-bc67-a072ef55ec3e" + ), +) diff --git a/samples/snippets/job_search_list_jobs_test.py b/samples/snippets/job_search_list_jobs_test.py new file mode 100644 index 00000000..a2187fd9 --- /dev/null +++ b/samples/snippets/job_search_list_jobs_test.py @@ -0,0 +1,26 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import job_search_list_jobs + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] + + +def test_list_jobs(capsys, tenant, company): + filter = 'companyName="projects/{}/companies/{}"'.format(PROJECT_ID, company) + jobs = job_search_list_jobs.list_jobs(PROJECT_ID, tenant, filter) + for job in jobs: + assert "projects/" in job diff --git a/samples/snippets/job_search_list_tenants.py b/samples/snippets/job_search_list_tenants.py new file mode 100644 index 00000000..2045bcca --- /dev/null +++ b/samples/snippets/job_search_list_tenants.py @@ -0,0 +1,38 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START job_search_list_tenants] + +from google.cloud import talent +import six + + +def list_tenants(project_id): + """List Tenants""" + + client = talent.TenantServiceClient() + + # project_id = 'Your Google Cloud Project ID' + + if isinstance(project_id, six.binary_type): + project_id = project_id.decode("utf-8") + parent = client.project_path(project_id) + + # Iterate over all results + for response_item in client.list_tenants(parent): + print("Tenant Name: {}".format(response_item.name)) + print("External ID: {}".format(response_item.external_id)) + + +# [END job_search_list_tenants] diff --git a/samples/snippets/job_search_list_tenants_test.py b/samples/snippets/job_search_list_tenants_test.py new file mode 100644 index 00000000..38d840d5 --- /dev/null +++ b/samples/snippets/job_search_list_tenants_test.py @@ -0,0 +1,25 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import job_search_list_tenants + +PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] + + +def test_list_tenants(capsys): + job_search_list_tenants.list_tenants(PROJECT_ID) + out, _ = capsys.readouterr() + assert "Tenant Name:" in out diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt new file mode 100644 index 00000000..10b405fa --- /dev/null +++ b/samples/snippets/requirements-test.txt @@ -0,0 +1 @@ +pytest==5.4.2 \ No newline at end of file diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt new file mode 100755 index 00000000..d2d6347f --- /dev/null +++ b/samples/snippets/requirements.txt @@ -0,0 +1 @@ +google.cloud.talent==0.6.0 \ No newline at end of file From ce7452e1e15937e1f89881a24e26b28b6687202e Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Thu, 21 May 2020 19:53:05 +0200 Subject: [PATCH 02/12] chore(deps): update dependency google.cloud.talent to v0.6.1 [(#3851)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/3851) --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index d2d6347f..9db6aba9 100755 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1 +1 @@ -google.cloud.talent==0.6.0 \ No newline at end of file +google.cloud.talent==0.6.1 \ No newline at end of file From 1803e94fae60e83cd648ea29b46d4863f957a91a Mon Sep 17 00:00:00 2001 From: Takashi Matsuo Date: Fri, 12 Jun 2020 11:54:07 -0700 Subject: [PATCH 03/12] [jobs] testing: use multiple projects [(#4068)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/4068) * [jobs] testing: use multiple projects * added README --- samples/snippets/README.rst | 6 +++ .../snippets/job_search_list_tenants_test.py | 19 +++++++++- samples/snippets/noxfile_config.py | 37 +++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 samples/snippets/README.rst create mode 100644 samples/snippets/noxfile_config.py diff --git a/samples/snippets/README.rst b/samples/snippets/README.rst new file mode 100644 index 00000000..a7bfef3c --- /dev/null +++ b/samples/snippets/README.rst @@ -0,0 +1,6 @@ +To run the sample, you need to enable the API at: https://console.cloud.google.com/apis/library/jobs.googleapis.com + +To run the sample, you need to have the following roles: + +* `Talent Solution Job Editor` +* `Talent Solution Profile Editor` diff --git a/samples/snippets/job_search_list_tenants_test.py b/samples/snippets/job_search_list_tenants_test.py index 38d840d5..03f2fce7 100644 --- a/samples/snippets/job_search_list_tenants_test.py +++ b/samples/snippets/job_search_list_tenants_test.py @@ -13,13 +13,30 @@ # limitations under the License. import os +import uuid + +from google.cloud import talent +import pytest import job_search_list_tenants PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"] -def test_list_tenants(capsys): +@pytest.fixture(scope="module") +def test_tenant(): + client = talent.TenantServiceClient() + external_id = f'test_tenant_{uuid.uuid4().hex}' + parent = client.project_path(PROJECT_ID) + tenant = {"external_id": external_id} + resp = client.create_tenant(parent, tenant) + + yield resp + + client.delete_tenant(resp.name) + + +def test_list_tenants(capsys, test_tenant): job_search_list_tenants.list_tenants(PROJECT_ID) out, _ = capsys.readouterr() assert "Tenant Name:" in out diff --git a/samples/snippets/noxfile_config.py b/samples/snippets/noxfile_config.py new file mode 100644 index 00000000..cfd0d439 --- /dev/null +++ b/samples/snippets/noxfile_config.py @@ -0,0 +1,37 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Default TEST_CONFIG_OVERRIDE for python repos. + +# You can copy this file into your directory, then it will be inported from +# the noxfile.py. + +# The source of truth: +# https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/noxfile_config.py + +TEST_CONFIG_OVERRIDE = { + # You can opt out from the test for specific Python versions. + 'ignored_versions': ["2.7"], + + # An envvar key for determining the project id to use. Change it + # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a + # build specific Cloud project. You can also use your own string + # to use your own Cloud project. + # 'gcloud_project_env': 'GOOGLE_CLOUD_PROJECT', + 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + 'envs': {}, +} From 93427f91bf700708b44dd5d4b1dba18026d915a6 Mon Sep 17 00:00:00 2001 From: Takashi Matsuo Date: Wed, 8 Jul 2020 16:12:35 -0700 Subject: [PATCH 04/12] cleanup(jobs): remove unnecessary code [(#4257)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/4257) Co-authored-by: Leah E. Cole <6719667+leahecole@users.noreply.github.com> --- samples/snippets/job_search_list_jobs.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/samples/snippets/job_search_list_jobs.py b/samples/snippets/job_search_list_jobs.py index 0ae52dc1..f31884dc 100644 --- a/samples/snippets/job_search_list_jobs.py +++ b/samples/snippets/job_search_list_jobs.py @@ -47,10 +47,3 @@ def list_jobs(project_id, tenant_id, filter_): # [END job_search_list_jobs] -list_jobs( - "python-docs-samples-tests", - "b603d325-3fb5-4979-8994-eba4ecf726f4", - 'companyName="projects/{}/companies/{}"'.format( - "python-docs-samples-tests", "4c0b9887-8f69-429b-bc67-a072ef55ec3e" - ), -) From 001bdd75e894b8345ef3a57bd58630c2346fd197 Mon Sep 17 00:00:00 2001 From: Mike <45373284+munkhuushmgl@users.noreply.github.com> Date: Thu, 9 Jul 2020 10:36:53 -0700 Subject: [PATCH 05/12] samples: Automl table batch test [(#4267)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/4267) * added rtest req.txt * samples: added automl batch predict test * added missing package * Update tables/automl/batch_predict_test.py Co-authored-by: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Co-authored-by: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> --- samples/snippets/requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 10b405fa..bbef4fe1 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1 +1 @@ -pytest==5.4.2 \ No newline at end of file +pytest==5.4.2 From 49b7ea95ca2b1f534b9abbccd98b712b8fc1c7d8 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Mon, 13 Jul 2020 00:46:30 +0200 Subject: [PATCH 06/12] chore(deps): update dependency pytest to v5.4.3 [(#4279)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/4279) * chore(deps): update dependency pytest to v5.4.3 * specify pytest for python 2 in appengine Co-authored-by: Leah Cole --- samples/snippets/requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index bbef4fe1..79738af5 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1 +1 @@ -pytest==5.4.2 +pytest==5.4.3 From 534aaa1da28ebabad8e73c1337dbf8de4612e60d Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Sat, 1 Aug 2020 21:51:00 +0200 Subject: [PATCH 07/12] Update dependency pytest to v6 [(#4390)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/4390) --- samples/snippets/requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 79738af5..7e460c8c 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1 +1 @@ -pytest==5.4.3 +pytest==6.0.1 From 2a4b373ae8d189cb37b8f8017b595e669c713147 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 24 Sep 2020 22:15:37 +0000 Subject: [PATCH 08/12] chore: update templates --- .github/snippet-bot.yml | 0 .gitignore | 3 +- .kokoro/build.sh | 8 +- .kokoro/docker/docs/Dockerfile | 98 +++++ .kokoro/docker/docs/fetch_gpg_keys.sh | 45 +++ .kokoro/docs/common.cfg | 21 +- .kokoro/docs/docs-presubmit.cfg | 17 + .kokoro/populate-secrets.sh | 43 ++ .kokoro/publish-docs.sh | 39 +- .kokoro/release/common.cfg | 50 +-- .kokoro/trampoline.sh | 15 +- .kokoro/trampoline_v2.sh | 487 +++++++++++++++++++++++ .trampolinerc | 51 +++ CONTRIBUTING.rst | 19 - docs/conf.py | 14 +- noxfile.py | 39 ++ samples/snippets/noxfile.py | 224 +++++++++++ scripts/decrypt-secrets.sh | 15 +- scripts/fixup_talent_v4beta1_keywords.py | 2 +- synth.metadata | 19 +- synth.py | 9 +- 21 files changed, 1131 insertions(+), 87 deletions(-) create mode 100644 .github/snippet-bot.yml create mode 100644 .kokoro/docker/docs/Dockerfile create mode 100755 .kokoro/docker/docs/fetch_gpg_keys.sh create mode 100644 .kokoro/docs/docs-presubmit.cfg create mode 100755 .kokoro/populate-secrets.sh create mode 100755 .kokoro/trampoline_v2.sh create mode 100644 .trampolinerc create mode 100644 samples/snippets/noxfile.py diff --git a/.github/snippet-bot.yml b/.github/snippet-bot.yml new file mode 100644 index 00000000..e69de29b diff --git a/.gitignore b/.gitignore index b87e1ed5..b9daa52f 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ pip-log.txt # Built documentation docs/_build bigquery/docs/generated +docs.metadata # Virtual environment env/ @@ -57,4 +58,4 @@ system_tests/local_test_setup # Make sure a generated file isn't accidentally committed. pylintrc -pylintrc.test \ No newline at end of file +pylintrc.test diff --git a/.kokoro/build.sh b/.kokoro/build.sh index 33b38dc6..291a09e9 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -36,4 +36,10 @@ python3.6 -m pip uninstall --yes --quiet nox-automation python3.6 -m pip install --upgrade --quiet nox python3.6 -m nox --version -python3.6 -m nox +# If NOX_SESSION is set, it only runs the specified session, +# otherwise run all the sessions. +if [[ -n "${NOX_SESSION:-}" ]]; then + python3.6 -m nox -s "${NOX_SESSION:-}" +else + python3.6 -m nox +fi diff --git a/.kokoro/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile new file mode 100644 index 00000000..412b0b56 --- /dev/null +++ b/.kokoro/docker/docs/Dockerfile @@ -0,0 +1,98 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ubuntu:20.04 + +ENV DEBIAN_FRONTEND noninteractive + +# Ensure local Python is preferred over distribution Python. +ENV PATH /usr/local/bin:$PATH + +# Install dependencies. +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + apt-transport-https \ + build-essential \ + ca-certificates \ + curl \ + dirmngr \ + git \ + gpg-agent \ + graphviz \ + libbz2-dev \ + libdb5.3-dev \ + libexpat1-dev \ + libffi-dev \ + liblzma-dev \ + libreadline-dev \ + libsnappy-dev \ + libssl-dev \ + libsqlite3-dev \ + portaudio19-dev \ + redis-server \ + software-properties-common \ + ssh \ + sudo \ + tcl \ + tcl-dev \ + tk \ + tk-dev \ + uuid-dev \ + wget \ + zlib1g-dev \ + && add-apt-repository universe \ + && apt-get update \ + && apt-get -y install jq \ + && apt-get clean autoclean \ + && apt-get autoremove -y \ + && rm -rf /var/lib/apt/lists/* \ + && rm -f /var/cache/apt/archives/*.deb + + +COPY fetch_gpg_keys.sh /tmp +# Install the desired versions of Python. +RUN set -ex \ + && export GNUPGHOME="$(mktemp -d)" \ + && echo "disable-ipv6" >> "${GNUPGHOME}/dirmngr.conf" \ + && /tmp/fetch_gpg_keys.sh \ + && for PYTHON_VERSION in 3.7.8 3.8.5; do \ + wget --no-check-certificate -O python-${PYTHON_VERSION}.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz" \ + && wget --no-check-certificate -O python-${PYTHON_VERSION}.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc" \ + && gpg --batch --verify python-${PYTHON_VERSION}.tar.xz.asc python-${PYTHON_VERSION}.tar.xz \ + && rm -r python-${PYTHON_VERSION}.tar.xz.asc \ + && mkdir -p /usr/src/python-${PYTHON_VERSION} \ + && tar -xJC /usr/src/python-${PYTHON_VERSION} --strip-components=1 -f python-${PYTHON_VERSION}.tar.xz \ + && rm python-${PYTHON_VERSION}.tar.xz \ + && cd /usr/src/python-${PYTHON_VERSION} \ + && ./configure \ + --enable-shared \ + # This works only on Python 2.7 and throws a warning on every other + # version, but seems otherwise harmless. + --enable-unicode=ucs4 \ + --with-system-ffi \ + --without-ensurepip \ + && make -j$(nproc) \ + && make install \ + && ldconfig \ + ; done \ + && rm -rf "${GNUPGHOME}" \ + && rm -rf /usr/src/python* \ + && rm -rf ~/.cache/ + +RUN wget -O /tmp/get-pip.py 'https://bootstrap.pypa.io/get-pip.py' \ + && python3.7 /tmp/get-pip.py \ + && python3.8 /tmp/get-pip.py \ + && rm /tmp/get-pip.py + +CMD ["python3.7"] diff --git a/.kokoro/docker/docs/fetch_gpg_keys.sh b/.kokoro/docker/docs/fetch_gpg_keys.sh new file mode 100755 index 00000000..d653dd86 --- /dev/null +++ b/.kokoro/docker/docs/fetch_gpg_keys.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# A script to fetch gpg keys with retry. +# Avoid jinja parsing the file. +# + +function retry { + if [[ "${#}" -le 1 ]]; then + echo "Usage: ${0} retry_count commands.." + exit 1 + fi + local retries=${1} + local command="${@:2}" + until [[ "${retries}" -le 0 ]]; do + $command && return 0 + if [[ $? -ne 0 ]]; then + echo "command failed, retrying" + ((retries--)) + fi + done + return 1 +} + +# 3.6.9, 3.7.5 (Ned Deily) +retry 3 gpg --keyserver ha.pool.sks-keyservers.net --recv-keys \ + 0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D + +# 3.8.0 (Łukasz Langa) +retry 3 gpg --keyserver ha.pool.sks-keyservers.net --recv-keys \ + E3FF2839C048B25C084DEBE9B26995E310250568 + +# diff --git a/.kokoro/docs/common.cfg b/.kokoro/docs/common.cfg index 7a9e8085..8cd3aaa0 100644 --- a/.kokoro/docs/common.cfg +++ b/.kokoro/docs/common.cfg @@ -11,12 +11,12 @@ action { gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" # Use the trampoline script to run in docker. -build_file: "python-talent/.kokoro/trampoline.sh" +build_file: "python-talent/.kokoro/trampoline_v2.sh" # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-multi" + value: "gcr.io/cloud-devrel-kokoro-resources/python-lib-docs" } env_vars: { key: "TRAMPOLINE_BUILD_FILE" @@ -28,6 +28,23 @@ env_vars: { value: "docs-staging" } +env_vars: { + key: "V2_STAGING_BUCKET" + value: "docs-staging-v2-staging" +} + +# It will upload the docker image after successful builds. +env_vars: { + key: "TRAMPOLINE_IMAGE_UPLOAD" + value: "true" +} + +# It will always build the docker image. +env_vars: { + key: "TRAMPOLINE_DOCKERFILE" + value: ".kokoro/docker/docs/Dockerfile" +} + # Fetch the token needed for reporting release status to GitHub before_action { fetch_keystore { diff --git a/.kokoro/docs/docs-presubmit.cfg b/.kokoro/docs/docs-presubmit.cfg new file mode 100644 index 00000000..11181078 --- /dev/null +++ b/.kokoro/docs/docs-presubmit.cfg @@ -0,0 +1,17 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "STAGING_BUCKET" + value: "gcloud-python-test" +} + +env_vars: { + key: "V2_STAGING_BUCKET" + value: "gcloud-python-test" +} + +# We only upload the image in the main `docs` build. +env_vars: { + key: "TRAMPOLINE_IMAGE_UPLOAD" + value: "false" +} diff --git a/.kokoro/populate-secrets.sh b/.kokoro/populate-secrets.sh new file mode 100755 index 00000000..f5251425 --- /dev/null +++ b/.kokoro/populate-secrets.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# Copyright 2020 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -eo pipefail + +function now { date +"%Y-%m-%d %H:%M:%S" | tr -d '\n' ;} +function msg { println "$*" >&2 ;} +function println { printf '%s\n' "$(now) $*" ;} + + +# Populates requested secrets set in SECRET_MANAGER_KEYS from service account: +# kokoro-trampoline@cloud-devrel-kokoro-resources.iam.gserviceaccount.com +SECRET_LOCATION="${KOKORO_GFILE_DIR}/secret_manager" +msg "Creating folder on disk for secrets: ${SECRET_LOCATION}" +mkdir -p ${SECRET_LOCATION} +for key in $(echo ${SECRET_MANAGER_KEYS} | sed "s/,/ /g") +do + msg "Retrieving secret ${key}" + docker run --entrypoint=gcloud \ + --volume=${KOKORO_GFILE_DIR}:${KOKORO_GFILE_DIR} \ + gcr.io/google.com/cloudsdktool/cloud-sdk \ + secrets versions access latest \ + --project cloud-devrel-kokoro-resources \ + --secret ${key} > \ + "${SECRET_LOCATION}/${key}" + if [[ $? == 0 ]]; then + msg "Secret written to ${SECRET_LOCATION}/${key}" + else + msg "Error retrieving secret ${key}" + fi +done diff --git a/.kokoro/publish-docs.sh b/.kokoro/publish-docs.sh index 3cacc818..8acb14e8 100755 --- a/.kokoro/publish-docs.sh +++ b/.kokoro/publish-docs.sh @@ -18,26 +18,16 @@ set -eo pipefail # Disable buffering, so that the logs stream through. export PYTHONUNBUFFERED=1 -cd github/python-talent - -# Remove old nox -python3.6 -m pip uninstall --yes --quiet nox-automation +export PATH="${HOME}/.local/bin:${PATH}" # Install nox -python3.6 -m pip install --upgrade --quiet nox -python3.6 -m nox --version +python3 -m pip install --user --upgrade --quiet nox +python3 -m nox --version # build docs nox -s docs -python3 -m pip install gcp-docuploader - -# install a json parser -sudo apt-get update -sudo apt-get -y install software-properties-common -sudo add-apt-repository universe -sudo apt-get update -sudo apt-get -y install jq +python3 -m pip install --user gcp-docuploader # create metadata python3 -m docuploader create-metadata \ @@ -52,4 +42,23 @@ python3 -m docuploader create-metadata \ cat docs.metadata # upload docs -python3 -m docuploader upload docs/_build/html --metadata-file docs.metadata --staging-bucket docs-staging +python3 -m docuploader upload docs/_build/html --metadata-file docs.metadata --staging-bucket "${STAGING_BUCKET}" + + +# docfx yaml files +nox -s docfx + +# create metadata. +python3 -m docuploader create-metadata \ + --name=$(jq --raw-output '.name // empty' .repo-metadata.json) \ + --version=$(python3 setup.py --version) \ + --language=$(jq --raw-output '.language // empty' .repo-metadata.json) \ + --distribution-name=$(python3 setup.py --name) \ + --product-page=$(jq --raw-output '.product_documentation // empty' .repo-metadata.json) \ + --github-repository=$(jq --raw-output '.repo // empty' .repo-metadata.json) \ + --issue-tracker=$(jq --raw-output '.issue_tracker // empty' .repo-metadata.json) + +cat docs.metadata + +# upload docs +python3 -m docuploader upload docs/_build/html/docfx_yaml --metadata-file docs.metadata --destination-prefix docfx --staging-bucket "${V2_STAGING_BUCKET}" diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg index 21177fa5..5dd54678 100644 --- a/.kokoro/release/common.cfg +++ b/.kokoro/release/common.cfg @@ -23,42 +23,18 @@ env_vars: { value: "github/python-talent/.kokoro/release.sh" } -# Fetch the token needed for reporting release status to GitHub -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "yoshi-automation-github-key" - } - } -} - -# Fetch PyPI password -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "google_cloud_pypi_password" - } - } -} - -# Fetch magictoken to use with Magic Github Proxy -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "releasetool-magictoken" - } - } +# Fetch PyPI password +before_action { + fetch_keystore { + keystore_resource { + keystore_config_id: 73713 + keyname: "google_cloud_pypi_password" + } + } } -# Fetch api key to use with Magic Github Proxy -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "magic-github-proxy-api-key" - } - } -} +# Tokens needed to report release status back to GitHub +env_vars: { + key: "SECRET_MANAGER_KEYS" + value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem" +} \ No newline at end of file diff --git a/.kokoro/trampoline.sh b/.kokoro/trampoline.sh index e8c4251f..f39236e9 100755 --- a/.kokoro/trampoline.sh +++ b/.kokoro/trampoline.sh @@ -15,9 +15,14 @@ set -eo pipefail -python3 "${KOKORO_GFILE_DIR}/trampoline_v1.py" || ret_code=$? +# Always run the cleanup script, regardless of the success of bouncing into +# the container. +function cleanup() { + chmod +x ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh + ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh + echo "cleanup"; +} +trap cleanup EXIT -chmod +x ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh -${KOKORO_GFILE_DIR}/trampoline_cleanup.sh || true - -exit ${ret_code} +$(dirname $0)/populate-secrets.sh # Secret Manager secrets. +python3 "${KOKORO_GFILE_DIR}/trampoline_v1.py" \ No newline at end of file diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh new file mode 100755 index 00000000..719bcd5b --- /dev/null +++ b/.kokoro/trampoline_v2.sh @@ -0,0 +1,487 @@ +#!/usr/bin/env bash +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# trampoline_v2.sh +# +# This script does 3 things. +# +# 1. Prepare the Docker image for the test +# 2. Run the Docker with appropriate flags to run the test +# 3. Upload the newly built Docker image +# +# in a way that is somewhat compatible with trampoline_v1. +# +# To run this script, first download few files from gcs to /dev/shm. +# (/dev/shm is passed into the container as KOKORO_GFILE_DIR). +# +# gsutil cp gs://cloud-devrel-kokoro-resources/python-docs-samples/secrets_viewer_service_account.json /dev/shm +# gsutil cp gs://cloud-devrel-kokoro-resources/python-docs-samples/automl_secrets.txt /dev/shm +# +# Then run the script. +# .kokoro/trampoline_v2.sh +# +# These environment variables are required: +# TRAMPOLINE_IMAGE: The docker image to use. +# TRAMPOLINE_DOCKERFILE: The location of the Dockerfile. +# +# You can optionally change these environment variables: +# TRAMPOLINE_IMAGE_UPLOAD: +# (true|false): Whether to upload the Docker image after the +# successful builds. +# TRAMPOLINE_BUILD_FILE: The script to run in the docker container. +# TRAMPOLINE_WORKSPACE: The workspace path in the docker container. +# Defaults to /workspace. +# Potentially there are some repo specific envvars in .trampolinerc in +# the project root. + + +set -euo pipefail + +TRAMPOLINE_VERSION="2.0.5" + +if command -v tput >/dev/null && [[ -n "${TERM:-}" ]]; then + readonly IO_COLOR_RED="$(tput setaf 1)" + readonly IO_COLOR_GREEN="$(tput setaf 2)" + readonly IO_COLOR_YELLOW="$(tput setaf 3)" + readonly IO_COLOR_RESET="$(tput sgr0)" +else + readonly IO_COLOR_RED="" + readonly IO_COLOR_GREEN="" + readonly IO_COLOR_YELLOW="" + readonly IO_COLOR_RESET="" +fi + +function function_exists { + [ $(LC_ALL=C type -t $1)"" == "function" ] +} + +# Logs a message using the given color. The first argument must be one +# of the IO_COLOR_* variables defined above, such as +# "${IO_COLOR_YELLOW}". The remaining arguments will be logged in the +# given color. The log message will also have an RFC-3339 timestamp +# prepended (in UTC). You can disable the color output by setting +# TERM=vt100. +function log_impl() { + local color="$1" + shift + local timestamp="$(date -u "+%Y-%m-%dT%H:%M:%SZ")" + echo "================================================================" + echo "${color}${timestamp}:" "$@" "${IO_COLOR_RESET}" + echo "================================================================" +} + +# Logs the given message with normal coloring and a timestamp. +function log() { + log_impl "${IO_COLOR_RESET}" "$@" +} + +# Logs the given message in green with a timestamp. +function log_green() { + log_impl "${IO_COLOR_GREEN}" "$@" +} + +# Logs the given message in yellow with a timestamp. +function log_yellow() { + log_impl "${IO_COLOR_YELLOW}" "$@" +} + +# Logs the given message in red with a timestamp. +function log_red() { + log_impl "${IO_COLOR_RED}" "$@" +} + +readonly tmpdir=$(mktemp -d -t ci-XXXXXXXX) +readonly tmphome="${tmpdir}/h" +mkdir -p "${tmphome}" + +function cleanup() { + rm -rf "${tmpdir}" +} +trap cleanup EXIT + +RUNNING_IN_CI="${RUNNING_IN_CI:-false}" + +# The workspace in the container, defaults to /workspace. +TRAMPOLINE_WORKSPACE="${TRAMPOLINE_WORKSPACE:-/workspace}" + +pass_down_envvars=( + # TRAMPOLINE_V2 variables. + # Tells scripts whether they are running as part of CI or not. + "RUNNING_IN_CI" + # Indicates which CI system we're in. + "TRAMPOLINE_CI" + # Indicates the version of the script. + "TRAMPOLINE_VERSION" +) + +log_yellow "Building with Trampoline ${TRAMPOLINE_VERSION}" + +# Detect which CI systems we're in. If we're in any of the CI systems +# we support, `RUNNING_IN_CI` will be true and `TRAMPOLINE_CI` will be +# the name of the CI system. Both envvars will be passing down to the +# container for telling which CI system we're in. +if [[ -n "${KOKORO_BUILD_ID:-}" ]]; then + # descriptive env var for indicating it's on CI. + RUNNING_IN_CI="true" + TRAMPOLINE_CI="kokoro" + if [[ "${TRAMPOLINE_USE_LEGACY_SERVICE_ACCOUNT:-}" == "true" ]]; then + if [[ ! -f "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json" ]]; then + log_red "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json does not exist. Did you forget to mount cloud-devrel-kokoro-resources/trampoline? Aborting." + exit 1 + fi + # This service account will be activated later. + TRAMPOLINE_SERVICE_ACCOUNT="${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json" + else + if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then + gcloud auth list + fi + log_yellow "Configuring Container Registry access" + gcloud auth configure-docker --quiet + fi + pass_down_envvars+=( + # KOKORO dynamic variables. + "KOKORO_BUILD_NUMBER" + "KOKORO_BUILD_ID" + "KOKORO_JOB_NAME" + "KOKORO_GIT_COMMIT" + "KOKORO_GITHUB_COMMIT" + "KOKORO_GITHUB_PULL_REQUEST_NUMBER" + "KOKORO_GITHUB_PULL_REQUEST_COMMIT" + # For Build Cop Bot + "KOKORO_GITHUB_COMMIT_URL" + "KOKORO_GITHUB_PULL_REQUEST_URL" + ) +elif [[ "${TRAVIS:-}" == "true" ]]; then + RUNNING_IN_CI="true" + TRAMPOLINE_CI="travis" + pass_down_envvars+=( + "TRAVIS_BRANCH" + "TRAVIS_BUILD_ID" + "TRAVIS_BUILD_NUMBER" + "TRAVIS_BUILD_WEB_URL" + "TRAVIS_COMMIT" + "TRAVIS_COMMIT_MESSAGE" + "TRAVIS_COMMIT_RANGE" + "TRAVIS_JOB_NAME" + "TRAVIS_JOB_NUMBER" + "TRAVIS_JOB_WEB_URL" + "TRAVIS_PULL_REQUEST" + "TRAVIS_PULL_REQUEST_BRANCH" + "TRAVIS_PULL_REQUEST_SHA" + "TRAVIS_PULL_REQUEST_SLUG" + "TRAVIS_REPO_SLUG" + "TRAVIS_SECURE_ENV_VARS" + "TRAVIS_TAG" + ) +elif [[ -n "${GITHUB_RUN_ID:-}" ]]; then + RUNNING_IN_CI="true" + TRAMPOLINE_CI="github-workflow" + pass_down_envvars+=( + "GITHUB_WORKFLOW" + "GITHUB_RUN_ID" + "GITHUB_RUN_NUMBER" + "GITHUB_ACTION" + "GITHUB_ACTIONS" + "GITHUB_ACTOR" + "GITHUB_REPOSITORY" + "GITHUB_EVENT_NAME" + "GITHUB_EVENT_PATH" + "GITHUB_SHA" + "GITHUB_REF" + "GITHUB_HEAD_REF" + "GITHUB_BASE_REF" + ) +elif [[ "${CIRCLECI:-}" == "true" ]]; then + RUNNING_IN_CI="true" + TRAMPOLINE_CI="circleci" + pass_down_envvars+=( + "CIRCLE_BRANCH" + "CIRCLE_BUILD_NUM" + "CIRCLE_BUILD_URL" + "CIRCLE_COMPARE_URL" + "CIRCLE_JOB" + "CIRCLE_NODE_INDEX" + "CIRCLE_NODE_TOTAL" + "CIRCLE_PREVIOUS_BUILD_NUM" + "CIRCLE_PROJECT_REPONAME" + "CIRCLE_PROJECT_USERNAME" + "CIRCLE_REPOSITORY_URL" + "CIRCLE_SHA1" + "CIRCLE_STAGE" + "CIRCLE_USERNAME" + "CIRCLE_WORKFLOW_ID" + "CIRCLE_WORKFLOW_JOB_ID" + "CIRCLE_WORKFLOW_UPSTREAM_JOB_IDS" + "CIRCLE_WORKFLOW_WORKSPACE_ID" + ) +fi + +# Configure the service account for pulling the docker image. +function repo_root() { + local dir="$1" + while [[ ! -d "${dir}/.git" ]]; do + dir="$(dirname "$dir")" + done + echo "${dir}" +} + +# Detect the project root. In CI builds, we assume the script is in +# the git tree and traverse from there, otherwise, traverse from `pwd` +# to find `.git` directory. +if [[ "${RUNNING_IN_CI:-}" == "true" ]]; then + PROGRAM_PATH="$(realpath "$0")" + PROGRAM_DIR="$(dirname "${PROGRAM_PATH}")" + PROJECT_ROOT="$(repo_root "${PROGRAM_DIR}")" +else + PROJECT_ROOT="$(repo_root $(pwd))" +fi + +log_yellow "Changing to the project root: ${PROJECT_ROOT}." +cd "${PROJECT_ROOT}" + +# To support relative path for `TRAMPOLINE_SERVICE_ACCOUNT`, we need +# to use this environment variable in `PROJECT_ROOT`. +if [[ -n "${TRAMPOLINE_SERVICE_ACCOUNT:-}" ]]; then + + mkdir -p "${tmpdir}/gcloud" + gcloud_config_dir="${tmpdir}/gcloud" + + log_yellow "Using isolated gcloud config: ${gcloud_config_dir}." + export CLOUDSDK_CONFIG="${gcloud_config_dir}" + + log_yellow "Using ${TRAMPOLINE_SERVICE_ACCOUNT} for authentication." + gcloud auth activate-service-account \ + --key-file "${TRAMPOLINE_SERVICE_ACCOUNT}" + log_yellow "Configuring Container Registry access" + gcloud auth configure-docker --quiet +fi + +required_envvars=( + # The basic trampoline configurations. + "TRAMPOLINE_IMAGE" + "TRAMPOLINE_BUILD_FILE" +) + +if [[ -f "${PROJECT_ROOT}/.trampolinerc" ]]; then + source "${PROJECT_ROOT}/.trampolinerc" +fi + +log_yellow "Checking environment variables." +for e in "${required_envvars[@]}" +do + if [[ -z "${!e:-}" ]]; then + log "Missing ${e} env var. Aborting." + exit 1 + fi +done + +# We want to support legacy style TRAMPOLINE_BUILD_FILE used with V1 +# script: e.g. "github/repo-name/.kokoro/run_tests.sh" +TRAMPOLINE_BUILD_FILE="${TRAMPOLINE_BUILD_FILE#github/*/}" +log_yellow "Using TRAMPOLINE_BUILD_FILE: ${TRAMPOLINE_BUILD_FILE}" + +# ignore error on docker operations and test execution +set +e + +log_yellow "Preparing Docker image." +# We only download the docker image in CI builds. +if [[ "${RUNNING_IN_CI:-}" == "true" ]]; then + # Download the docker image specified by `TRAMPOLINE_IMAGE` + + # We may want to add --max-concurrent-downloads flag. + + log_yellow "Start pulling the Docker image: ${TRAMPOLINE_IMAGE}." + if docker pull "${TRAMPOLINE_IMAGE}"; then + log_green "Finished pulling the Docker image: ${TRAMPOLINE_IMAGE}." + has_image="true" + else + log_red "Failed pulling the Docker image: ${TRAMPOLINE_IMAGE}." + has_image="false" + fi +else + # For local run, check if we have the image. + if docker images "${TRAMPOLINE_IMAGE}:latest" | grep "${TRAMPOLINE_IMAGE}"; then + has_image="true" + else + has_image="false" + fi +fi + + +# The default user for a Docker container has uid 0 (root). To avoid +# creating root-owned files in the build directory we tell docker to +# use the current user ID. +user_uid="$(id -u)" +user_gid="$(id -g)" +user_name="$(id -un)" + +# To allow docker in docker, we add the user to the docker group in +# the host os. +docker_gid=$(cut -d: -f3 < <(getent group docker)) + +update_cache="false" +if [[ "${TRAMPOLINE_DOCKERFILE:-none}" != "none" ]]; then + # Build the Docker image from the source. + context_dir=$(dirname "${TRAMPOLINE_DOCKERFILE}") + docker_build_flags=( + "-f" "${TRAMPOLINE_DOCKERFILE}" + "-t" "${TRAMPOLINE_IMAGE}" + "--build-arg" "UID=${user_uid}" + "--build-arg" "USERNAME=${user_name}" + ) + if [[ "${has_image}" == "true" ]]; then + docker_build_flags+=("--cache-from" "${TRAMPOLINE_IMAGE}") + fi + + log_yellow "Start building the docker image." + if [[ "${TRAMPOLINE_VERBOSE:-false}" == "true" ]]; then + echo "docker build" "${docker_build_flags[@]}" "${context_dir}" + fi + + # ON CI systems, we want to suppress docker build logs, only + # output the logs when it fails. + if [[ "${RUNNING_IN_CI:-}" == "true" ]]; then + if docker build "${docker_build_flags[@]}" "${context_dir}" \ + > "${tmpdir}/docker_build.log" 2>&1; then + if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then + cat "${tmpdir}/docker_build.log" + fi + + log_green "Finished building the docker image." + update_cache="true" + else + log_red "Failed to build the Docker image, aborting." + log_yellow "Dumping the build logs:" + cat "${tmpdir}/docker_build.log" + exit 1 + fi + else + if docker build "${docker_build_flags[@]}" "${context_dir}"; then + log_green "Finished building the docker image." + update_cache="true" + else + log_red "Failed to build the Docker image, aborting." + exit 1 + fi + fi +else + if [[ "${has_image}" != "true" ]]; then + log_red "We do not have ${TRAMPOLINE_IMAGE} locally, aborting." + exit 1 + fi +fi + +# We use an array for the flags so they are easier to document. +docker_flags=( + # Remove the container after it exists. + "--rm" + + # Use the host network. + "--network=host" + + # Run in priviledged mode. We are not using docker for sandboxing or + # isolation, just for packaging our dev tools. + "--privileged" + + # Run the docker script with the user id. Because the docker image gets to + # write in ${PWD} you typically want this to be your user id. + # To allow docker in docker, we need to use docker gid on the host. + "--user" "${user_uid}:${docker_gid}" + + # Pass down the USER. + "--env" "USER=${user_name}" + + # Mount the project directory inside the Docker container. + "--volume" "${PROJECT_ROOT}:${TRAMPOLINE_WORKSPACE}" + "--workdir" "${TRAMPOLINE_WORKSPACE}" + "--env" "PROJECT_ROOT=${TRAMPOLINE_WORKSPACE}" + + # Mount the temporary home directory. + "--volume" "${tmphome}:/h" + "--env" "HOME=/h" + + # Allow docker in docker. + "--volume" "/var/run/docker.sock:/var/run/docker.sock" + + # Mount the /tmp so that docker in docker can mount the files + # there correctly. + "--volume" "/tmp:/tmp" + # Pass down the KOKORO_GFILE_DIR and KOKORO_KEYSTORE_DIR + # TODO(tmatsuo): This part is not portable. + "--env" "TRAMPOLINE_SECRET_DIR=/secrets" + "--volume" "${KOKORO_GFILE_DIR:-/dev/shm}:/secrets/gfile" + "--env" "KOKORO_GFILE_DIR=/secrets/gfile" + "--volume" "${KOKORO_KEYSTORE_DIR:-/dev/shm}:/secrets/keystore" + "--env" "KOKORO_KEYSTORE_DIR=/secrets/keystore" +) + +# Add an option for nicer output if the build gets a tty. +if [[ -t 0 ]]; then + docker_flags+=("-it") +fi + +# Passing down env vars +for e in "${pass_down_envvars[@]}" +do + if [[ -n "${!e:-}" ]]; then + docker_flags+=("--env" "${e}=${!e}") + fi +done + +# If arguments are given, all arguments will become the commands run +# in the container, otherwise run TRAMPOLINE_BUILD_FILE. +if [[ $# -ge 1 ]]; then + log_yellow "Running the given commands '" "${@:1}" "' in the container." + readonly commands=("${@:1}") + if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then + echo docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}" "${commands[@]}" + fi + docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}" "${commands[@]}" +else + log_yellow "Running the tests in a Docker container." + docker_flags+=("--entrypoint=${TRAMPOLINE_BUILD_FILE}") + if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then + echo docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}" + fi + docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}" +fi + + +test_retval=$? + +if [[ ${test_retval} -eq 0 ]]; then + log_green "Build finished with ${test_retval}" +else + log_red "Build finished with ${test_retval}" +fi + +# Only upload it when the test passes. +if [[ "${update_cache}" == "true" ]] && \ + [[ $test_retval == 0 ]] && \ + [[ "${TRAMPOLINE_IMAGE_UPLOAD:-false}" == "true" ]]; then + log_yellow "Uploading the Docker image." + if docker push "${TRAMPOLINE_IMAGE}"; then + log_green "Finished uploading the Docker image." + else + log_red "Failed uploading the Docker image." + fi + # Call trampoline_after_upload_hook if it's defined. + if function_exists trampoline_after_upload_hook; then + trampoline_after_upload_hook + fi + +fi + +exit "${test_retval}" diff --git a/.trampolinerc b/.trampolinerc new file mode 100644 index 00000000..995ee291 --- /dev/null +++ b/.trampolinerc @@ -0,0 +1,51 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Template for .trampolinerc + +# Add required env vars here. +required_envvars+=( + "STAGING_BUCKET" + "V2_STAGING_BUCKET" +) + +# Add env vars which are passed down into the container here. +pass_down_envvars+=( + "STAGING_BUCKET" + "V2_STAGING_BUCKET" +) + +# Prevent unintentional override on the default image. +if [[ "${TRAMPOLINE_IMAGE_UPLOAD:-false}" == "true" ]] && \ + [[ -z "${TRAMPOLINE_IMAGE:-}" ]]; then + echo "Please set TRAMPOLINE_IMAGE if you want to upload the Docker image." + exit 1 +fi + +# Define the default value if it makes sense. +if [[ -z "${TRAMPOLINE_IMAGE_UPLOAD:-}" ]]; then + TRAMPOLINE_IMAGE_UPLOAD="" +fi + +if [[ -z "${TRAMPOLINE_IMAGE:-}" ]]; then + TRAMPOLINE_IMAGE="" +fi + +if [[ -z "${TRAMPOLINE_DOCKERFILE:-}" ]]; then + TRAMPOLINE_DOCKERFILE="" +fi + +if [[ -z "${TRAMPOLINE_BUILD_FILE:-}" ]]; then + TRAMPOLINE_BUILD_FILE="" +fi diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 730e2013..1de28a96 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -80,25 +80,6 @@ We use `nox `__ to instrument our tests. .. nox: https://pypi.org/project/nox/ -Note on Editable Installs / Develop Mode -======================================== - -- As mentioned previously, using ``setuptools`` in `develop mode`_ - or a ``pip`` `editable install`_ is not possible with this - library. This is because this library uses `namespace packages`_. - For context see `Issue #2316`_ and the relevant `PyPA issue`_. - - Since ``editable`` / ``develop`` mode can't be used, packages - need to be installed directly. Hence your changes to the source - tree don't get incorporated into the **already installed** - package. - -.. _namespace packages: https://www.python.org/dev/peps/pep-0420/ -.. _Issue #2316: https://github.com/GoogleCloudPlatform/google-cloud-python/issues/2316 -.. _PyPA issue: https://github.com/pypa/packaging-problems/issues/12 -.. _develop mode: https://setuptools.readthedocs.io/en/latest/setuptools.html#development-mode -.. _editable install: https://pip.pypa.io/en/stable/reference/pip_install/#editable-installs - ***************************************** I'm getting weird errors... Can you help? ***************************************** diff --git a/docs/conf.py b/docs/conf.py index 6c1527b2..d3afd421 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,12 +20,16 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath("..")) +# For plugins that can not read conf.py. +# See also: https://github.com/docascode/sphinx-docfx-yaml/issues/85 +sys.path.insert(0, os.path.abspath(".")) + __version__ = "" # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = "1.6.3" +needs_sphinx = "1.5.5" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -35,6 +39,7 @@ "sphinx.ext.autosummary", "sphinx.ext.intersphinx", "sphinx.ext.coverage", + "sphinx.ext.doctest", "sphinx.ext.napoleon", "sphinx.ext.todo", "sphinx.ext.viewcode", @@ -90,7 +95,12 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ["_build"] +exclude_patterns = [ + "_build", + "samples/AUTHORING_GUIDE.md", + "samples/CONTRIBUTING.md", + "samples/snippets/README.rst", +] # The reST default role (used for this markup: `text`) to use for all # documents. diff --git a/noxfile.py b/noxfile.py index a30ba11a..8e537c9d 100644 --- a/noxfile.py +++ b/noxfile.py @@ -102,6 +102,10 @@ def system(session): """Run the system test suite.""" system_test_path = os.path.join("tests", "system.py") system_test_folder_path = os.path.join("tests", "system") + + # Check the value of `RUN_SYSTEM_TESTS` env var. It defaults to true. + if os.environ.get("RUN_SYSTEM_TESTS", "true") == "false": + session.skip("RUN_SYSTEM_TESTS is set to false, skipping") # Sanity check: Only run tests if the environment variable is set. if not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS", ""): session.skip("Credentials must be set via environment variable") @@ -162,3 +166,38 @@ def docs(session): os.path.join("docs", ""), os.path.join("docs", "_build", "html", ""), ) + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def docfx(session): + """Build the docfx yaml files for this library.""" + + session.install("-e", ".") + # sphinx-docfx-yaml supports up to sphinx version 1.5.5. + # https://github.com/docascode/sphinx-docfx-yaml/issues/97 + session.install("sphinx==1.5.5", "alabaster", "recommonmark", "sphinx-docfx-yaml") + + shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) + session.run( + "sphinx-build", + "-T", # show full traceback on exception + "-N", # no colors + "-D", + ( + "extensions=sphinx.ext.autodoc," + "sphinx.ext.autosummary," + "docfx_yaml.extension," + "sphinx.ext.intersphinx," + "sphinx.ext.coverage," + "sphinx.ext.napoleon," + "sphinx.ext.todo," + "sphinx.ext.viewcode," + "recommonmark" + ), + "-b", + "html", + "-d", + os.path.join("docs", "_build", "doctrees", ""), + os.path.join("docs", ""), + os.path.join("docs", "_build", "html", ""), + ) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py new file mode 100644 index 00000000..ba55d7ce --- /dev/null +++ b/samples/snippets/noxfile.py @@ -0,0 +1,224 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import print_function + +import os +from pathlib import Path +import sys + +import nox + + +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING +# DO NOT EDIT THIS FILE EVER! +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING + +# Copy `noxfile_config.py` to your directory and modify it instead. + + +# `TEST_CONFIG` dict is a configuration hook that allows users to +# modify the test configurations. The values here should be in sync +# with `noxfile_config.py`. Users will copy `noxfile_config.py` into +# their directory and modify it. + +TEST_CONFIG = { + # You can opt out from the test for specific Python versions. + 'ignored_versions': ["2.7"], + + # An envvar key for determining the project id to use. Change it + # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a + # build specific Cloud project. You can also use your own string + # to use your own Cloud project. + 'gcloud_project_env': 'GOOGLE_CLOUD_PROJECT', + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + 'envs': {}, +} + + +try: + # Ensure we can import noxfile_config in the project's directory. + sys.path.append('.') + from noxfile_config import TEST_CONFIG_OVERRIDE +except ImportError as e: + print("No user noxfile_config found: detail: {}".format(e)) + TEST_CONFIG_OVERRIDE = {} + +# Update the TEST_CONFIG with the user supplied values. +TEST_CONFIG.update(TEST_CONFIG_OVERRIDE) + + +def get_pytest_env_vars(): + """Returns a dict for pytest invocation.""" + ret = {} + + # Override the GCLOUD_PROJECT and the alias. + env_key = TEST_CONFIG['gcloud_project_env'] + # This should error out if not set. + ret['GOOGLE_CLOUD_PROJECT'] = os.environ[env_key] + + # Apply user supplied envs. + ret.update(TEST_CONFIG['envs']) + return ret + + +# DO NOT EDIT - automatically generated. +# All versions used to tested samples. +ALL_VERSIONS = ["2.7", "3.6", "3.7", "3.8"] + +# Any default versions that should be ignored. +IGNORED_VERSIONS = TEST_CONFIG['ignored_versions'] + +TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS]) + +INSTALL_LIBRARY_FROM_SOURCE = bool(os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False)) +# +# Style Checks +# + + +def _determine_local_import_names(start_dir): + """Determines all import names that should be considered "local". + + This is used when running the linter to insure that import order is + properly checked. + """ + file_ext_pairs = [os.path.splitext(path) for path in os.listdir(start_dir)] + return [ + basename + for basename, extension in file_ext_pairs + if extension == ".py" + or os.path.isdir(os.path.join(start_dir, basename)) + and basename not in ("__pycache__") + ] + + +# Linting with flake8. +# +# We ignore the following rules: +# E203: whitespace before ‘:’ +# E266: too many leading ‘#’ for block comment +# E501: line too long +# I202: Additional newline in a section of imports +# +# We also need to specify the rules which are ignored by default: +# ['E226', 'W504', 'E126', 'E123', 'W503', 'E24', 'E704', 'E121'] +FLAKE8_COMMON_ARGS = [ + "--show-source", + "--builtin=gettext", + "--max-complexity=20", + "--import-order-style=google", + "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py", + "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202", + "--max-line-length=88", +] + + +@nox.session +def lint(session): + session.install("flake8", "flake8-import-order") + + local_names = _determine_local_import_names(".") + args = FLAKE8_COMMON_ARGS + [ + "--application-import-names", + ",".join(local_names), + "." + ] + session.run("flake8", *args) + + +# +# Sample Tests +# + + +PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"] + + +def _session_tests(session, post_install=None): + """Runs py.test for a particular project.""" + if os.path.exists("requirements.txt"): + session.install("-r", "requirements.txt") + + if os.path.exists("requirements-test.txt"): + session.install("-r", "requirements-test.txt") + + if INSTALL_LIBRARY_FROM_SOURCE: + session.install("-e", _get_repo_root()) + + if post_install: + post_install(session) + + session.run( + "pytest", + *(PYTEST_COMMON_ARGS + session.posargs), + # Pytest will return 5 when no tests are collected. This can happen + # on travis where slow and flaky tests are excluded. + # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html + success_codes=[0, 5], + env=get_pytest_env_vars() + ) + + +@nox.session(python=ALL_VERSIONS) +def py(session): + """Runs py.test for a sample using the specified version of Python.""" + if session.python in TESTED_VERSIONS: + _session_tests(session) + else: + session.skip("SKIPPED: {} tests are disabled for this sample.".format( + session.python + )) + + +# +# Readmegen +# + + +def _get_repo_root(): + """ Returns the root folder of the project. """ + # Get root of this repository. Assume we don't have directories nested deeper than 10 items. + p = Path(os.getcwd()) + for i in range(10): + if p is None: + break + if Path(p / ".git").exists(): + return str(p) + p = p.parent + raise Exception("Unable to detect repository root.") + + +GENERATED_READMES = sorted([x for x in Path(".").rglob("*.rst.in")]) + + +@nox.session +@nox.parametrize("path", GENERATED_READMES) +def readmegen(session, path): + """(Re-)generates the readme for a sample.""" + session.install("jinja2", "pyyaml") + dir_ = os.path.dirname(path) + + if os.path.exists(os.path.join(dir_, "requirements.txt")): + session.install("-r", os.path.join(dir_, "requirements.txt")) + + in_file = os.path.join(dir_, "README.rst.in") + session.run( + "python", _get_repo_root() + "/scripts/readme-gen/readme_gen.py", in_file + ) diff --git a/scripts/decrypt-secrets.sh b/scripts/decrypt-secrets.sh index ff599eb2..21f6d2a2 100755 --- a/scripts/decrypt-secrets.sh +++ b/scripts/decrypt-secrets.sh @@ -20,14 +20,27 @@ ROOT=$( dirname "$DIR" ) # Work from the project root. cd $ROOT +# Prevent it from overriding files. +# We recommend that sample authors use their own service account files and cloud project. +# In that case, they are supposed to prepare these files by themselves. +if [[ -f "testing/test-env.sh" ]] || \ + [[ -f "testing/service-account.json" ]] || \ + [[ -f "testing/client-secrets.json" ]]; then + echo "One or more target files exist, aborting." + exit 1 +fi + # Use SECRET_MANAGER_PROJECT if set, fallback to cloud-devrel-kokoro-resources. PROJECT_ID="${SECRET_MANAGER_PROJECT:-cloud-devrel-kokoro-resources}" gcloud secrets versions access latest --secret="python-docs-samples-test-env" \ + --project="${PROJECT_ID}" \ > testing/test-env.sh gcloud secrets versions access latest \ --secret="python-docs-samples-service-account" \ + --project="${PROJECT_ID}" \ > testing/service-account.json gcloud secrets versions access latest \ --secret="python-docs-samples-client-secrets" \ - > testing/client-secrets.json \ No newline at end of file + --project="${PROJECT_ID}" \ + > testing/client-secrets.json diff --git a/scripts/fixup_talent_v4beta1_keywords.py b/scripts/fixup_talent_v4beta1_keywords.py index e74650c5..ad407d40 100644 --- a/scripts/fixup_talent_v4beta1_keywords.py +++ b/scripts/fixup_talent_v4beta1_keywords.py @@ -43,7 +43,7 @@ class talentCallTransformer(cst.CSTTransformer): 'batch_create_jobs': ('parent', 'jobs', ), 'batch_delete_jobs': ('parent', 'filter', ), 'batch_update_jobs': ('parent', 'jobs', 'update_mask', ), - 'complete_query': ('parent', 'query', 'page_size', 'language_codes', 'company', 'scope', 'type', ), + 'complete_query': ('parent', 'query', 'page_size', 'language_codes', 'company', 'scope', 'type_', ), 'create_application': ('parent', 'application', ), 'create_client_event': ('parent', 'client_event', ), 'create_company': ('parent', 'company', ), diff --git a/synth.metadata b/synth.metadata index dc6fa66a..ddbee2fb 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,14 +4,29 @@ "git": { "name": ".", "remote": "git@github.com:googleapis/python-talent.git", - "sha": "2dfbb967b35e9edc97fdbe13bf536a6dff0ea676" + "sha": "f68f48ed16aacd669bafc829b2c3a8d3744aa05e" + } + }, + { + "git": { + "name": "googleapis", + "remote": "https://github.com/googleapis/googleapis.git", + "sha": "2987612c6aacc1857ee35468e7aeb1c393460799", + "internalRef": "333583499" + } + }, + { + "git": { + "name": "synthtool", + "remote": "https://github.com/googleapis/synthtool.git", + "sha": "da29da32b3a988457b49ae290112b74f14b713cc" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "ee7506d15daa3873accfff9430eff7e3953f0248" + "sha": "da29da32b3a988457b49ae290112b74f14b713cc" } } ], diff --git a/synth.py b/synth.py index 31dca605..6481c072 100644 --- a/synth.py +++ b/synth.py @@ -16,6 +16,7 @@ import synthtool as s from synthtool import gcp +from synthtool.languages import python gapic = gcp.GAPICBazel() common = gcp.CommonTemplates() @@ -51,7 +52,7 @@ # Add templated files # ---------------------------------------------------------------------------- templated_files = common.py_library( - samples=False, # set to True only if there are samples + samples=True, # set to True only if there are samples microgenerator=True, cov_level=99, ) @@ -63,4 +64,10 @@ # https://github.com/googleapis/gapic-generator-python/issues/525 s.replace("noxfile.py", '[\"\']-W[\"\']', '# "-W"') +# ---------------------------------------------------------------------------- +# Samples templates +# ---------------------------------------------------------------------------- + +python.py_samples(skip_readmes=True) + s.shell.run(["nox", "-s", "blacken"], hide_output=False) From df6d3f4e83525a7ea097660907d97be4cb240c77 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 24 Sep 2020 22:53:11 +0000 Subject: [PATCH 09/12] build: add BUILD_SPECIFIC_GCLOUD_PROJECT --- .kokoro/samples/python3.6/common.cfg | 6 ++++++ .kokoro/samples/python3.7/common.cfg | 6 ++++++ .kokoro/samples/python3.8/common.cfg | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/.kokoro/samples/python3.6/common.cfg b/.kokoro/samples/python3.6/common.cfg index 24c08609..71b9ff52 100644 --- a/.kokoro/samples/python3.6/common.cfg +++ b/.kokoro/samples/python3.6/common.cfg @@ -13,6 +13,12 @@ env_vars: { value: "py-3.6" } +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py36" +} + env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/python-talent/.kokoro/test-samples.sh" diff --git a/.kokoro/samples/python3.7/common.cfg b/.kokoro/samples/python3.7/common.cfg index c3b001b0..455dc164 100644 --- a/.kokoro/samples/python3.7/common.cfg +++ b/.kokoro/samples/python3.7/common.cfg @@ -13,6 +13,12 @@ env_vars: { value: "py-3.7" } +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py37" +} + env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/python-talent/.kokoro/test-samples.sh" diff --git a/.kokoro/samples/python3.8/common.cfg b/.kokoro/samples/python3.8/common.cfg index ab18d8d2..998d2cb7 100644 --- a/.kokoro/samples/python3.8/common.cfg +++ b/.kokoro/samples/python3.8/common.cfg @@ -24,6 +24,12 @@ env_vars: { value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" } +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py38" +} + # Download secrets for samples gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" From 6ce195180353b4b985322d3a31ef074bc16c7fbb Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 24 Sep 2020 23:24:39 +0000 Subject: [PATCH 10/12] test: fix sample tests --- .../job_search_autocomplete_job_title.py | 16 +++---- .../snippets/job_search_batch_create_jobs.py | 4 +- .../snippets/job_search_batch_update_jobs.py | 46 ++++++++++--------- samples/snippets/job_search_commute_search.py | 40 +++++++++------- .../job_search_create_client_event.py | 4 +- samples/snippets/job_search_create_company.py | 4 +- samples/snippets/job_search_create_job.py | 2 +- ...job_search_create_job_custom_attributes.py | 24 +++++----- samples/snippets/job_search_create_tenant.py | 10 ++-- .../job_search_custom_ranking_search.py | 30 ++++++------ samples/snippets/job_search_delete_company.py | 2 +- samples/snippets/job_search_delete_job.py | 2 +- samples/snippets/job_search_delete_tenant.py | 2 +- samples/snippets/job_search_get_company.py | 6 +-- samples/snippets/job_search_get_job.py | 18 ++++---- samples/snippets/job_search_get_tenant.py | 4 +- .../snippets/job_search_histogram_search.py | 19 ++++---- samples/snippets/job_search_list_companies.py | 10 ++-- samples/snippets/job_search_list_jobs.py | 12 ++--- samples/snippets/job_search_list_tenants.py | 8 ++-- 20 files changed, 138 insertions(+), 125 deletions(-) diff --git a/samples/snippets/job_search_autocomplete_job_title.py b/samples/snippets/job_search_autocomplete_job_title.py index af817ec1..e9f52868 100644 --- a/samples/snippets/job_search_autocomplete_job_title.py +++ b/samples/snippets/job_search_autocomplete_job_title.py @@ -15,7 +15,6 @@ # [START job_search_autocomplete_job_title] from google.cloud import talent_v4beta1 -from google.cloud.talent import enums import six @@ -35,21 +34,20 @@ def complete_query(project_id, tenant_id, query): if isinstance(query, six.binary_type): query = query.decode("utf-8") - parent = client.tenant_path(project_id, tenant_id) + parent = f"projects/{project_id}/tenants/{tenant_id}" - response = client.complete_query( - parent, - query, + request = talent_v4beta1.CompleteQueryRequest( + parent=parent, + query=query, page_size=5, # limit for number of results language_codes=["en-US"], # language code ) + response = client.complete_query(request=request) for result in response.completion_results: - print("Suggested title: {}".format(result.suggestion)) + print(f"Suggested title: {result.suggestion}") # Suggestion type is JOB_TITLE or COMPANY_TITLE print( - "Suggestion type: {}".format( - enums.CompleteQueryRequest.CompletionType(result.type).name - ) + f"Suggestion type: {talent_v4beta1.CompleteQueryRequest.CompletionType(result.type).name}" ) diff --git a/samples/snippets/job_search_batch_create_jobs.py b/samples/snippets/job_search_batch_create_jobs.py index a221cc57..18d48ae9 100644 --- a/samples/snippets/job_search_batch_create_jobs.py +++ b/samples/snippets/job_search_batch_create_jobs.py @@ -95,7 +95,7 @@ def batch_create_jobs( address_two = address_two.decode("utf-8") if isinstance(language_code_two, six.binary_type): language_code_two = language_code_two.decode("utf-8") - parent = client.tenant_path(project_id, tenant_id) + parent = f"projects/{project_id}/tenants/{tenant_id}" uris = [job_application_url_one] application_info = {"uris": uris} addresses = [address_one] @@ -122,7 +122,7 @@ def batch_create_jobs( } jobs = [jobs_element, jobs_element_2] - operation = client.batch_create_jobs(parent, jobs) + operation = client.batch_create_jobs(parent=parent, jobs=jobs) print("Waiting for operation to complete...") response = operation.result(90) diff --git a/samples/snippets/job_search_batch_update_jobs.py b/samples/snippets/job_search_batch_update_jobs.py index bac4232e..0aa576c4 100644 --- a/samples/snippets/job_search_batch_update_jobs.py +++ b/samples/snippets/job_search_batch_update_jobs.py @@ -103,36 +103,38 @@ def batch_update_jobs( address_two = address_two.decode("utf-8") if isinstance(language_code_two, six.binary_type): language_code_two = language_code_two.decode("utf-8") - parent = client.tenant_path(project_id, tenant_id) + parent = f"projects/{project_id}/tenants/{tenant_id}" uris = [job_application_url_one] application_info = {"uris": uris} addresses = [address_one] - jobs_element = { - "name": job_name_one, - "company": company_name_one, - "requisition_id": requisition_id_one, - "title": title_one, - "description": description_one, - "application_info": application_info, - "addresses": addresses, - "language_code": language_code_one, - } + jobs_element = talent.Job( + name=job_name_one, + company=company_name_one, + requisition_id=requisition_id_one, + title=title_one, + description=description_one, + application_info=application_info, + addresses=addresses, + language_code=language_code_one + ) + uris_2 = [job_application_url_two] application_info_2 = {"uris": uris_2} addresses_2 = [address_two] - jobs_element_2 = { - "name": job_name_two, - "company": company_name_two, - "requisition_id": requisition_id_two, - "title": title_two, - "description": description_two, - "application_info": application_info_2, - "addresses": addresses_2, - "language_code": language_code_two, - } + jobs_element_2 = talent.Job( + name=job_name_two, + company=company_name_two, + requisition_id=requisition_id_two, + title=title_two, + description=description_two, + application_info=application_info_2, + addresses=addresses_2, + language_code=language_code_two + ) + jobs = [jobs_element, jobs_element_2] - operation = client.batch_update_jobs(parent, jobs) + operation = client.batch_update_jobs(parent=parent, jobs=jobs) print("Waiting for operation to complete...") response = operation.result(90) diff --git a/samples/snippets/job_search_commute_search.py b/samples/snippets/job_search_commute_search.py index 1a5df79f..037bbd08 100644 --- a/samples/snippets/job_search_commute_search.py +++ b/samples/snippets/job_search_commute_search.py @@ -15,7 +15,6 @@ # [START job_search_commute_search] from google.cloud import talent -from google.cloud.talent import enums import six @@ -31,35 +30,42 @@ def search_jobs(project_id, tenant_id): project_id = project_id.decode("utf-8") if isinstance(tenant_id, six.binary_type): tenant_id = tenant_id.decode("utf-8") - parent = client.tenant_path(project_id, tenant_id) + parent = f"projects/{project_id}/tenants/{tenant_id}/" domain = "www.example.com" session_id = "Hashed session identifier" user_id = "Hashed user identifier" - request_metadata = {"domain": domain, "session_id": session_id, "user_id": user_id} - commute_method = enums.CommuteMethod.TRANSIT + request_metadata = talent.RequestMetadata( + domain=domain, + session_id=session_id, + user_id=user_id + ) + commute_method = talent.CommuteMethod.TRANSIT seconds = 1800 travel_duration = {"seconds": seconds} latitude = 37.422408 longitude = -122.084068 start_coordinates = {"latitude": latitude, "longitude": longitude} - commute_filter = { - "commute_method": commute_method, - "travel_duration": travel_duration, - "start_coordinates": start_coordinates, - } - job_query = {"commute_filter": commute_filter} + commute_filter = talent.CommuteFilter( + commute_method=commute_method, + travel_duration=travel_duration, + start_coordinates=start_coordinates, + ) + job_query = talent.JobQuery(commute_filter=commute_filter) # Iterate over all results results = [] - for response_item in client.search_jobs( - parent, request_metadata, job_query=job_query - ): - print("Job summary: {}".format(response_item.job_summary)) - print("Job title snippet: {}".format(response_item.job_title_snippet)) + request = talent.SearchJobsRequest( + parent=parent, + request_metadata=request_metadata, + job_query=job_query, + ) + for response_item in client.search_jobs(request=request): + print(f"Job summary: {response_item.job_summary}") + print(f"Job title snippet: {response_item.job_title_snippet}") job = response_item.job results.append(job.name) - print("Job name: {}".format(job.name)) - print("Job title: {}".format(job.title)) + print(f"Job name: {job.name}") + print(f"Job title: {job.title}") return results diff --git a/samples/snippets/job_search_create_client_event.py b/samples/snippets/job_search_create_client_event.py index c5bb1b48..5b068570 100644 --- a/samples/snippets/job_search_create_client_event.py +++ b/samples/snippets/job_search_create_client_event.py @@ -46,7 +46,7 @@ def create_client_event(project_id, tenant_id, request_id, event_id): request_id = request_id.decode("utf-8") if isinstance(event_id, six.binary_type): event_id = event_id.decode("utf-8") - parent = client.tenant_path(project_id, tenant_id) + parent = f"projects/{project_id}/tenants/{tenant_id}" # The timestamp of the event as seconds of UTC time since Unix epoch # For more information on how to create google.protobuf.Timestamps @@ -70,7 +70,7 @@ def create_client_event(project_id, tenant_id, request_id, event_id): "job_event": job_event, } - response = client.create_client_event(parent, client_event) + response = client.create_client_event(parent=parent, client_event=client_event) print(response) diff --git a/samples/snippets/job_search_create_company.py b/samples/snippets/job_search_create_company.py index 828fb0e4..28619a37 100644 --- a/samples/snippets/job_search_create_company.py +++ b/samples/snippets/job_search_create_company.py @@ -36,10 +36,10 @@ def create_company(project_id, tenant_id, display_name, external_id): display_name = display_name.decode("utf-8") if isinstance(external_id, six.binary_type): external_id = external_id.decode("utf-8") - parent = client.tenant_path(project_id, tenant_id) + parent = f"projects/{project_id}/tenants/{tenant_id}" company = {"display_name": display_name, "external_id": external_id} - response = client.create_company(parent, company) + response = client.create_company(parent=parent, company=company) print("Created Company") print("Name: {}".format(response.name)) print("Display Name: {}".format(response.display_name)) diff --git a/samples/snippets/job_search_create_job.py b/samples/snippets/job_search_create_job.py index ed065d24..d48c962c 100644 --- a/samples/snippets/job_search_create_job.py +++ b/samples/snippets/job_search_create_job.py @@ -46,7 +46,7 @@ def create_job( requisition_id = requisition_id.decode("utf-8") if isinstance(job_application_url, six.binary_type): job_application_url = job_application_url.decode("utf-8") - parent = client.tenant_path(project_id, tenant_id) + parent = f"projects/{project_id}/tenants/{tenant_id}" uris = [job_application_url] application_info = {"uris": uris} addresses = [ diff --git a/samples/snippets/job_search_create_job_custom_attributes.py b/samples/snippets/job_search_create_job_custom_attributes.py index de8aa40e..1ae10f2c 100644 --- a/samples/snippets/job_search_create_job_custom_attributes.py +++ b/samples/snippets/job_search_create_job_custom_attributes.py @@ -39,24 +39,24 @@ def create_job(project_id, tenant_id, company_id, requisition_id): # Custom attribute can be string or numeric value, # and can be filtered in search queries. # https://cloud.google.com/talent-solution/job-search/docs/custom-attributes - custom_attribute = talent.types.CustomAttribute() + custom_attribute = talent.CustomAttribute() custom_attribute.filterable = True custom_attribute.string_values.append("Intern") custom_attribute.string_values.append("Apprenticeship") - parent = client.tenant_path(project_id, tenant_id) + parent = f"projects/{project_id}/tenants/{tenant_id}" - job = { - "company": company_id, - "title": "Software Engineer", - "requisition_id": requisition_id, - "description": "This is a description of this job", - "language_code": "en-US", - "custom_attributes": {"FOR_STUDENTS": custom_attribute}, - } + job = talent.Job( + company=company_id, + title="Software Engineer", + requisition_id=requisition_id, + description="This is a description of this job", + language_code="en-us", + custom_attributes={"FOR_STUDENTS": custom_attribute} + ) - response = client.create_job(parent, job) - print("Created job: {}".format(response.name)) + response = client.create_job(parent=parent, job=job) + print(f"Created job: {response.name}") return response.name diff --git a/samples/snippets/job_search_create_tenant.py b/samples/snippets/job_search_create_tenant.py index 8d4fde32..4d9760e0 100644 --- a/samples/snippets/job_search_create_tenant.py +++ b/samples/snippets/job_search_create_tenant.py @@ -30,13 +30,13 @@ def create_tenant(project_id, external_id): project_id = project_id.decode("utf-8") if isinstance(external_id, six.binary_type): external_id = external_id.decode("utf-8") - parent = client.project_path(project_id) - tenant = {"external_id": external_id} + parent = f"projects/{project_id}" + tenant = talent.Tenant(external_id=external_id) - response = client.create_tenant(parent, tenant) + response = client.create_tenant(parent=parent, tenant=tenant) print("Created Tenant") - print("Name: {}".format(response.name)) - print("External ID: {}".format(response.external_id)) + print(f"Name: {response.name}") + print(f"External ID: {response.external_id}") return response.name diff --git a/samples/snippets/job_search_custom_ranking_search.py b/samples/snippets/job_search_custom_ranking_search.py index 1b33aab3..015291e4 100644 --- a/samples/snippets/job_search_custom_ranking_search.py +++ b/samples/snippets/job_search_custom_ranking_search.py @@ -15,7 +15,6 @@ # [START job_search_custom_ranking_search] from google.cloud import talent -from google.cloud.talent import enums import six @@ -31,12 +30,16 @@ def search_jobs(project_id, tenant_id): project_id = project_id.decode("utf-8") if isinstance(tenant_id, six.binary_type): tenant_id = tenant_id.decode("utf-8") - parent = client.tenant_path(project_id, tenant_id) + parent = f"projects/{project_id}/tenants/{tenant_id}" domain = "www.example.com" session_id = "Hashed session identifier" user_id = "Hashed user identifier" - request_metadata = {"domain": domain, "session_id": session_id, "user_id": user_id} - importance_level = enums.SearchJobsRequest.CustomRankingInfo.ImportanceLevel.EXTREME + request_metadata = talent.RequestMetadata( + domain=domain, + session_id=session_id, + user_id=user_id + ) + importance_level = talent.SearchJobsRequest.CustomRankingInfo.ImportanceLevel.EXTREME ranking_expression = "(someFieldLong + 25) * 0.25" custom_ranking_info = { "importance_level": importance_level, @@ -46,18 +49,19 @@ def search_jobs(project_id, tenant_id): # Iterate over all results results = [] - for response_item in client.search_jobs( - parent, - request_metadata, + request = talent.SearchJobsRequest( + parent=parent, + request_metadata=request_metadata, custom_ranking_info=custom_ranking_info, - order_by=order_by, - ): - print("Job summary: {}".format(response_item.job_summary)) - print("Job title snippet: {}".format(response_item.job_title_snippet)) + order_by=order_by + ) + for response_item in client.search_jobs(request=request): + print(f"Job summary: {response_item.job_summary}") + print(f"Job title snippet: {response_item.job_title_snippet}") job = response_item.job results.append(job.name) - print("Job name: {}".format(job.name)) - print("Job title: {}".format(job.title)) + print(f"Job name: {job.name}") + print(f"Job title: {job.title}") return results diff --git a/samples/snippets/job_search_delete_company.py b/samples/snippets/job_search_delete_company.py index 1eb1ed19..98174db5 100644 --- a/samples/snippets/job_search_delete_company.py +++ b/samples/snippets/job_search_delete_company.py @@ -35,7 +35,7 @@ def delete_company(project_id, tenant_id, company_id): company_id = company_id.decode("utf-8") name = client.company_path(project_id, tenant_id, company_id) - client.delete_company(name) + client.delete_company(name=name) print("Deleted company") diff --git a/samples/snippets/job_search_delete_job.py b/samples/snippets/job_search_delete_job.py index f6dd0cf3..ba165590 100644 --- a/samples/snippets/job_search_delete_job.py +++ b/samples/snippets/job_search_delete_job.py @@ -35,7 +35,7 @@ def delete_job(project_id, tenant_id, job_id): job_id = job_id.decode("utf-8") name = client.job_path(project_id, tenant_id, job_id) - client.delete_job(name) + client.delete_job(name=name) print("Deleted job.") diff --git a/samples/snippets/job_search_delete_tenant.py b/samples/snippets/job_search_delete_tenant.py index 9d95498b..5db49c1c 100644 --- a/samples/snippets/job_search_delete_tenant.py +++ b/samples/snippets/job_search_delete_tenant.py @@ -32,7 +32,7 @@ def delete_tenant(project_id, tenant_id): tenant_id = tenant_id.decode("utf-8") name = client.tenant_path(project_id, tenant_id) - client.delete_tenant(name) + client.delete_tenant(name=name) print("Deleted Tenant.") diff --git a/samples/snippets/job_search_get_company.py b/samples/snippets/job_search_get_company.py index ceac6e89..f597da89 100644 --- a/samples/snippets/job_search_get_company.py +++ b/samples/snippets/job_search_get_company.py @@ -35,9 +35,9 @@ def get_company(project_id, tenant_id, company_id): company_id = company_id.decode("utf-8") name = client.company_path(project_id, tenant_id, company_id) - response = client.get_company(name) - print("Company name: {}".format(response.name)) - print("Display name: {}".format(response.display_name)) + response = client.get_company(name=name) + print(f"Company name: {response.name}") + print(f"Display name: {response.display_name}") # [END job_search_get_company] diff --git a/samples/snippets/job_search_get_job.py b/samples/snippets/job_search_get_job.py index f311202b..cef60889 100644 --- a/samples/snippets/job_search_get_job.py +++ b/samples/snippets/job_search_get_job.py @@ -35,18 +35,18 @@ def get_job(project_id, tenant_id, job_id): job_id = job_id.decode("utf-8") name = client.job_path(project_id, tenant_id, job_id) - response = client.get_job(name) - print("Job name: {}".format(response.name)) - print("Requisition ID: {}".format(response.requisition_id)) - print("Title: {}".format(response.title)) - print("Description: {}".format(response.description)) - print("Posting language: {}".format(response.language_code)) + response = client.get_job(name=name) + print(f"Job name: {response.name}") + print(f"Requisition ID: {response.requisition_id}") + print(f"Title: {response.title}") + print(f"Description: {response.description}") + print(f"Posting language: {response.language_code}") for address in response.addresses: - print("Address: {}".format(address)) + print(f"Address: {address}") for email in response.application_info.emails: - print("Email: {}".format(email)) + print(f"Email: {email}") for website_uri in response.application_info.uris: - print("Website: {}".format(website_uri)) + print(f"Website: {website_uri}") # [END job_search_get_job] diff --git a/samples/snippets/job_search_get_tenant.py b/samples/snippets/job_search_get_tenant.py index 484c44d6..84f8d700 100644 --- a/samples/snippets/job_search_get_tenant.py +++ b/samples/snippets/job_search_get_tenant.py @@ -33,8 +33,8 @@ def get_tenant(project_id, tenant_id): name = client.tenant_path(project_id, tenant_id) response = client.get_tenant(name) - print("Name: {}".format(response.name)) - print("External ID: {}".format(response.external_id)) + print(f"Name: {response.name}") + print(f"External ID: {response.external_id}") # [END job_search_get_tenant] diff --git a/samples/snippets/job_search_histogram_search.py b/samples/snippets/job_search_histogram_search.py index f238e6a6..73bbc974 100644 --- a/samples/snippets/job_search_histogram_search.py +++ b/samples/snippets/job_search_histogram_search.py @@ -40,7 +40,7 @@ def search_jobs(project_id, tenant_id, query): tenant_id = tenant_id.decode("utf-8") if isinstance(query, six.binary_type): query = query.decode("utf-8") - parent = client.tenant_path(project_id, tenant_id) + parent = f"projects/{project_id}/tenants/{tenant_id}" domain = "www.example.com" session_id = "Hashed session identifier" user_id = "Hashed user identifier" @@ -50,15 +50,18 @@ def search_jobs(project_id, tenant_id, query): # Iterate over all results results = [] - for response_item in client.search_jobs( - parent, request_metadata, histogram_queries=histogram_queries - ): - print("Job summary: {}".format(response_item.job_summary)) - print("Job title snippet: {}".format(response_item.job_title_snippet)) + request = talent.SearchJobsRequest( + parent=parent, + request_metadata=request_metadata, + histogram_queries=histogram_queries, + ) + for response_item in client.search_jobs(request=request): + print("Job summary: {response_item.job_summary}") + print("Job title snippet: {response_item.job_title_snippet}") job = response_item.job results.append(job) - print("Job name: {}".format(job.name)) - print("Job title: {}".format(job.title)) + print("Job name: {job.name}") + print("Job title: {job.title}") return results diff --git a/samples/snippets/job_search_list_companies.py b/samples/snippets/job_search_list_companies.py index bdef589c..4cad5a4d 100644 --- a/samples/snippets/job_search_list_companies.py +++ b/samples/snippets/job_search_list_companies.py @@ -30,15 +30,15 @@ def list_companies(project_id, tenant_id): project_id = project_id.decode("utf-8") if isinstance(tenant_id, six.binary_type): tenant_id = tenant_id.decode("utf-8") - parent = client.tenant_path(project_id, tenant_id) + parent = f"projects/{project_id}/tenants/{tenant_id}" # Iterate over all results results = [] - for company in client.list_companies(parent): + for company in client.list_companies(parent=parent): results.append(company.name) - print("Company Name: {}".format(company.name)) - print("Display Name: {}".format(company.display_name)) - print("External ID: {}".format(company.external_id)) + print(f"Company Name: {company.name}") + print(f"Display Name: {company.display_name}") + print(f"External ID: {company.external_id}") return results diff --git a/samples/snippets/job_search_list_jobs.py b/samples/snippets/job_search_list_jobs.py index f31884dc..fd164bf7 100644 --- a/samples/snippets/job_search_list_jobs.py +++ b/samples/snippets/job_search_list_jobs.py @@ -33,16 +33,16 @@ def list_jobs(project_id, tenant_id, filter_): tenant_id = tenant_id.decode("utf-8") if isinstance(filter_, six.binary_type): filter_ = filter_.decode("utf-8") - parent = client.tenant_path(project_id, tenant_id) + parent = f"projects/{project_id}/tenants/{tenant_id}" # Iterate over all results results = [] - for job in client.list_jobs(parent, filter_): + for job in client.list_jobs(parent=parent, filter=filter_): results.append(job.name) - print("Job name: {}".format(job.name)) - print("Job requisition ID: {}".format(job.requisition_id)) - print("Job title: {}".format(job.title)) - print("Job description: {}".format(job.description)) + print("Job name: {job.name}") + print("Job requisition ID: {job.requisition_id}") + print("Job title: {job.title}") + print("Job description: {job.description}") return results diff --git a/samples/snippets/job_search_list_tenants.py b/samples/snippets/job_search_list_tenants.py index 2045bcca..11fe4550 100644 --- a/samples/snippets/job_search_list_tenants.py +++ b/samples/snippets/job_search_list_tenants.py @@ -27,12 +27,12 @@ def list_tenants(project_id): if isinstance(project_id, six.binary_type): project_id = project_id.decode("utf-8") - parent = client.project_path(project_id) + parent = "projects/{project_id}" # Iterate over all results - for response_item in client.list_tenants(parent): - print("Tenant Name: {}".format(response_item.name)) - print("External ID: {}".format(response_item.external_id)) + for response_item in client.list_tenants(parent=parent): + print(f"Tenant Name: {response_item.name}") + print(f"External ID: {response_item.external_id}") # [END job_search_list_tenants] From 3510730042fb4b28524975047aa9337e10be3971 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 24 Sep 2020 23:45:38 +0000 Subject: [PATCH 11/12] test: fix samples --- samples/snippets/job_search_create_job.py | 2 +- samples/snippets/job_search_get_tenant.py | 2 +- samples/snippets/job_search_list_tenants.py | 2 +- samples/snippets/job_search_list_tenants_test.py | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/snippets/job_search_create_job.py b/samples/snippets/job_search_create_job.py index d48c962c..0252a3e0 100644 --- a/samples/snippets/job_search_create_job.py +++ b/samples/snippets/job_search_create_job.py @@ -63,7 +63,7 @@ def create_job( "language_code": "en-US", } - response = client.create_job(parent, job) + response = client.create_job(parent=parent, job=job) print("Created job: {}".format(response.name)) return response.name diff --git a/samples/snippets/job_search_get_tenant.py b/samples/snippets/job_search_get_tenant.py index 84f8d700..1799c556 100644 --- a/samples/snippets/job_search_get_tenant.py +++ b/samples/snippets/job_search_get_tenant.py @@ -32,7 +32,7 @@ def get_tenant(project_id, tenant_id): tenant_id = tenant_id.decode("utf-8") name = client.tenant_path(project_id, tenant_id) - response = client.get_tenant(name) + response = client.get_tenant(name=name) print(f"Name: {response.name}") print(f"External ID: {response.external_id}") diff --git a/samples/snippets/job_search_list_tenants.py b/samples/snippets/job_search_list_tenants.py index 11fe4550..65239855 100644 --- a/samples/snippets/job_search_list_tenants.py +++ b/samples/snippets/job_search_list_tenants.py @@ -27,7 +27,7 @@ def list_tenants(project_id): if isinstance(project_id, six.binary_type): project_id = project_id.decode("utf-8") - parent = "projects/{project_id}" + parent = f"projects/{project_id}" # Iterate over all results for response_item in client.list_tenants(parent=parent): diff --git a/samples/snippets/job_search_list_tenants_test.py b/samples/snippets/job_search_list_tenants_test.py index 03f2fce7..b12f2605 100644 --- a/samples/snippets/job_search_list_tenants_test.py +++ b/samples/snippets/job_search_list_tenants_test.py @@ -27,13 +27,13 @@ def test_tenant(): client = talent.TenantServiceClient() external_id = f'test_tenant_{uuid.uuid4().hex}' - parent = client.project_path(PROJECT_ID) + parent = f"projects/{PROJECT_ID}" tenant = {"external_id": external_id} - resp = client.create_tenant(parent, tenant) + resp = client.create_tenant(parent=parent, tenant=tenant) yield resp - client.delete_tenant(resp.name) + client.delete_tenant(name=resp.name) def test_list_tenants(capsys, test_tenant): From 57dea035b3df250537afc6f1eff28847cca182c5 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 24 Sep 2020 23:52:57 +0000 Subject: [PATCH 12/12] sample: fix parent path --- samples/snippets/job_search_commute_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/job_search_commute_search.py b/samples/snippets/job_search_commute_search.py index 037bbd08..0b768441 100644 --- a/samples/snippets/job_search_commute_search.py +++ b/samples/snippets/job_search_commute_search.py @@ -30,7 +30,7 @@ def search_jobs(project_id, tenant_id): project_id = project_id.decode("utf-8") if isinstance(tenant_id, six.binary_type): tenant_id = tenant_id.decode("utf-8") - parent = f"projects/{project_id}/tenants/{tenant_id}/" + parent = f"projects/{project_id}/tenants/{tenant_id}" domain = "www.example.com" session_id = "Hashed session identifier" user_id = "Hashed user identifier"