Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New/set product types by title keywords #285

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ This directory is built automatically. Each task's documentation is generated fr
* [Set a default tracking number for new fulfillments](./set-a-default-tracking-number-for-new-fulfillments)
* [Set product or variant metafields values in bulk](./set-product-or-variant-metafields-in-bulk)
* [Set product templates based on product tags](./set-product-templates-based-on-product-tags)
* [Set product types by title keywords](./set-product-types-by-title-keywords)
* [Standardize UK postcodes](./standardize-uk-shipping-postcodes)
* [Sync an inverse customer tag](./sync-an-inverse-customer-tag)
* [Sync an inverse order tag](./sync-an-inverse-order-tag)
Expand Down Expand Up @@ -1362,6 +1363,7 @@ This directory is built automatically. Each task's documentation is generated fr
* [Send an email when a specific product is shipped](./send-an-email-when-a-specific-product-is-shipped)
* [Set product or variant metafields values in bulk](./set-product-or-variant-metafields-in-bulk)
* [Set product templates based on product tags](./set-product-templates-based-on-product-tags)
* [Set product types by title keywords](./set-product-types-by-title-keywords)
* [Sync inventory across a product type](./sync-inventory-across-a-product-type)
* [Sync inventory across product variants](./sync-inventory-across-product-variants)
* [Sync inventory levels to variant metafields](./sync-inventory-levels-to-variant-metafields)
Expand Down
51 changes: 51 additions & 0 deletions docs/set-product-types-by-title-keywords/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Set product types by title keywords

Tags: Products

Use this task to quickly reset product types in bulk based on product titles. When run manually, it will query for all products in the shop and for each product with a matched keyword in the title, the task will assign the paired product type to it.

* View in the task library: [tasks.mechanic.dev/set-product-types-by-title-keywords](https://tasks.mechanic.dev/set-product-types-by-title-keywords)
* Task JSON, for direct import: [task.json](../../tasks/set-product-types-by-title-keywords.json)
* Preview task code: [script.liquid](./script.liquid)

## Default options

```json
{
"product_types_and_keywords__keyval_multiline_required": {
"Shirts": "shirt\nshirts\nt-shirt\nt-shirts\ntee\ntees"
}
}
```

[Learn about task options in Mechanic](https://learn.mechanic.dev/core/tasks/options)

## Subscriptions

```liquid
mechanic/user/trigger
```

[Learn about event subscriptions in Mechanic](https://learn.mechanic.dev/core/tasks/subscriptions)

## Documentation

Use this task to quickly reset product types in bulk based on product titles. When run manually, it will query for all products in the shop and for each product with a matched keyword in the title, the task will assign the paired product type to it.

Configure the product types to set on the left, and the keyword(s) on the right. The task is pre-filled with a sample entry for "Shirts", which can be replaced if not needed.

**Notes:**
- The task will search for keywords in the order of entry in the task configuration, and it will stop once a match is made.
- If a product title does not contain any of the configured keywords, then that product will be ignored.

## Installing this task

Find this task [in the library at tasks.mechanic.dev](https://tasks.mechanic.dev/set-product-types-by-title-keywords), and use the "Try this task" button. Or, import [this task's JSON export](../../tasks/set-product-types-by-title-keywords.json) – see [Importing and exporting tasks](https://learn.mechanic.dev/core/tasks/import-and-export) to learn how imports work.

## Contributions

Found a bug? Got an improvement to add? Start here: [../../CONTRIBUTING.md](../../CONTRIBUTING.md).

## Task requests

Submit your [task requests](https://mechanic.canny.io/task-requests) for consideration by the Mechanic community, and they may be chosen for development and inclusion in the [task library](https://tasks.mechanic.dev/)!
140 changes: 140 additions & 0 deletions docs/set-product-types-by-title-keywords/script.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
{% assign product_types_and_keywords = options.product_types_and_keywords__keyval_multiline_required %}

{% comment %}
-- set preview values for the configuration field that will work with the preview query data
{% endcomment %}

{% if event.preview %}
{% capture product_types_and_keywords_json %}
{
"Shirts": "shirt\nshirts\nt-shirt\nt-shirts\ntee\ntees"
}
{% endcapture %}

{% assign product_types_and_keywords = product_types_and_keywords_json | parse_json %}
{% endif %}

{% assign cursor = nil %}

{% comment %}
-- query for all products in the shop (if > 25K products, the "100" loop value can be adjusted upward)
{% endcomment %}

{% for n in (1..100) %}
{% capture query %}
query {
products(
first: 250
after: {{ cursor | json }}
) {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
title
productType
}
}
}
{% endcapture %}

{% assign result = query | shopify %}

{% if event.preview %}
{% capture result_json %}
{
"data": {
"products": {
"nodes": [
{
"id": "gid://shopify/Product/1234567890",
"title": "Super soft tees",
"productType": "Shoes"
}
]
}
}
}
{% endcapture %}

{% assign result = result_json | parse_json %}
{% endif %}

{% comment %}
-- process each product in this result before querying for more products
{% endcomment %}

{% for product in result.data.products.nodes %}
{% comment %}
-- use downcase on product title and configured keywords since the "contains" operator is case-sensitive
-- split on spaces in title to create an array of words to be compared to keywords
{% endcomment %}

{% assign product_title_words_downcase
= product.title
| downcase
| split: " "
%}
{% assign product_type_to_set = nil %}

{% for keyval in product_types_and_keywords %}
{% assign product_type = keyval.first %}
{% assign keywords = keyval.last | split: newline %}

{% for keyword in keywords %}
{% if keyword == blank or keyword == "" %}
{% comment %}
-- protect against accidental empty keyword lines in the task config
{% endcomment %}

{% continue %}
{% endif %}

{% assign keyword_downcase = keyword | downcase %}

{% if product_title_words_downcase contains keyword_downcase %}
{% assign product_type_to_set = product_type %}
{% break %}
{% endif %}
{% endfor %}

{% if product_type_to_set != blank %}
{% break %}
{% endif %}
{% endfor %}

{% comment %}
-- set a new product type if a keyword match was made and the product does not already have that type
{% endcomment %}

{% if product_type_to_set != blank and product_type_to_set != product.productType %}
{% action "shopify" %}
mutation {
productUpdate(
input: {
id: {{ product.id | json }}
productType: {{ product_type_to_set | json }}
}
) {
product {
title
productType
}
userErrors {
field
message
}
}
}
{% endaction %}
{% endif %}
{% endfor %}

{% if result.data.products.pageInfo.hasNextPage %}
{% assign cursor = result.data.products.pageInfo.endCursor %}
{% else %}
{% break %}
{% endif %}
{% endfor %}
18 changes: 18 additions & 0 deletions tasks/set-product-types-by-title-keywords.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"docs": "Use this task to quickly reset product types in bulk based on product titles. When run manually, it will query for all products in the shop and for each product with a matched keyword in the title, the task will assign the paired product type to it.\n\nConfigure the product types to set on the left, and the keyword(s) on the right. The task is pre-filled with a sample entry for \"Shirts\", which can be replaced if not needed.\n\n**Notes:**\n- The task will search for keywords in the order of entry in the task configuration, and it will stop once a match is made.\n- If a product title does not contain any of the configured keywords, then that product will be ignored.",
"halt_action_run_sequence_on_error": false,
"name": "Set product types by title keywords",
"online_store_javascript": null,
"options": {
"product_types_and_keywords__keyval_multiline_required": {
"Shirts": "shirt\nshirts\nt-shirt\nt-shirts\ntee\ntees"
}
},
"order_status_javascript": null,
"perform_action_runs_in_sequence": false,
"preview_event_definitions": [],
"script": "{% assign product_types_and_keywords = options.product_types_and_keywords__keyval_multiline_required %}\n\n{% comment %}\n -- set preview values for the configuration field that will work with the preview query data\n{% endcomment %}\n\n{% if event.preview %}\n {% capture product_types_and_keywords_json %}\n {\n \"Shirts\": \"shirt\\nshirts\\nt-shirt\\nt-shirts\\ntee\\ntees\"\n }\n {% endcapture %}\n\n {% assign product_types_and_keywords = product_types_and_keywords_json | parse_json %}\n{% endif %}\n\n{% assign cursor = nil %}\n\n{% comment %}\n -- query for all products in the shop (if > 25K products, the \"100\" loop value can be adjusted upward)\n{% endcomment %}\n\n{% for n in (1..100) %}\n {% capture query %}\n query {\n products(\n first: 250\n after: {{ cursor | json }}\n ) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n id\n title\n productType\n }\n }\n }\n {% endcapture %}\n\n {% assign result = query | shopify %}\n\n {% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"products\": {\n \"nodes\": [\n {\n \"id\": \"gid://shopify/Product/1234567890\",\n \"title\": \"Super soft tees\",\n \"productType\": \"Shoes\"\n }\n ]\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n {% endif %}\n\n {% comment %}\n -- process each product in this result before querying for more products\n {% endcomment %}\n\n {% for product in result.data.products.nodes %}\n {% comment %}\n -- use downcase on product title and configured keywords since the \"contains\" operator is case-sensitive\n -- split on spaces in title to create an array of words to be compared to keywords\n {% endcomment %}\n\n {% assign product_title_words_downcase\n = product.title\n | downcase\n | split: \" \"\n %}\n {% assign product_type_to_set = nil %}\n\n {% for keyval in product_types_and_keywords %}\n {% assign product_type = keyval.first %}\n {% assign keywords = keyval.last | split: newline %}\n\n {% for keyword in keywords %}\n {% if keyword == blank or keyword == \"\" %}\n {% comment %}\n -- protect against accidental empty keyword lines in the task config\n {% endcomment %}\n\n {% continue %}\n {% endif %}\n\n {% assign keyword_downcase = keyword | downcase %}\n\n {% if product_title_words_downcase contains keyword_downcase %}\n {% assign product_type_to_set = product_type %}\n {% break %}\n {% endif %}\n {% endfor %}\n\n {% if product_type_to_set != blank %}\n {% break %}\n {% endif %}\n {% endfor %}\n\n {% comment %}\n -- set a new product type if a keyword match was made and the product does not already have that type\n {% endcomment %}\n\n {% if product_type_to_set != blank and product_type_to_set != product.productType %}\n {% action \"shopify\" %}\n mutation {\n productUpdate(\n input: {\n id: {{ product.id | json }}\n productType: {{ product_type_to_set | json }}\n }\n ) {\n product {\n title\n productType\n }\n userErrors {\n field\n message\n }\n }\n }\n {% endaction %}\n {% endif %}\n {% endfor %}\n\n {% if result.data.products.pageInfo.hasNextPage %}\n {% assign cursor = result.data.products.pageInfo.endCursor %}\n {% else %}\n {% break %}\n {% endif %}\n{% endfor %}\n",
"subscriptions": ["mechanic/user/trigger"],
"subscriptions_template": "mechanic/user/trigger",
"tags": ["Products"]
}