{"id":19329727,"url":"https://github.com/outerbounds/metaflow-card-html","last_synced_at":"2025-04-22T21:31:57.322Z","repository":{"id":107340687,"uuid":"451582609","full_name":"outerbounds/metaflow-card-html","owner":"outerbounds","description":null,"archived":false,"fork":false,"pushed_at":"2025-01-16T23:10:53.000Z","size":38,"stargazers_count":1,"open_issues_count":2,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-27T02:39:05.537Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/outerbounds.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-01-24T18:22:08.000Z","updated_at":"2025-01-16T23:10:55.000Z","dependencies_parsed_at":"2024-06-21T02:36:25.539Z","dependency_job_id":"6e515c9d-95e0-4da0-8436-bf032999329f","html_url":"https://github.com/outerbounds/metaflow-card-html","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outerbounds%2Fmetaflow-card-html","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outerbounds%2Fmetaflow-card-html/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outerbounds%2Fmetaflow-card-html/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outerbounds%2Fmetaflow-card-html/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/outerbounds","download_url":"https://codeload.github.com/outerbounds/metaflow-card-html/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250328576,"owners_count":21412638,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T02:29:39.268Z","updated_at":"2025-04-22T21:31:57.045Z","avatar_url":"https://github.com/outerbounds.png","language":"Python","funding_links":[],"categories":["Cards \u0026 Visualization"],"sub_categories":[],"readme":" [![](https://img.shields.io/github/license/outerbounds/metaflow-card-notebook)](./LICENSE)  [![Test Flow](https://github.com/outerbounds/metaflow-card-html/actions/workflows/test.yaml/badge.svg)](https://github.com/outerbounds/metaflow-card-html/actions/workflows/test.yaml) [![](https://img.shields.io/pypi/v/metaflow-card-html)](https://pypi.org/project/metaflow-card-html/)  [![](https://img.shields.io/badge/slack-@outerbounds-purple.svg?logo=slack )](http://slack.outerbounds.co/)  \n\n# HTML Metaflow Card\n\nRender raw HTML as a [Metaflow card][1].\n\n## Installation\n\n```bash\npip install metaflow-card-html\n```\n\n## Usage\n\nTo use the HTML card, you need to supply the `type=html` argument to the `@card` decorator.  You can then set the `html` attribute of your Flow to an html string you want to render:\n\n```python\n@card(type='html')\n@step\ndef train(self):\n    ...\n    ...\n    self.html = some_html() # set some html to the self attribute\n```\n\nIn these examples, we assume `some_html` is returning a HTML string, like this:\n\n```py\ndef some_html():\n    return \"\u003chtml\u003e\u003cbody\u003e\u003ch1 style='color: blue'\u003eHello World\u003c/h1\u003e\u003c/body\u003e\u003c/html\u003e\"\n```\n\nHowever, you may want to use a different attribute name than `html`.  To accomplish this, you can use the `options` parameter of the `@card` decorator.  For example, if we wanted to store our HTML in an attribute named `myhtml` we would do the following:\n\n```python\n@card(type='html',options={\"attribute\":\"myhtml\"}) # set the attribute name to myhtml\n@step\ndef train(self):\n    ...\n    ...\n    self.myhtml = some_html() # set the html like before\n```\n\n## Error Handling\n\nSome things to keep in mind about this card:\n\n- This card is meant to render HTML passed as a string.  If you pass a non-string value, this card will attempt to render it as a string instead.  \n- Failure to render a card will not result in an error in the flow.\n\n# Making Your Own Card Modules\n\nYou may want to implement your own card modules to customize your own workflows.  This HTML card also serves as a great example of how to implement your own card modules.  You can follow the steps below to accomplish this:\n\n## Step 1: Create a directory structure\n\nTo get started create the following directory structure:\n\n```\nsome_random_dir/ # the name of this dir doesn't matter\n├ setup.py\n├ metaflow_extensions/ # namespace package name \n│  └ card_html/ # NO __init__.py file, rename this folder to card_yourcardname\n│      └ plugins/ # NO __init__.py file\n│        └ cards/ # NO __init__.py file \n│           └ my_card_module/  # Name of card_module\n│               └ __init__.py. # This is the __init__.py is required to recoginize `my_card_module` as a package\n│               └ somerandomfile.py. # [Optional] some file as a part of the package. \n.\n```\n\nIf you are using GitHub, you can easily create this directory structure by using [this template][2].  Otherwise, you can copy the directory structure in [the template](https://github.com/outerbounds/metaflow-card-template) and modify it as indicated below.\n\n_Note: Metaflow cards are distributed via [namespace packages](https://packaging.python.org/en/latest/guides/packaging-namespace-packages/), under the namespace `metaflow_extensions`.  You need not worry about the mechanics of namespace packages to distribute or publish your own card module!  We recommend just following the directory structure indicated above._\n\n## Step 2: Modify Files \u0026 Directory Structure\n\nAfter creating the necessary directory structure, you will need to modify the following files/directories:\n\n1. `setup.py`: review and change the parameters passed to `setup` as appropriate.  Do not forget to add dependencies to other packages if they are required.\n2. Change the name of folder `metaflow_extensions/card_*` to `metaflow_extensions/card_\u003cyourcardname\u003e`.  This is a suggestion to help keep the directory structure consistent with other cards\n3. Change the name of the folder `metaflow_extensions/..../cards/html` to `metaflow_extensions/.../cards/\u003cyourcardname\u003e`\n\n## Step 3: Create a Card Module\n\nIn `__init__.py` located in `metaflow_extensions/.../cards/\u003cyourcardname\u003e`, you must import or define your custom card module. Here is a minimal example:\n\n```python\nfrom metaflow.cards import MetaflowCard\n\nclass BasicCard(MetaflowCard):\n    type = \"basic_card\"\n\n    def render(self, task): # this function returns the HTML to be rendered\n        return task.data.html # assumes you are saving an attribute named `html` in the task\n\nCARDS = [BasicCard]\n```\n\nNote that `__init__.py` requires a `CARDS` attribute which needs to be a `list` of objects inheriting `MetaflowCard` class.  \n\nWhat is shown above is only a minimal example.  Recall that in the `HTML` card, you can specify the name of the attribute via the `options` parameter.  We can implement this functionality as follows:\n\n```python\nfrom metaflow.cards import MetaflowCard\n\nclass HTMLCard(MetaflowCard):\n\n    type = 'html'\n    \n    def __init__(self, options={\"attribute\":\"html\"}, **kwargs):\n        self._attr_nm = options.get(\"attribute\", \"html\")\n \n    def render(self, task):\n        if self._attr_nm in task:\n            return str(task[self._attr_nm].data) # retrieves the html from the task by accessing `task[self._attr_nm]`\n\nCARDS = [HTMLCard]\n```\n\n## Step 4: Test Your Card\n\nNow that you have finished creating your custom card, you can install it so that it is  present in the python path.   You can then test your card by passing the correct argument for `type` to `@card` as follows (no need to import anything):\n\n```python\n@card(type='helloworld')\n@step\ndef train(self):\n    ...\n    ...\n    self.html = some_html() # set some html to the self attribute\n```\n\nWe recommend setting up automated tests in CI if possible.  Take a look at [tests/](tests/) and [.github/workflows/](.github/workflows/) for an example.  This is optional.\n\n## Step 5: Publish Your Card\n\nYou are now ready to publish your card to pyipi.  If you have are not familiar with how to do this, you can follow the steps in [this tutorial](https://realpython.com/pypi-publish-python-package/).  For a more in-depth discussion on python packaging, you can read [this article](https://packaging.python.org/tutorials/packaging-projects/).\n\n## Step 6: Tell Everyone About Your Card :rocket:\n\nNow its time to let people know about your card!  You can make a [PR to this README][3] as well as letting us know on Twitter, tagging [@MetaflowOSS](https://twitter.com/MetaflowOSS).\n\n\n[1]: https://docs.metaflow.org/\n[2]: https://github.com/outerbounds/metaflow-card-template/generate\n[3]: https://github.com/outerbounds/awesome-metaflow/edit/main/README.md\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fouterbounds%2Fmetaflow-card-html","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fouterbounds%2Fmetaflow-card-html","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fouterbounds%2Fmetaflow-card-html/lists"}