Skip to content

Commit b5fde9c

Browse files
Merge pull request #11 from google-marketing-solutions/dev
Add Glossary Functionality and UI updates
2 parents ca1fdcd + d9b7aa9 commit b5fde9c

47 files changed

Lines changed: 2050 additions & 790 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ directly. Instead, running the workers creates prefilled [Google Ads Editor
110110
Templates](https://support.google.com/google-ads/answer/10702525?hl=en) for Ads,
111111
Keywords, Ad Groups and Campaign for download as CSV files.
112112

113-
> NOTE: After running a worker the produced download URLs have an expiration of
113+
> [!NOTE]
114+
> After running a worker the produced download URLs have an expiration of
114115
> 1 hour for security purposes as those links are universally accessible. If you
115116
> need to retrieve the generated CSVs after the link expiration you can still
116117
> download them but you have to do so directly from the Cloud Storage Bucket you
@@ -159,6 +160,66 @@ Costs depend on the size of the translation requests, see
159160
[Cloud Translation Pricing](https://cloud.google.com/translate/pricing) for
160161
details.
161162

163+
## FAQs & Troubleshooting
164+
165+
1. **How do I stop certain terms (e.g., brand terms) from being translated?**
166+
167+
This can be avoided by using Glossaries, which are supported by Keyword
168+
Platform. During installation a dedicated bucket for glossary files is
169+
generated to which you can upload [unidirectional glossary files](https://cloud.google.com/translate/docs/advanced/glossary#unidirectional_glossaries). They will
170+
automatically be picked up by a pub/sub subscription and processed, which
171+
creates a glossary entry in your Google Cloud project. When running
172+
translations you can select the glossary you want to apply from an optional
173+
dropdown.
174+
175+
1. **How can I add terms to a glossary via the UI?**
176+
177+
This functionality is currently not available, but glossary entries are
178+
overwritten by default. To update a previously generated glossary, simply
179+
upload a new file with the same name containing previous and newly added
180+
terms.
181+
182+
1. **Why are there no accounts in the account dropdown?**
183+
184+
This is likely due to credentials not being valid. To get more details, you
185+
can check the logs by going to `Logs Explorer` in the Google Cloud Console.
186+
To generate new credentials you can either run the installation again
187+
(`./setup/install.sh`) or manually create new credentials using the [oauth
188+
playground](https://developers.google.com/oauthplayground/). To do that, you
189+
need to:
190+
* Add `https://developers.google.com/oauthplayground`` to the `Authorized
191+
redirect URIs` in your OAuth2.0 client in the Google Cloud Console under
192+
`APIs & Services > Credentials`.
193+
* On the [oauth playground page](https://developers.google.com/oauthplayground/)
194+
add the client id and secret under `Settings > Use your own OAuth
195+
credentials`.
196+
* Select all the [required scopes](https://github.com/google-marketing-solutions/keyword_platform/blob/main/setup/utils/oauth_flow.py#L41).
197+
* Hit `Authorize APIs` and exchange the access token for a refresh token.
198+
* In Google Cloud Console head to `Secret Manager` and update the secrets
199+
accordingly.
200+
201+
1. **Why am I getting a `Http failure …/proxy: 500 OK` response after hitting
202+
the RUN button?**
203+
204+
There can be various reasons for this and the logs will give you more
205+
details as to what happened. Head to your Cloud Console `Logs Explorer`
206+
and investigate any errors.
207+
208+
1. **When logging into my Google Account during installation I get "Access
209+
blocked: This app's request is invalid".**
210+
211+
Make sure to have added http://localhost:8080 to the `Authorized redirect
212+
URIs`` in your OAuth2.0 client in the Google Cloud Console under `APIs &
213+
Services > Credentials`.
214+
215+
1. **During installation terraform command fails with `error creating Brand:
216+
googleapi: Error 409: Requested entity already exists`.**
217+
218+
We use terraform for installation and setting up the Google Cloud resources.
219+
Terraform only supports internal OAuth Consent Screens. Before installing
220+
Keyword Platform, ensure that your OAuth Consent Screen is set to `internal`.
221+
222+
162223
## Privacy Notice
163224

164225
> [!NOTE]

py/common/api_utils.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,10 @@ def validate_credentials(
7676

7777
def send_api_request(
7878
url: str,
79-
params: dict[str, Any],
79+
params: dict[str, Any] | None,
8080
http_header: dict[str, str],
81-
method: str = 'POST') -> Any:
81+
method: str = 'POST',
82+
) -> Any:
8283
"""Call the requested API endpoint with the given parameters.
8384
8485
Args:

py/common/cloud_translation_client.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class Glossary:
7373

7474
id: str = dataclasses.field(default_factory=str)
7575
name: str = dataclasses.field(default_factory=str)
76+
display_name: str = dataclasses.field(default_factory=str)
7677

7778

7879
class GlossaryError(Exception):
@@ -376,16 +377,17 @@ def list_glossaries(self) -> list[Glossary]:
376377
gcp_region=self._gcp_region,
377378
)
378379
try:
379-
response = api_utils.send_api_request(url, {}, self._get_http_header())
380+
response = api_utils.send_api_request(
381+
url, None, self._get_http_header(), method='GET'
382+
)
380383
except requests.exceptions.HTTPError as http_error:
381384
logging.exception(
382385
'Encountered error during calls to Translation API: %s', http_error
383386
)
384387
raise
385388
for glossary in response['glossaries']:
386-
glossaries.append(
387-
Glossary(id=glossary['name'].split('/')[-1], name=glossary['name'])
388-
)
389+
id = glossary['name'].split('/')[-1]
390+
glossaries.append(Glossary(id=id, name=glossary['name'], display_name=id))
389391
return glossaries
390392

391393
def get_glossary_info_from_cloud_event_data(
@@ -455,7 +457,6 @@ def create_or_replace_glossary(
455457

456458
try:
457459
self._delete_glossary(glossary_id)
458-
logging.info('Deleted glossary with id %s', glossary_id)
459460
except google.api_core.exceptions.NotFound:
460461
logging.info('Creating glossary with id %s', glossary_id)
461462

@@ -525,6 +526,7 @@ def _create_glossary(
525526
finished_operation = api_utils.send_api_request(
526527
url, params, self._get_http_header()
527528
)
529+
logging.info('Finished glossary operation: %s.', finished_operation)
528530
except requests.exceptions.HTTPError as http_error:
529531
logging.exception(
530532
'Encountered error during calls to Translation API: %s', http_error
@@ -551,7 +553,8 @@ def _delete_glossary(self, glossary_id: str) -> None:
551553
)
552554

553555
try:
554-
api_utils.send_api_request(url, {}, self._get_http_header(), 'DELETE')
556+
api_utils.send_api_request(url, None, self._get_http_header(), 'DELETE')
557+
logging.info('Deleted glossary with id %s', glossary_id)
555558
except requests.exceptions.HTTPError as http_error:
556559
if http_error.response.status_code == 404:
557560
logging.info('Glossary with id %s not found.', glossary_id)

py/common/cloud_translation_client_test.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -488,10 +488,12 @@ def test_shorten_text_to_char_limit_not_called_unsupported_language(
488488
cloud_translation_client_lib.Glossary(
489489
id='fake-glossary-1',
490490
name='projects/fake-project-id/locations/fake-region/glossaries/fake-glossary-1',
491+
display_name='fake-glossary-1',
491492
),
492493
cloud_translation_client_lib.Glossary(
493494
id='fake-glossary-2',
494495
name='projects/fake-project-id/locations/fake-region/glossaries/fake-glossary-2',
496+
display_name='fake-glossary-2',
495497
),
496498
],
497499
},
@@ -608,13 +610,15 @@ def test_create_or_replace_glossary(self):
608610
# First call will be to try and delete the glossary if it exists.
609611
mock.call(
610612
expected_delete_url,
611-
{}, # Parameters are empty for a delete request.
613+
None, # Parameters are empty for a delete request.
612614
expected_header,
613615
'DELETE',
614616
),
615617
mock.call(expected_create_url, expected_create_params, expected_header),
616618
mock.call(
617-
expected_operation_url, expected_operation_params, expected_header
619+
expected_operation_url,
620+
expected_operation_params,
621+
expected_header,
618622
),
619623
])
620624

ui/package-lock.json

Lines changed: 53 additions & 39 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!--
2+
Copyright 2023 Google LLC.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
-->
16+
17+
<div class="position">
18+
<div *ngIf="showSpinner" class="spinner">
19+
<mat-spinner></mat-spinner>
20+
</div>
21+
<form [formGroup]="form">
22+
<app-multi-select
23+
#control
24+
[controllerName]="'accounts'"
25+
[className]="'responsive'"
26+
[values]="accounts"
27+
[label]="'Select accounts'"
28+
[isRequired]=true
29+
(selectionEvent)="accountSelection($event)">
30+
</app-multi-select>
31+
</form>
32+
</div>

0 commit comments

Comments
 (0)