Skip to content

Commit

Permalink
new task: set product types by title keywords
Browse files Browse the repository at this point in the history
  • Loading branch information
tekhaus committed Sep 28, 2023
1 parent c34bf52 commit a590493
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 0 deletions.
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\nt-shirt\ntee"
}
}
```

[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/)!
135 changes: 135 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,135 @@
{% 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 %}
{
"Shoes": "shoe\nshoes"
}
{% 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": "Alpha Shoe",
"productType": "Shirts"
}
]
}
}
}
{% 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
{% endcomment %}

{% assign product_title_downcase = product.title | downcase %}
{% 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_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\nt-shirt\ntee"
}
},
"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 \"Shoes\": \"shoe\\nshoes\"\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\": \"Alpha Shoe\",\n \"productType\": \"Shirts\"\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 {% endcomment %}\n\n {% assign product_title_downcase = product.title | downcase %}\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_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 %}",
"subscriptions": ["mechanic/user/trigger"],
"subscriptions_template": "mechanic/user/trigger",
"tags": ["Products"]
}

0 comments on commit a590493

Please sign in to comment.