← All ivy-nodes
IVYXSTUDIO · IVY NODE
S

Social: pack the publish results

ivy.node.publish-pack · v0.1.0

ivyx✓

Gathers the publishers' rows into one answer of kind publish-result for an IVYX Social content item; a target no publisher ran for is a failed row with the reason. No model, no network.

#social#publish#ivyx-social

Inputs

FieldTypeDescription
content_itemstringNot declared
resultsarrayPublish results: account, platform, status, post_key, url, at, error.
results_aarrayPublish results: account, platform, status, post_key, url, at, error.
results_barrayPublish results: account, platform, status, post_key, url, at, error.
results_carrayPublish results: account, platform, status, post_key, url, at, error.
results_darrayPublish results: account, platform, status, post_key, url, at, error.
skippedarrayNot declared

Outputs

FieldTypeDescription
kindrequiredstringAlways publish-result.
content_itemstringNot declared
resultsarrayPublish results: account, platform, status, post_key, url, at, error.
countrequiredintegerNot declared
publishedintegerNot declared

Source

python

# Input preparation
inp = __ivy_ctx__["nodes"][__ivy_node_id__]["input"]

# Compute
# Shared by every social node (built into each by build.mjs): dates, blocks, merging.
import datetime, json, re, statistics

def day(s):
    s = str(s or "")[:10]
    return s if re.fullmatch(r"\d{4}-\d{2}-\d{2}", s) else None

def now_iso():
    return datetime.datetime.utcnow().replace(microsecond=0).isoformat() + "Z"

def ms_of(s):
    if s is None or s == "":
        return None
    if isinstance(s, (int, float)):
        return int(s * 1000) if s < 1e12 else int(s)
    t = str(s).replace("Z", "+00:00")
    t = re.sub(r"([+-]\d{2})(\d{2})$", r"\1:\2", t)
    try:
        return int(datetime.datetime.fromisoformat(t).timestamp() * 1000)
    except ValueError:
        return None

def rows_of(block):
    """A {fields, rows} block as dicts."""
    if not isinstance(block, dict) or not block.get("fields"):
        return []
    f = block["fields"]
    return [dict(zip(f, r)) for r in block.get("rows") or []]

def block_of(dicts, first):
    fields = list(first)
    for d in dicts:
        for k in d:
            if k not in fields:
                fields.append(k)
    return {"fields": fields, "rows": [[d.get(k) for k in fields] for d in dicts]}

def listed(v):
    return [x for x in (v if isinstance(v, list) else [v] if v else []) if x is not None]

def text_of(v, limit=4000):
    s = " ".join(str(v).split()) if isinstance(v, (str, int, float)) else ""
    return s[:limit]


class PublishPack:
    "Social: pack the publish results"

    def run(self, inp):
        out = {}
        # The publishers' rows as one answer of kind publish-result for the content item.
        rows = []
        for key in ("results", "results_a", "results_b", "results_c", "results_d"):
            for r in listed(inp.get(key)):
                if isinstance(r, dict) and r.get("platform"):
                    rows.append(r)
        for p in listed(inp.get("skipped")):
            rows.append({"platform": p, "status": "failed", "error": "No publisher for this platform in this run (a connection, an id or the media is missing).", "at": int(datetime.datetime.utcnow().timestamp() * 1000)})
        out.update({"kind": "publish-result", "content_item": inp.get("content_item") or "", "results": rows, "count": len(rows), "published": sum(1 for r in rows if r.get("status") == "published")})
        return out


result = PublishPack().run(inp)

# Output collection (the runner reads it here)
out = __ivy_ctx__["nodes"][__ivy_node_id__]["output"]
out.update(result)

Tests

Requires: python:3.9

  • merge

    A published row and a platform no publisher ran for become two rows, one published.

  • empty

    No result is an empty answer of the same kind.