From 3f23fbc0de0fe5f85ec4d2282a944ea6fa2bb45d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 19 Mar 2024 11:25:54 +0000 Subject: [PATCH] deploy: 1cf97fa2ecc824d68c2e09ff7e2e627a29398e87 --- 404.html | 8 ++++---- FAQ/index.html | 10 +++++----- assets/js/090462b1.758f53d1.js | 1 + assets/js/090462b1.96edb2cc.js | 1 - assets/js/0a19367a.0190a3ab.js | 1 - assets/js/0a19367a.60ba1501.js | 1 + assets/js/0e384e19.96f019ec.js | 1 - assets/js/0e384e19.a31cf92b.js | 1 + .../js/{1a3c9b31.dc917f97.js => 1a3c9b31.c9def7af.js} | 2 +- assets/js/1ead5b6d.25860973.js | 1 - assets/js/1ead5b6d.e1c9946b.js | 1 + assets/js/244c9605.20b5e353.js | 1 - assets/js/244c9605.98fd4bd8.js | 1 + assets/js/28f080da.f1939e8e.js | 1 - assets/js/28f080da.f3fdf67d.js | 1 + .../js/{29966d47.72f5fed4.js => 29966d47.96892e78.js} | 2 +- assets/js/2d14298d.cf4df2f6.js | 1 + assets/js/2d14298d.d3baaf66.js | 1 - assets/js/3c29e2ef.85deae6f.js | 1 - assets/js/3c29e2ef.a772b7cb.js | 1 + assets/js/425ff8ea.44661c58.js | 1 - assets/js/425ff8ea.c1d1c789.js | 1 + .../js/{449bc0d9.4592c902.js => 449bc0d9.27b81fa6.js} | 2 +- assets/js/486bc80e.4b12c136.js | 1 - assets/js/486bc80e.7898ccbb.js | 1 + assets/js/5c5a410f.5c2cc41d.js | 1 + assets/js/5c5a410f.75606c3d.js | 1 - .../js/{63925da8.78d94b7c.js => 63925da8.8499b17f.js} | 2 +- assets/js/68ffc9f1.28002050.js | 1 - assets/js/68ffc9f1.df4945b5.js | 1 + assets/js/75e434b4.03a9174b.js | 1 + assets/js/75e434b4.6ba4fb88.js | 1 - assets/js/91c76d4c.136aee68.js | 1 + assets/js/91c76d4c.9525df5d.js | 1 - assets/js/93f138be.5f45353c.js | 1 + assets/js/93f138be.dcabecf0.js | 1 - assets/js/a4954f21.34a05832.js | 1 - assets/js/a4954f21.97c940d4.js | 1 + assets/js/ad895e75.6ddd513d.js | 1 - assets/js/ad895e75.8c6cb83b.js | 1 + assets/js/b5ada7f5.9a0a6900.js | 1 + assets/js/b5ada7f5.cf500660.js | 1 - assets/js/bdfbfcca.34dfc0db.js | 1 + assets/js/bdfbfcca.42b4dcbc.js | 1 - assets/js/cca8bc57.0af78829.js | 1 - assets/js/cca8bc57.374bda3a.js | 1 + assets/js/f744e480.4b66db01.js | 1 - assets/js/f744e480.cb13829f.js | 1 + assets/js/fd565be6.1a85a6a4.js | 1 + assets/js/fd565be6.1f7d4f62.js | 1 - assets/js/{main.c42b18fe.js => main.c7c3252d.js} | 4 ++-- ...8fe.js.LICENSE.txt => main.c7c3252d.js.LICENSE.txt} | 0 assets/js/runtime~main.08341cdf.js | 1 - assets/js/runtime~main.8b550ce2.js | 1 + contributions/index.html | 10 +++++----- developers/how-to-build-plugins/index.html | 10 +++++----- developers/how-to-refine-plugins/index.html | 10 +++++----- developers/how-to-visualize-results/index.html | 10 +++++----- developers/how-to-write-unit-tests/index.html | 10 +++++----- developers/index.html | 10 +++++----- index.html | 8 ++++---- intro/index.html | 10 +++++----- major-concepts/aggregation/index.html | 10 +++++----- major-concepts/design-philosophy/index.html | 10 +++++----- major-concepts/groupby/index.html | 10 +++++----- major-concepts/if/index.html | 10 +++++----- major-concepts/index.html | 10 +++++----- major-concepts/manifest-file/index.html | 10 +++++----- major-concepts/parameters/index.html | 10 +++++----- major-concepts/plugins/index.html | 10 +++++----- major-concepts/time/index.html | 10 +++++----- reference/cli/index.html | 10 +++++----- reference/index.html | 10 +++++----- reference/plugins/index.html | 10 +++++----- users/how-to-export-to-csv/index.html | 10 +++++----- users/how-to-import-plugins/index.html | 10 +++++----- users/how-to-install-if/index.html | 10 +++++----- users/how-to-write-manifests/index.html | 10 +++++----- users/index.html | 10 +++++----- users/quick-start/index.html | 10 +++++----- 80 files changed, 167 insertions(+), 167 deletions(-) create mode 100644 assets/js/090462b1.758f53d1.js delete mode 100644 assets/js/090462b1.96edb2cc.js delete mode 100644 assets/js/0a19367a.0190a3ab.js create mode 100644 assets/js/0a19367a.60ba1501.js delete mode 100644 assets/js/0e384e19.96f019ec.js create mode 100644 assets/js/0e384e19.a31cf92b.js rename assets/js/{1a3c9b31.dc917f97.js => 1a3c9b31.c9def7af.js} (68%) delete mode 100644 assets/js/1ead5b6d.25860973.js create mode 100644 assets/js/1ead5b6d.e1c9946b.js delete mode 100644 assets/js/244c9605.20b5e353.js create mode 100644 assets/js/244c9605.98fd4bd8.js delete mode 100644 assets/js/28f080da.f1939e8e.js create mode 100644 assets/js/28f080da.f3fdf67d.js rename assets/js/{29966d47.72f5fed4.js => 29966d47.96892e78.js} (65%) create mode 100644 assets/js/2d14298d.cf4df2f6.js delete mode 100644 assets/js/2d14298d.d3baaf66.js delete mode 100644 assets/js/3c29e2ef.85deae6f.js create mode 100644 assets/js/3c29e2ef.a772b7cb.js delete mode 100644 assets/js/425ff8ea.44661c58.js create mode 100644 assets/js/425ff8ea.c1d1c789.js rename assets/js/{449bc0d9.4592c902.js => 449bc0d9.27b81fa6.js} (61%) delete mode 100644 assets/js/486bc80e.4b12c136.js create mode 100644 assets/js/486bc80e.7898ccbb.js create mode 100644 assets/js/5c5a410f.5c2cc41d.js delete mode 100644 assets/js/5c5a410f.75606c3d.js rename assets/js/{63925da8.78d94b7c.js => 63925da8.8499b17f.js} (63%) delete mode 100644 assets/js/68ffc9f1.28002050.js create mode 100644 assets/js/68ffc9f1.df4945b5.js create mode 100644 assets/js/75e434b4.03a9174b.js delete mode 100644 assets/js/75e434b4.6ba4fb88.js create mode 100644 assets/js/91c76d4c.136aee68.js delete mode 100644 assets/js/91c76d4c.9525df5d.js create mode 100644 assets/js/93f138be.5f45353c.js delete mode 100644 assets/js/93f138be.dcabecf0.js delete mode 100644 assets/js/a4954f21.34a05832.js create mode 100644 assets/js/a4954f21.97c940d4.js delete mode 100644 assets/js/ad895e75.6ddd513d.js create mode 100644 assets/js/ad895e75.8c6cb83b.js create mode 100644 assets/js/b5ada7f5.9a0a6900.js delete mode 100644 assets/js/b5ada7f5.cf500660.js create mode 100644 assets/js/bdfbfcca.34dfc0db.js delete mode 100644 assets/js/bdfbfcca.42b4dcbc.js delete mode 100644 assets/js/cca8bc57.0af78829.js create mode 100644 assets/js/cca8bc57.374bda3a.js delete mode 100644 assets/js/f744e480.4b66db01.js create mode 100644 assets/js/f744e480.cb13829f.js create mode 100644 assets/js/fd565be6.1a85a6a4.js delete mode 100644 assets/js/fd565be6.1f7d4f62.js rename assets/js/{main.c42b18fe.js => main.c7c3252d.js} (98%) rename assets/js/{main.c42b18fe.js.LICENSE.txt => main.c7c3252d.js.LICENSE.txt} (100%) delete mode 100644 assets/js/runtime~main.08341cdf.js create mode 100644 assets/js/runtime~main.8b550ce2.js diff --git a/404.html b/404.html index 429340d8..83ad0ecd 100644 --- a/404.html +++ b/404.html @@ -8,13 +8,13 @@ Page Not Found | Impact Framework - - + +
Skip to main content
This project is an incubation project being run inside the Green Software Foundation; as such, we DON’T recommend using it in any critical use case. Incubation projects are experimental, offer no support guarantee, have minimal governance and process, and may be retired at any moment. This project may one day Graduate, in which case this disclaimer will be removed.

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- - + + \ No newline at end of file diff --git a/FAQ/index.html b/FAQ/index.html index f9da8fad..0b45e7a2 100644 --- a/FAQ/index.html +++ b/FAQ/index.html @@ -8,13 +8,13 @@ FAQs | Impact Framework - - + +
-
Skip to main content
This project is an incubation project being run inside the Green Software Foundation; as such, we DON’T recommend using it in any critical use case. Incubation projects are experimental, offer no support guarantee, have minimal governance and process, and may be retired at any moment. This project may one day Graduate, in which case this disclaimer will be removed.

FAQs

Is there any way to auto-generate manifest files?

You have to create your basic structure but it's expected that your input data will be fille dusing an importer plugin (we have one for Azure VMs and there will be mroe after the hackathon). For testing and experimenting we also have a MockObservations plugin that can autofill manifests with dummy data

What is your vision for the IF?

The vision is to be a protocol that enables you to use IF for any and all of those use-cases, and more.

Right now we focus on software applications running in the cloud, but we have people using it on-premise and even for supply chain modeling. The idea is that we provide the minimal infrastructure required for you to build up different use cases using plugins.

Is there a way to generate an audit/report along with a csv/yaml output?

No - we see the manifest file and associated manifest as a new type of "executable audit". The IF itself does not generate any other form of report, although there is nothing stopping others from building out this functionality on top of IF.

The manifest approach makes things transparent, but you can still manipulate the data you put as inputs in the manifest. How to fight again this?

We can't really stop people inputting fake data into a manifest file. But we're very interested in ways we can verify that the computation was done correctly and provide public proofs. Don't expect anything imminently, but we are thinking along these lines for the future.

Is it planned to make the IF more user friendly or also more usable for non it people?

IF is a low level infrastructure project. the core team focuses on building out solid foundations for others to build UIs, apps etc on top. There is no official IF UX tooling.

Does it only calculate emissions for CPU usage or does it also work with other meters like storage/bandwith?

We do have basic models for memory and network usage in addition to CPU. You can create plugins for anything you can observe and model.

Is the cloud-metadata only for public cloud providers?

Yes it is - it's based on a database we maintain as part of the IF repository. You could extend it by adding the same data for your on premise resource.

Have you compared the results to the carbon reports that cloud providers are producing for their customers?

No we haven't - we've been focussing on capturing impacts of individual applications over relatively short time periods so far. It's difficult to find the underlying data to build the comparisons.

Are there plans to include other pieces of cloud metadata beyond servers e.g. for PaaS services?

We don't have immediate specific plans to build this as the IF core team, but we're enthusiastic about teams building plugins for this to support more use-cases

Is there a cloud-metadata for VMware and/or Openstack ?

No there isn't! It would be a nice addition.

Will IF be developed beyond the upcoming hackathon?

yes! we are very passionate about this project and it is only going to grow from here, far beyond the hackathon!

- - +
Skip to main content
This project is an incubation project being run inside the Green Software Foundation; as such, we DON’T recommend using it in any critical use case. Incubation projects are experimental, offer no support guarantee, have minimal governance and process, and may be retired at any moment. This project may one day Graduate, in which case this disclaimer will be removed.

FAQs

Is there any way to auto-generate manifest files?

You have to create your basic structure but it's expected that your input data will be fille dusing an importer plugin (we have one for Azure VMs and there will be mroe after the hackathon). For testing and experimenting we also have a MockObservations plugin that can autofill manifests with dummy data

What is your vision for the IF?

The vision is to be a protocol that enables you to use IF for any and all of those use-cases, and more.

Right now we focus on software applications running in the cloud, but we have people using it on-premise and even for supply chain modeling. The idea is that we provide the minimal infrastructure required for you to build up different use cases using plugins.

Is there a way to generate an audit/report along with a csv/yaml output?

No - we see the manifest file and associated manifest as a new type of "executable audit". The IF itself does not generate any other form of report, although there is nothing stopping others from building out this functionality on top of IF.

The manifest approach makes things transparent, but you can still manipulate the data you put as inputs in the manifest. How to fight again this?

We can't really stop people inputting fake data into a manifest file. But we're very interested in ways we can verify that the computation was done correctly and provide public proofs. Don't expect anything imminently, but we are thinking along these lines for the future.

Is it planned to make the IF more user friendly or also more usable for non it people?

IF is a low level infrastructure project. the core team focuses on building out solid foundations for others to build UIs, apps etc on top. There is no official IF UX tooling.

Does it only calculate emissions for CPU usage or does it also work with other meters like storage/bandwith?

We do have basic models for memory and network usage in addition to CPU. You can create plugins for anything you can observe and model.

Is the cloud-metadata only for public cloud providers?

Yes it is - it's based on a database we maintain as part of the IF repository. You could extend it by adding the same data for your on premise resource.

Have you compared the results to the carbon reports that cloud providers are producing for their customers?

No we haven't - we've been focussing on capturing impacts of individual applications over relatively short time periods so far. It's difficult to find the underlying data to build the comparisons.

Are there plans to include other pieces of cloud metadata beyond servers e.g. for PaaS services?

We don't have immediate specific plans to build this as the IF core team, but we're enthusiastic about teams building plugins for this to support more use-cases

Is there a cloud-metadata for VMware and/or Openstack ?

No there isn't! It would be a nice addition.

Will IF be developed beyond the upcoming hackathon?

yes! we are very passionate about this project and it is only going to grow from here, far beyond the hackathon!

+ + \ No newline at end of file diff --git a/assets/js/090462b1.758f53d1.js b/assets/js/090462b1.758f53d1.js new file mode 100644 index 00000000..bb4bc4e5 --- /dev/null +++ b/assets/js/090462b1.758f53d1.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[941],{4137:(n,e,t)=>{t.d(e,{Zo:()=>d,kt:()=>m});var a=t(7294);function r(n,e,t){return e in n?Object.defineProperty(n,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):n[e]=t,n}function o(n,e){var t=Object.keys(n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(n);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),t.push.apply(t,a)}return t}function i(n){for(var e=1;e=0||(r[t]=n[t]);return r}(n,e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(n);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(n,t)&&(r[t]=n[t])}return r}var u=a.createContext({}),c=function(n){var e=a.useContext(u),t=e;return n&&(t="function"==typeof n?n(e):i(i({},e),n)),t},d=function(n){var e=c(n.components);return a.createElement(u.Provider,{value:e},n.children)},s={inlineCode:"code",wrapper:function(n){var e=n.children;return a.createElement(a.Fragment,{},e)}},p=a.forwardRef((function(n,e){var t=n.components,r=n.mdxType,o=n.originalType,u=n.parentName,d=l(n,["components","mdxType","originalType","parentName"]),p=c(t),m=r,g=p["".concat(u,".").concat(m)]||p[m]||s[m]||o;return t?a.createElement(g,i(i({ref:e},d),{},{components:t})):a.createElement(g,i({ref:e},d))}));function m(n,e){var t=arguments,r=e&&e.mdxType;if("string"==typeof n||r){var o=t.length,i=new Array(o);i[0]=p;var l={};for(var u in e)hasOwnProperty.call(e,u)&&(l[u]=e[u]);l.originalType=n,l.mdxType="string"==typeof n?n:r,i[1]=l;for(var c=2;c{t.r(e),t.d(e,{assets:()=>u,contentTitle:()=>i,default:()=>s,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var a=t(7462),r=(t(7294),t(4137));const o={sidebar_position:5},i="How to export to CSV",l={unversionedId:"users/how-to-export-to-csv",id:"users/how-to-export-to-csv",title:"How to export to CSV",description:"IF supports exporting data to CSV files. This provides users with a data format that enables visualization and data analysis using standard data analysis tools.",source:"@site/docs/users/how-to-export-to-csv.md",sourceDirName:"users",slug:"/users/how-to-export-to-csv",permalink:"/users/how-to-export-to-csv",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-export-to-csv.md",tags:[],version:"current",sidebarPosition:5,frontMatter:{sidebar_position:5},sidebar:"tutorialSidebar",previous:{title:"How to write a manifest file",permalink:"/users/how-to-write-manifests"},next:{title:"Developers",permalink:"/developers/"}},u={},c=[{value:"Manifest config",id:"manifest-config",level:2},{value:"CLI command",id:"cli-command",level:2},{value:"Comparing CSV to Yaml",id:"comparing-csv-to-yaml",level:2},{value:"CSV and aggregation",id:"csv-and-aggregation",level:2},{value:"Walkthrough",id:"walkthrough",level:2}],d={toc:c};function s(n){let{components:e,...t}=n;return(0,r.kt)("wrapper",(0,a.Z)({},d,t,{components:e,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"how-to-export-to-csv"},"How to export to CSV"),(0,r.kt)("p",null,"IF supports exporting data to CSV files. This provides users with a data format that enables visualization and data analysis using standard data analysis tools."),(0,r.kt)("h2",{id:"manifest-config"},"Manifest config"),(0,r.kt)("p",null,"To export your data to a CSV file, you have to provide a small piece of config data to your manifest file:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n outputs:\n - csv\n")),(0,r.kt)("p",null,"You can also add ",(0,r.kt)("inlineCode",{parentName:"p"},"- yaml")," if you want to export to both ",(0,r.kt)("inlineCode",{parentName:"p"},"yaml")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"csv")," simultaneously."),(0,r.kt)("h2",{id:"cli-command"},"CLI command"),(0,r.kt)("p",null,"Then, you must select the metric you want to export to CSV. The name of that metric must be added to the savepath provided to the ",(0,r.kt)("inlineCode",{parentName:"p"},"--output")," command in the CLI, after a hashtag."),(0,r.kt)("p",null,"For example, to export the ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," data from your tree to a CSV file:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest example.yml --output example#carbon\n")),(0,r.kt)("p",null,"This will save a CSV file called ",(0,r.kt)("inlineCode",{parentName:"p"},"example.csv"),". The contents will look similar to the following:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"Path")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"Aggregated")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"2024-03-05T00:00:00.000Z")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"2024-03-05T00:05:00.000Z")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"2024-03-05T00:10:00.000Z"))),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"tree.carbon"),(0,r.kt)("td",{parentName:"tr",align:null},"425.289232008725"),(0,r.kt)("td",{parentName:"tr",align:null},"17.9269877157543"),(0,r.kt)("td",{parentName:"tr",align:null},"8.9024388783018"),(0,r.kt)("td",{parentName:"tr",align:null},"45.6021901509012")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"tree.children.westus3.carbon"),(0,r.kt)("td",{parentName:"tr",align:null},"104.696836722878"),(0,r.kt)("td",{parentName:"tr",align:null},"3.59973803197887"),(0,r.kt)("td",{parentName:"tr",align:null},"3.47438149032372"),(0,r.kt)("td",{parentName:"tr",align:null},"6.91318436533634")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"tree.children.westus3.children.server-1.carbon"),(0,r.kt)("td",{parentName:"tr",align:null},"104.696836722878"),(0,r.kt)("td",{parentName:"tr",align:null},"3.59973803197887"),(0,r.kt)("td",{parentName:"tr",align:null},"3.47438149032372"),(0,r.kt)("td",{parentName:"tr",align:null},"6.91318436533634")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"tree.children.france.carbon"),(0,r.kt)("td",{parentName:"tr",align:null},"320.592395285847"),(0,r.kt)("td",{parentName:"tr",align:null},"14.3272496837754"),(0,r.kt)("td",{parentName:"tr",align:null},"5.42805738797808"),(0,r.kt)("td",{parentName:"tr",align:null},"38.6890057855649")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"tree.children.france.children.server-2.carbon"),(0,r.kt)("td",{parentName:"tr",align:null},"320.592395285847"),(0,r.kt)("td",{parentName:"tr",align:null},"14.3272496837754"),(0,r.kt)("td",{parentName:"tr",align:null},"5.42805738797808"),(0,r.kt)("td",{parentName:"tr",align:null},"38.6890057855649")))),(0,r.kt)("h2",{id:"comparing-csv-to-yaml"},"Comparing CSV to Yaml"),(0,r.kt)("p",null,"The CSV above is generated from the following yaml. The ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," metric is extracted and added to the CSV. Otherwise,the CSV is an exact representation of the following yaml tree. You can see that the CSV reepresentation is ",(0,r.kt)("em",{parentName:"p"},"much")," easier to understand than the full yaml tree:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n pipeline:\n - mock-observations\n - group-by\n - cloud-metadata\n - time-sync\n - watttime\n - teads-curve\n - operational-carbon\n defaults:\n grid/carbon-intensity: 500\n config:\n group-by:\n group:\n - cloud/region\n - name\n children:\n westus3:\n children:\n server-1:\n inputs:\n - timestamp: '2024-03-05T00:00:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 66\n grid/carbon-intensity: 500\n - timestamp: '2024-03-05T00:05:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 4\n grid/carbon-intensity: 500\n - timestamp: '2024-03-05T00:10:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 54\n grid/carbon-intensity: 500\n - timestamp: '2024-03-05T00:15:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 19\n grid/carbon-intensity: 500\n outputs:\n - timestamp: '2024-03-05T00:00:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 65.78\n grid/carbon-intensity: 369.4947514218548\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: CAISO\n cloud/region-em-zone-id: US-CAL-CISO\n cloud/region-wt-id: CAISO_NORTH\n cloud/region-location: US West (N. California)\n cloud/region-geolocation: 34.0497,-118.1326\n geolocation: 34.0497,-118.1326\n cpu/energy: 0.018934842060004835\n carbon: 6.996324760173567\n - timestamp: '2024-03-05T00:05:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 3.986666666666667\n grid/carbon-intensity: 369.38452029076234\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: CAISO\n cloud/region-em-zone-id: US-CAL-CISO\n cloud/region-wt-id: CAISO_NORTH\n cloud/region-location: US West (N. California)\n cloud/region-geolocation: 34.0497,-118.1326\n geolocation: 34.0497,-118.1326\n cpu/energy: 0.004545546617763956\n carbon: 1.6790545568620359\n - timestamp: '2024-03-05T00:10:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 53.82\n grid/carbon-intensity: 372.58122309244305\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: CAISO\n cloud/region-em-zone-id: US-CAL-CISO\n cloud/region-wt-id: CAISO_NORTH\n cloud/region-location: US West (N. California)\n cloud/region-geolocation: 34.0497,-118.1326\n geolocation: 34.0497,-118.1326\n cpu/energy: 0.017357893372978016\n carbon: 6.467225143212361\n - timestamp: '2024-03-05T00:15:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 18.936666666666667\n grid/carbon-intensity: 434.20042537311633\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: CAISO\n cloud/region-em-zone-id: US-CAL-CISO\n cloud/region-wt-id: CAISO_NORTH\n cloud/region-location: US West (N. California)\n cloud/region-geolocation: 34.0497,-118.1326\n geolocation: 34.0497,-118.1326\n cpu/energy: 0.010385485956624245\n carbon: 4.5093824200727735\n aggregated:\n carbon: 19.651986880320734\n outputs:\n - carbon: 6.996324760173567\n timestamp: '2024-03-05T00:00:00.000Z'\n duration: 300\n - carbon: 1.6790545568620359\n timestamp: '2024-03-05T00:05:00.000Z'\n duration: 300\n - carbon: 6.467225143212361\n timestamp: '2024-03-05T00:10:00.000Z'\n duration: 300\n - carbon: 4.5093824200727735\n timestamp: '2024-03-05T00:15:00.000Z'\n duration: 300\n aggregated:\n carbon: 19.651986880320734\n france:\n children:\n server-2:\n inputs:\n - timestamp: '2024-03-05T00:00:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 15\n grid/carbon-intensity: 500\n - timestamp: '2024-03-05T00:05:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 78\n grid/carbon-intensity: 500\n - timestamp: '2024-03-05T00:10:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 16\n grid/carbon-intensity: 500\n - timestamp: '2024-03-05T00:15:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 6\n grid/carbon-intensity: 500\n outputs:\n - timestamp: '2024-03-05T00:00:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 14.95\n grid/carbon-intensity: 1719.1647205176753\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: France\n cloud/region-em-zone-id: FR\n cloud/region-wt-id: FR\n cloud/region-location: Paris\n cloud/region-geolocation: 48.8567,2.3522\n geolocation: 48.8567,2.3522\n cpu/energy: 0.00905914075141129\n carbon: 15.574155178030272\n - timestamp: '2024-03-05T00:05:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 77.74\n grid/carbon-intensity: 1719.0544893865829\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: France\n cloud/region-em-zone-id: FR\n cloud/region-wt-id: FR\n cloud/region-location: Paris\n cloud/region-geolocation: 48.8567,2.3522\n geolocation: 48.8567,2.3522\n cpu/energy: 0.020379266251888902\n carbon: 35.0330691407141\n - timestamp: '2024-03-05T00:10:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 15.946666666666667\n grid/carbon-intensity: 1718.8707708347622\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: France\n cloud/region-em-zone-id: FR\n cloud/region-wt-id: FR\n cloud/region-location: Paris\n cloud/region-geolocation: 48.8567,2.3522\n geolocation: 48.8567,2.3522\n cpu/energy: 0.009405866514354337\n carbon: 16.16746902589712\n - timestamp: '2024-03-05T00:15:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 5.98\n grid/carbon-intensity: 1718.6686804277592\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: France\n cloud/region-em-zone-id: FR\n cloud/region-wt-id: FR\n cloud/region-location: Paris\n cloud/region-geolocation: 48.8567,2.3522\n geolocation: 48.8567,2.3522\n cpu/energy: 0.0054492484351820105\n carbon: 9.365452617417297\n aggregated:\n carbon: 76.1401459620588\n outputs:\n - carbon: 15.574155178030272\n timestamp: '2024-03-05T00:00:00.000Z'\n duration: 300\n - carbon: 35.0330691407141\n timestamp: '2024-03-05T00:05:00.000Z'\n duration: 300\n - carbon: 16.16746902589712\n timestamp: '2024-03-05T00:10:00.000Z'\n duration: 300\n - carbon: 9.365452617417297\n timestamp: '2024-03-05T00:15:00.000Z'\n duration: 300\n aggregated:\n carbon: 76.1401459620588\n outputs:\n - carbon: 22.57047993820384\n timestamp: '2024-03-05T00:00:00.000Z'\n duration: 300\n - carbon: 36.71212369757613\n timestamp: '2024-03-05T00:05:00.000Z'\n duration: 300\n - carbon: 22.63469416910948\n timestamp: '2024-03-05T00:10:00.000Z'\n duration: 300\n - carbon: 13.87483503749007\n timestamp: '2024-03-05T00:15:00.000Z'\n duration: 300\n aggregated:\n carbon: 95.79213284237952\n")),(0,r.kt)("h2",{id:"csv-and-aggregation"},"CSV and aggregation"),(0,r.kt)("p",null,'The CSV representation of the output data is helpful for intuiting how the aggregation procedure works. What we refer to as "horizontal" aggregation is really an aggregation of the ',(0,r.kt)("em",{parentName:"p"},"rows"),' of the CSV. You can replicate the IF aggregation function by summing the cells in each row of the CSV. Similarly, what we refer to as "vertical" aggregation can be replicatd by summing the ',(0,r.kt)("em",{parentName:"p"},"columns")," in the CSV representation (this is not ",(0,r.kt)("em",{parentName:"p"},"exactly")," accurate because you have to skip summing both parent nodes and their children, both of which are represented in the CSV, but it is true conceptually)."),(0,r.kt)("h2",{id:"walkthrough"},"Walkthrough"),(0,r.kt)("p",null,"You can start with a demo yaml, such as the following (save this as ",(0,r.kt)("inlineCode",{parentName:"p"},"pipeline-demo.yml"),"):"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},'name: pipeline-demo\ndescription:\ntags:\ninitialize:\n outputs:\n - csv\n plugins:\n boavizta-cpu:\n method: BoaviztaCpuOutput\n path: "@grnsft/if-unofficial-plugins"\n global-config:\n allocation: LINEAR\n verbose: true\n "sum":\n path: "@grnsft/if-plugins"\n method: Sum\n global-config:\n input-parameters:\n - cpu/energy\n - network/energy\n output-parameter: energy\n "sci-m":\n path: "@grnsft/if-plugins"\n method: SciM\n "sci-o":\n path: "@grnsft/if-plugins"\n method: SciO\ntree:\n children:\n child-1:\n pipeline:\n - boavizta-cpu\n - sum\n - sci-m\n - sci-o\n config:\n defaults:\n cpu/thermal-design-power: 100\n grid/carbon-intensity: 800\n device/emissions-embodied: 1533.120 # gCO2eq\n time-reserved: 3600 # 1hr in seconds\n device/expected-lifespan: 94608000 # 3 years in seconds\n resources-reserved: 1\n resources-total: 8\n cpu/number-cores: 24\n cpu/name: Intel\xae Core\u2122 i7-1185G7\n inputs:\n - timestamp: "2023-12-12T00:00:00.000Z"\n cloud/instance-type: A1\n region: uk-west\n duration: 1\n cpu/utilization: 50\n network/energy: 0.000001\n - timestamp: "2023-12-12T00:00:01.000Z"\n duration: 5\n cpu/utilization: 20\n cloud/instance-type: A1\n region: uk-west\n network/energy: 0.000001\n - timestamp: "2023-12-12T00:00:06.000Z"\n duration: 7\n cpu/utilization: 15\n cloud/instance-type: A1\n region: uk-west\n network/energy: 0.000001\n - timestamp: "2023-12-12T00:00:13.000Z"\n duration: 30\n cloud/instance-type: A1\n region: uk-west\n cpu/utilization: 15\n network/energy: 0.000001\n')),(0,r.kt)("p",null,"Run this using:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest pipeline-demo.yml --output pipeline-demo#carbon\n")),(0,r.kt)("p",null,"This will save a csv file called ",(0,r.kt)("inlineCode",{parentName:"p"},"pipeline-demo.csv"),". Inside, the data will look as follows:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"Path ")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"2023-12-12T00:00:00.000Z")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"2023-12-12T00:00:01.000Z")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"2023-12-12T00:00:06.000Z")),(0,r.kt)("td",{parentName:"tr",align:null},"b")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"tree.children.child-1.energy"),(0,r.kt)("td",{parentName:"tr",align:null},"0.00002877777777777778"),(0,r.kt)("td",{parentName:"tr",align:null},"0.00011211111111111111"),(0,r.kt)("td",{parentName:"tr",align:null},"0.0002787777777777778"),(0,r.kt)("td",{parentName:"tr",align:null},"0.0005565555555555556")))))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/090462b1.96edb2cc.js b/assets/js/090462b1.96edb2cc.js deleted file mode 100644 index 77c8271b..00000000 --- a/assets/js/090462b1.96edb2cc.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[941],{4137:(n,e,t)=>{t.d(e,{Zo:()=>d,kt:()=>m});var a=t(7294);function r(n,e,t){return e in n?Object.defineProperty(n,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):n[e]=t,n}function o(n,e){var t=Object.keys(n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(n);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),t.push.apply(t,a)}return t}function i(n){for(var e=1;e=0||(r[t]=n[t]);return r}(n,e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(n);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(n,t)&&(r[t]=n[t])}return r}var u=a.createContext({}),c=function(n){var e=a.useContext(u),t=e;return n&&(t="function"==typeof n?n(e):i(i({},e),n)),t},d=function(n){var e=c(n.components);return a.createElement(u.Provider,{value:e},n.children)},s={inlineCode:"code",wrapper:function(n){var e=n.children;return a.createElement(a.Fragment,{},e)}},p=a.forwardRef((function(n,e){var t=n.components,r=n.mdxType,o=n.originalType,u=n.parentName,d=l(n,["components","mdxType","originalType","parentName"]),p=c(t),m=r,g=p["".concat(u,".").concat(m)]||p[m]||s[m]||o;return t?a.createElement(g,i(i({ref:e},d),{},{components:t})):a.createElement(g,i({ref:e},d))}));function m(n,e){var t=arguments,r=e&&e.mdxType;if("string"==typeof n||r){var o=t.length,i=new Array(o);i[0]=p;var l={};for(var u in e)hasOwnProperty.call(e,u)&&(l[u]=e[u]);l.originalType=n,l.mdxType="string"==typeof n?n:r,i[1]=l;for(var c=2;c{t.r(e),t.d(e,{assets:()=>u,contentTitle:()=>i,default:()=>s,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var a=t(7462),r=(t(7294),t(4137));const o={sidebar_position:5},i="How to export to CSV",l={unversionedId:"users/how-to-export-to-csv",id:"users/how-to-export-to-csv",title:"How to export to CSV",description:"IF supports exporting data to CSV files. This provides users with a data format that enables visualization and data analysis using standard data analysis tools.",source:"@site/docs/users/how-to-export-to-csv.md",sourceDirName:"users",slug:"/users/how-to-export-to-csv",permalink:"/users/how-to-export-to-csv",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/users/how-to-export-to-csv.md",tags:[],version:"current",sidebarPosition:5,frontMatter:{sidebar_position:5},sidebar:"tutorialSidebar",previous:{title:"How to write a manifest file",permalink:"/users/how-to-write-manifests"},next:{title:"Developers",permalink:"/developers/"}},u={},c=[{value:"Manifest config",id:"manifest-config",level:2},{value:"CLI command",id:"cli-command",level:2},{value:"Comparing CSV to Yaml",id:"comparing-csv-to-yaml",level:2},{value:"CSV and aggregation",id:"csv-and-aggregation",level:2},{value:"Walkthrough",id:"walkthrough",level:2}],d={toc:c};function s(n){let{components:e,...t}=n;return(0,r.kt)("wrapper",(0,a.Z)({},d,t,{components:e,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"how-to-export-to-csv"},"How to export to CSV"),(0,r.kt)("p",null,"IF supports exporting data to CSV files. This provides users with a data format that enables visualization and data analysis using standard data analysis tools."),(0,r.kt)("h2",{id:"manifest-config"},"Manifest config"),(0,r.kt)("p",null,"To export your data to a CSV file, you have to provide a small piece of config data to your manifest file:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n outputs:\n - csv\n")),(0,r.kt)("p",null,"You can also add ",(0,r.kt)("inlineCode",{parentName:"p"},"- yaml")," if you want to export to both ",(0,r.kt)("inlineCode",{parentName:"p"},"yaml")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"csv")," simultaneously."),(0,r.kt)("h2",{id:"cli-command"},"CLI command"),(0,r.kt)("p",null,"Then, you must select the metric you want to export to CSV. The name of that metric must be added to the savepath provided to the ",(0,r.kt)("inlineCode",{parentName:"p"},"--output")," command in the CLI, after a hashtag."),(0,r.kt)("p",null,"For example, to export the ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," data from your tree to a CSV file:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest example.yml --output example#carbon\n")),(0,r.kt)("p",null,"This will save a CSV file called ",(0,r.kt)("inlineCode",{parentName:"p"},"example.csv"),". The contents will look similar to the following:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"Path")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"Aggregated")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"2024-03-05T00:00:00.000Z")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"2024-03-05T00:05:00.000Z")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"2024-03-05T00:10:00.000Z"))),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"tree.carbon"),(0,r.kt)("td",{parentName:"tr",align:null},"425.289232008725"),(0,r.kt)("td",{parentName:"tr",align:null},"17.9269877157543"),(0,r.kt)("td",{parentName:"tr",align:null},"8.9024388783018"),(0,r.kt)("td",{parentName:"tr",align:null},"45.6021901509012")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"tree.children.westus3.carbon"),(0,r.kt)("td",{parentName:"tr",align:null},"104.696836722878"),(0,r.kt)("td",{parentName:"tr",align:null},"3.59973803197887"),(0,r.kt)("td",{parentName:"tr",align:null},"3.47438149032372"),(0,r.kt)("td",{parentName:"tr",align:null},"6.91318436533634")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"tree.children.westus3.children.server-1.carbon"),(0,r.kt)("td",{parentName:"tr",align:null},"104.696836722878"),(0,r.kt)("td",{parentName:"tr",align:null},"3.59973803197887"),(0,r.kt)("td",{parentName:"tr",align:null},"3.47438149032372"),(0,r.kt)("td",{parentName:"tr",align:null},"6.91318436533634")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"tree.children.france.carbon"),(0,r.kt)("td",{parentName:"tr",align:null},"320.592395285847"),(0,r.kt)("td",{parentName:"tr",align:null},"14.3272496837754"),(0,r.kt)("td",{parentName:"tr",align:null},"5.42805738797808"),(0,r.kt)("td",{parentName:"tr",align:null},"38.6890057855649")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"tree.children.france.children.server-2.carbon"),(0,r.kt)("td",{parentName:"tr",align:null},"320.592395285847"),(0,r.kt)("td",{parentName:"tr",align:null},"14.3272496837754"),(0,r.kt)("td",{parentName:"tr",align:null},"5.42805738797808"),(0,r.kt)("td",{parentName:"tr",align:null},"38.6890057855649")))),(0,r.kt)("h2",{id:"comparing-csv-to-yaml"},"Comparing CSV to Yaml"),(0,r.kt)("p",null,"The CSV above is generated from the following yaml. The ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," metric is extracted and added to the CSV. Otherwise,the CSV is an exact representation of the following yaml tree. You can see that the CSV reepresentation is ",(0,r.kt)("em",{parentName:"p"},"much")," easier to understand than the full yaml tree:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n pipeline:\n - mock-observations\n - group-by\n - cloud-metadata\n - time-sync\n - watttime\n - teads-curve\n - operational-carbon\n defaults:\n grid/carbon-intensity: 500\n config:\n group-by:\n group:\n - cloud/region\n - name\n children:\n westus3:\n children:\n server-1:\n inputs:\n - timestamp: '2024-03-05T00:00:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 66\n grid/carbon-intensity: 500\n - timestamp: '2024-03-05T00:05:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 4\n grid/carbon-intensity: 500\n - timestamp: '2024-03-05T00:10:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 54\n grid/carbon-intensity: 500\n - timestamp: '2024-03-05T00:15:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 19\n grid/carbon-intensity: 500\n outputs:\n - timestamp: '2024-03-05T00:00:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 65.78\n grid/carbon-intensity: 369.4947514218548\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: CAISO\n cloud/region-em-zone-id: US-CAL-CISO\n cloud/region-wt-id: CAISO_NORTH\n cloud/region-location: US West (N. California)\n cloud/region-geolocation: 34.0497,-118.1326\n geolocation: 34.0497,-118.1326\n cpu/energy: 0.018934842060004835\n carbon: 6.996324760173567\n - timestamp: '2024-03-05T00:05:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 3.986666666666667\n grid/carbon-intensity: 369.38452029076234\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: CAISO\n cloud/region-em-zone-id: US-CAL-CISO\n cloud/region-wt-id: CAISO_NORTH\n cloud/region-location: US West (N. California)\n cloud/region-geolocation: 34.0497,-118.1326\n geolocation: 34.0497,-118.1326\n cpu/energy: 0.004545546617763956\n carbon: 1.6790545568620359\n - timestamp: '2024-03-05T00:10:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 53.82\n grid/carbon-intensity: 372.58122309244305\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: CAISO\n cloud/region-em-zone-id: US-CAL-CISO\n cloud/region-wt-id: CAISO_NORTH\n cloud/region-location: US West (N. California)\n cloud/region-geolocation: 34.0497,-118.1326\n geolocation: 34.0497,-118.1326\n cpu/energy: 0.017357893372978016\n carbon: 6.467225143212361\n - timestamp: '2024-03-05T00:15:00.000Z'\n duration: 300\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/utilization: 18.936666666666667\n grid/carbon-intensity: 434.20042537311633\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: CAISO\n cloud/region-em-zone-id: US-CAL-CISO\n cloud/region-wt-id: CAISO_NORTH\n cloud/region-location: US West (N. California)\n cloud/region-geolocation: 34.0497,-118.1326\n geolocation: 34.0497,-118.1326\n cpu/energy: 0.010385485956624245\n carbon: 4.5093824200727735\n aggregated:\n carbon: 19.651986880320734\n outputs:\n - carbon: 6.996324760173567\n timestamp: '2024-03-05T00:00:00.000Z'\n duration: 300\n - carbon: 1.6790545568620359\n timestamp: '2024-03-05T00:05:00.000Z'\n duration: 300\n - carbon: 6.467225143212361\n timestamp: '2024-03-05T00:10:00.000Z'\n duration: 300\n - carbon: 4.5093824200727735\n timestamp: '2024-03-05T00:15:00.000Z'\n duration: 300\n aggregated:\n carbon: 19.651986880320734\n france:\n children:\n server-2:\n inputs:\n - timestamp: '2024-03-05T00:00:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 15\n grid/carbon-intensity: 500\n - timestamp: '2024-03-05T00:05:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 78\n grid/carbon-intensity: 500\n - timestamp: '2024-03-05T00:10:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 16\n grid/carbon-intensity: 500\n - timestamp: '2024-03-05T00:15:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 6\n grid/carbon-intensity: 500\n outputs:\n - timestamp: '2024-03-05T00:00:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 14.95\n grid/carbon-intensity: 1719.1647205176753\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: France\n cloud/region-em-zone-id: FR\n cloud/region-wt-id: FR\n cloud/region-location: Paris\n cloud/region-geolocation: 48.8567,2.3522\n geolocation: 48.8567,2.3522\n cpu/energy: 0.00905914075141129\n carbon: 15.574155178030272\n - timestamp: '2024-03-05T00:05:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 77.74\n grid/carbon-intensity: 1719.0544893865829\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: France\n cloud/region-em-zone-id: FR\n cloud/region-wt-id: FR\n cloud/region-location: Paris\n cloud/region-geolocation: 48.8567,2.3522\n geolocation: 48.8567,2.3522\n cpu/energy: 0.020379266251888902\n carbon: 35.0330691407141\n - timestamp: '2024-03-05T00:10:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 15.946666666666667\n grid/carbon-intensity: 1718.8707708347622\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: France\n cloud/region-em-zone-id: FR\n cloud/region-wt-id: FR\n cloud/region-location: Paris\n cloud/region-geolocation: 48.8567,2.3522\n geolocation: 48.8567,2.3522\n cpu/energy: 0.009405866514354337\n carbon: 16.16746902589712\n - timestamp: '2024-03-05T00:15:00.000Z'\n duration: 300\n name: server-2\n cloud/instance-type: Standard_E64_v3\n cloud/region: france\n cloud/vendor: azure\n cpu/utilization: 5.98\n grid/carbon-intensity: 1718.6686804277592\n vcpus-allocated: 64\n vcpus-total: 64\n memory-available: 432\n physical-processor: >-\n Intel\xae Xeon\xae Platinum 8370C,Intel\xae Xeon\xae Platinum 8272CL,Intel\xae\n Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae E5-2673 v4 2.3 GHz\n cpu/thermal-design-power: 269.1\n cloud/region-cfe: France\n cloud/region-em-zone-id: FR\n cloud/region-wt-id: FR\n cloud/region-location: Paris\n cloud/region-geolocation: 48.8567,2.3522\n geolocation: 48.8567,2.3522\n cpu/energy: 0.0054492484351820105\n carbon: 9.365452617417297\n aggregated:\n carbon: 76.1401459620588\n outputs:\n - carbon: 15.574155178030272\n timestamp: '2024-03-05T00:00:00.000Z'\n duration: 300\n - carbon: 35.0330691407141\n timestamp: '2024-03-05T00:05:00.000Z'\n duration: 300\n - carbon: 16.16746902589712\n timestamp: '2024-03-05T00:10:00.000Z'\n duration: 300\n - carbon: 9.365452617417297\n timestamp: '2024-03-05T00:15:00.000Z'\n duration: 300\n aggregated:\n carbon: 76.1401459620588\n outputs:\n - carbon: 22.57047993820384\n timestamp: '2024-03-05T00:00:00.000Z'\n duration: 300\n - carbon: 36.71212369757613\n timestamp: '2024-03-05T00:05:00.000Z'\n duration: 300\n - carbon: 22.63469416910948\n timestamp: '2024-03-05T00:10:00.000Z'\n duration: 300\n - carbon: 13.87483503749007\n timestamp: '2024-03-05T00:15:00.000Z'\n duration: 300\n aggregated:\n carbon: 95.79213284237952\n")),(0,r.kt)("h2",{id:"csv-and-aggregation"},"CSV and aggregation"),(0,r.kt)("p",null,'The CSV representation of the output data is helpful for intuiting how the aggregation procedure works. What we refer to as "horizontal" aggregation is really an aggregation of the ',(0,r.kt)("em",{parentName:"p"},"rows"),' of the CSV. You can replicate the IF aggregation function by summing the cells in each row of the CSV. Similarly, what we refer to as "vertical" aggregation can be replicatd by summing the ',(0,r.kt)("em",{parentName:"p"},"columns")," in the CSV representation (this is not ",(0,r.kt)("em",{parentName:"p"},"exactly")," accurate because you have to skip summing both parent nodes and their children, both of which are represented in the CSV, but it is true conceptually)."),(0,r.kt)("h2",{id:"walkthrough"},"Walkthrough"),(0,r.kt)("p",null,"You can start with a demo yaml, such as the following (save this as ",(0,r.kt)("inlineCode",{parentName:"p"},"pipeline-demo.yml"),"):"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},'name: pipeline-demo\ndescription:\ntags:\ninitialize:\n outputs:\n - csv\n plugins:\n boavizta-cpu:\n method: BoaviztaCpuOutput\n path: "@grnsft/if-unofficial-plugins"\n global-config:\n allocation: LINEAR\n verbose: true\n "sum":\n path: "@grnsft/if-plugins"\n method: Sum\n global-config:\n input-parameters:\n - cpu/energy\n - network/energy\n output-parameter: energy\n "sci-m":\n path: "@grnsft/if-plugins"\n method: SciM\n "sci-o":\n path: "@grnsft/if-plugins"\n method: SciO\ntree:\n children:\n child-1:\n pipeline:\n - boavizta-cpu\n - sum\n - sci-m\n - sci-o\n config:\n defaults:\n cpu/thermal-design-power: 100\n grid/carbon-intensity: 800\n device/emissions-embodied: 1533.120 # gCO2eq\n time-reserved: 3600 # 1hr in seconds\n device/expected-lifespan: 94608000 # 3 years in seconds\n resources-reserved: 1\n resources-total: 8\n cpu/number-cores: 24\n cpu/name: Intel\xae Core\u2122 i7-1185G7\n inputs:\n - timestamp: "2023-12-12T00:00:00.000Z"\n cloud/instance-type: A1\n region: uk-west\n duration: 1\n cpu/utilization: 50\n network/energy: 0.000001\n - timestamp: "2023-12-12T00:00:01.000Z"\n duration: 5\n cpu/utilization: 20\n cloud/instance-type: A1\n region: uk-west\n network/energy: 0.000001\n - timestamp: "2023-12-12T00:00:06.000Z"\n duration: 7\n cpu/utilization: 15\n cloud/instance-type: A1\n region: uk-west\n network/energy: 0.000001\n - timestamp: "2023-12-12T00:00:13.000Z"\n duration: 30\n cloud/instance-type: A1\n region: uk-west\n cpu/utilization: 15\n network/energy: 0.000001\n')),(0,r.kt)("p",null,"Run this using:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest pipeline-demo.yml --output pipeline-demo#carbon\n")),(0,r.kt)("p",null,"This will save a csv file called ",(0,r.kt)("inlineCode",{parentName:"p"},"pipeline-demo.csv"),". Inside, the data will look as follows:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}),(0,r.kt)("th",{parentName:"tr",align:null}))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"Path ")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"2023-12-12T00:00:00.000Z")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"2023-12-12T00:00:01.000Z")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"2023-12-12T00:00:06.000Z")),(0,r.kt)("td",{parentName:"tr",align:null},"b")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"tree.children.child-1.energy"),(0,r.kt)("td",{parentName:"tr",align:null},"0.00002877777777777778"),(0,r.kt)("td",{parentName:"tr",align:null},"0.00011211111111111111"),(0,r.kt)("td",{parentName:"tr",align:null},"0.0002787777777777778"),(0,r.kt)("td",{parentName:"tr",align:null},"0.0005565555555555556")))))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0a19367a.0190a3ab.js b/assets/js/0a19367a.0190a3ab.js deleted file mode 100644 index 65137c7d..00000000 --- a/assets/js/0a19367a.0190a3ab.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[109],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>d});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},u=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),c=p(n),d=i,f=c["".concat(s,".").concat(d)]||c[d]||m[d]||r;return n?a.createElement(f,l(l({ref:t},u),{},{components:n})):a.createElement(f,l({ref:t},u))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,l=new Array(r);l[0]=c;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o.mdxType="string"==typeof e?e:i,l[1]=o;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>m,frontMatter:()=>r,metadata:()=>o,toc:()=>p});var a=n(7462),i=(n(7294),n(4137));const r={sidebar_position:1},l="Quick start",o={unversionedId:"users/quick-start",id:"users/quick-start",title:"Quick start",description:"This page will provide the basic instructions for getting up and running with Impact Framework.",source:"@site/docs/users/quick-start.md",sourceDirName:"users",slug:"/users/quick-start",permalink:"/users/quick-start",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/users/quick-start.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Users",permalink:"/users/"},next:{title:"How to install Impact Framework",permalink:"/users/how-to-install-if"}},s={},p=[{value:"1: Install Impact Framework",id:"1-install-impact-framework",level:2},{value:"2: Install some plugins",id:"2-install-some-plugins",level:2},{value:"3: Create a manifest file",id:"3-create-a-manifest-file",level:2},{value:"4: Compute your manifest file",id:"4-compute-your-manifest-file",level:2},{value:"Next steps",id:"next-steps",level:2}],u={toc:p};function m(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"quick-start"},"Quick start"),(0,i.kt)("p",null,"This page will provide the basic instructions for getting up and running with Impact Framework."),(0,i.kt)("h2",{id:"1-install-impact-framework"},"1: Install Impact Framework"),(0,i.kt)("p",null,"Install the Impact Framework globally using npm."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm install -g @grnsft/if\n")),(0,i.kt)("p",null,"Read our detailed guide to ",(0,i.kt)("a",{parentName:"p",href:"/users/how-to-install-if"},"installing IF"),"."),(0,i.kt)("h2",{id:"2-install-some-plugins"},"2: Install some plugins"),(0,i.kt)("p",null,"Install some of the plugins you want to include in your pipeline. The following commands will install both the official and unofficial IF model packages."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm install -g @grnsft/if-plugins\nnpm install -g @grnsft/if-unofficial-plugins\n")),(0,i.kt)("p",null,"Read our detailed guide to ",(0,i.kt)("a",{parentName:"p",href:"/users/how-to-import-plugins"},"loading plugins"),"."),(0,i.kt)("h2",{id:"3-create-a-manifest-file"},"3: Create a manifest file"),(0,i.kt)("p",null,"A manifest file contains all the configuration and input data required to measure your application's energy and carbon impacts and should have a ",(0,i.kt)("inlineCode",{parentName:"p"},".yml")," extension. "),(0,i.kt)("p",null,"Open the file, add your data and save the file. The simple example below runs a single snapshot observation through a single plugin."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: basic-demo\ndescription:\ntags:\ninitialize:\n plugins:\n teads-curve: \n path: '@grnsft/if-unofficial-plugins'\n method: TeadsCurve\n global-config:\n interpolation: spline\ntree:\n children:\n child-0:\n defaults:\n cpu/thermal-design-power: 100\n pipeline:\n - teads-curve\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 1\n cpu/utilization: 20\n - timestamp: 2023-07-06T00:01\n duration: 1\n cpu/utilization: 80\n - timestamp: 2023-07-06T00:02\n duration: 1\n cpu/utilization: 20\n")),(0,i.kt)("p",null,"Read our detailed guide to ",(0,i.kt)("a",{parentName:"p",href:"/users/how-to-write-manifests"},"writing manifest files"),"."),(0,i.kt)("h2",{id:"4-compute-your-manifest-file"},"4: Compute your manifest file"),(0,i.kt)("p",null,"Run the pipeline by passing the path to your manifest file to the ",(0,i.kt)("inlineCode",{parentName:"p"},"ie")," command line tool:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest \n")),(0,i.kt)("p",null,"\ud83c\udf89",(0,i.kt)("strong",{parentName:"p"},"Congratulations")," \ud83c\udf89! You have just used the Impact Framework to compute the energy consumed by an application! "),(0,i.kt)("h2",{id:"next-steps"},"Next steps"),(0,i.kt)("p",null,"Now you know how to use the ",(0,i.kt)("inlineCode",{parentName:"p"},"ie")," you can start building more complex pipelines of plugins and more complicated manifest files. Your overall aim is to create a manifest file that accurately represents a real software application, and a plugin pipeline that yields an environmental metric that's important to you (e.g. ",(0,i.kt)("inlineCode",{parentName:"p"},"carbon"),")."),(0,i.kt)("p",null,"Experiment by adding more plugins to the pipeline, for example add ",(0,i.kt)("inlineCode",{parentName:"p"},"sci-o")," to convert energy into ",(0,i.kt)("inlineCode",{parentName:"p"},"operational-carbon"),". Your output data will be displayed in your console. "),(0,i.kt)("p",null,"You can also configure ",(0,i.kt)("inlineCode",{parentName:"p"},"if")," to save your output data to another ",(0,i.kt)("inlineCode",{parentName:"p"},"yaml")," file. To do this, add the ",(0,i.kt)("inlineCode",{parentName:"p"},"--output")," flag and the path to the output file where the results are saved. You will also need to add the following config to the ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block in your manifest file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"outputs:\n - yaml\n")),(0,i.kt)("p",null,"The command is then as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest --output \n")),(0,i.kt)("p",null,"Explore our user documentation for walkthrough guides to common Impact Framework tasks:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/users/how-to-install-if"},"How to install Impact Framework")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/users/how-to-import-plugins"},"How to load plugins")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/users/how-to-write-manifests"},"How to write manifest files"))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0a19367a.60ba1501.js b/assets/js/0a19367a.60ba1501.js new file mode 100644 index 00000000..c3a94bad --- /dev/null +++ b/assets/js/0a19367a.60ba1501.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[109],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>d});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},u=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),c=p(n),d=i,f=c["".concat(s,".").concat(d)]||c[d]||m[d]||r;return n?a.createElement(f,l(l({ref:t},u),{},{components:n})):a.createElement(f,l({ref:t},u))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,l=new Array(r);l[0]=c;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o.mdxType="string"==typeof e?e:i,l[1]=o;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>m,frontMatter:()=>r,metadata:()=>o,toc:()=>p});var a=n(7462),i=(n(7294),n(4137));const r={sidebar_position:1},l="Quick start",o={unversionedId:"users/quick-start",id:"users/quick-start",title:"Quick start",description:"This page will provide the basic instructions for getting up and running with Impact Framework.",source:"@site/docs/users/quick-start.md",sourceDirName:"users",slug:"/users/quick-start",permalink:"/users/quick-start",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/quick-start.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Users",permalink:"/users/"},next:{title:"How to install Impact Framework",permalink:"/users/how-to-install-if"}},s={},p=[{value:"1: Install Impact Framework",id:"1-install-impact-framework",level:2},{value:"2: Install some plugins",id:"2-install-some-plugins",level:2},{value:"3: Create a manifest file",id:"3-create-a-manifest-file",level:2},{value:"4: Compute your manifest file",id:"4-compute-your-manifest-file",level:2},{value:"Next steps",id:"next-steps",level:2}],u={toc:p};function m(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"quick-start"},"Quick start"),(0,i.kt)("p",null,"This page will provide the basic instructions for getting up and running with Impact Framework."),(0,i.kt)("h2",{id:"1-install-impact-framework"},"1: Install Impact Framework"),(0,i.kt)("p",null,"Install the Impact Framework globally using npm."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm install -g @grnsft/if\n")),(0,i.kt)("p",null,"Read our detailed guide to ",(0,i.kt)("a",{parentName:"p",href:"/users/how-to-install-if"},"installing IF"),"."),(0,i.kt)("h2",{id:"2-install-some-plugins"},"2: Install some plugins"),(0,i.kt)("p",null,"Install some of the plugins you want to include in your pipeline. The following commands will install both the official and unofficial IF model packages."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm install -g @grnsft/if-plugins\nnpm install -g @grnsft/if-unofficial-plugins\n")),(0,i.kt)("p",null,"Read our detailed guide to ",(0,i.kt)("a",{parentName:"p",href:"/users/how-to-import-plugins"},"loading plugins"),"."),(0,i.kt)("h2",{id:"3-create-a-manifest-file"},"3: Create a manifest file"),(0,i.kt)("p",null,"A manifest file contains all the configuration and input data required to measure your application's energy and carbon impacts and should have a ",(0,i.kt)("inlineCode",{parentName:"p"},".yml")," extension. "),(0,i.kt)("p",null,"Open the file, add your data and save the file. The simple example below runs a single snapshot observation through a single plugin."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: basic-demo\ndescription:\ntags:\ninitialize:\n plugins:\n teads-curve: \n path: '@grnsft/if-unofficial-plugins'\n method: TeadsCurve\n global-config:\n interpolation: spline\ntree:\n children:\n child-0:\n defaults:\n cpu/thermal-design-power: 100\n pipeline:\n - teads-curve\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 1\n cpu/utilization: 20\n - timestamp: 2023-07-06T00:01\n duration: 1\n cpu/utilization: 80\n - timestamp: 2023-07-06T00:02\n duration: 1\n cpu/utilization: 20\n")),(0,i.kt)("p",null,"Read our detailed guide to ",(0,i.kt)("a",{parentName:"p",href:"/users/how-to-write-manifests"},"writing manifest files"),"."),(0,i.kt)("h2",{id:"4-compute-your-manifest-file"},"4: Compute your manifest file"),(0,i.kt)("p",null,"Run the pipeline by passing the path to your manifest file to the ",(0,i.kt)("inlineCode",{parentName:"p"},"ie")," command line tool:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest \n")),(0,i.kt)("p",null,"\ud83c\udf89",(0,i.kt)("strong",{parentName:"p"},"Congratulations")," \ud83c\udf89! You have just used the Impact Framework to compute the energy consumed by an application! "),(0,i.kt)("h2",{id:"next-steps"},"Next steps"),(0,i.kt)("p",null,"Now you know how to use the ",(0,i.kt)("inlineCode",{parentName:"p"},"ie")," you can start building more complex pipelines of plugins and more complicated manifest files. Your overall aim is to create a manifest file that accurately represents a real software application, and a plugin pipeline that yields an environmental metric that's important to you (e.g. ",(0,i.kt)("inlineCode",{parentName:"p"},"carbon"),")."),(0,i.kt)("p",null,"Experiment by adding more plugins to the pipeline, for example add ",(0,i.kt)("inlineCode",{parentName:"p"},"sci-o")," to convert energy into ",(0,i.kt)("inlineCode",{parentName:"p"},"operational-carbon"),". Your output data will be displayed in your console. "),(0,i.kt)("p",null,"You can also configure ",(0,i.kt)("inlineCode",{parentName:"p"},"if")," to save your output data to another ",(0,i.kt)("inlineCode",{parentName:"p"},"yaml")," file. To do this, add the ",(0,i.kt)("inlineCode",{parentName:"p"},"--output")," flag and the path to the output file where the results are saved. You will also need to add the following config to the ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block in your manifest file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"outputs:\n - yaml\n")),(0,i.kt)("p",null,"The command is then as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest --output \n")),(0,i.kt)("p",null,"Explore our user documentation for walkthrough guides to common Impact Framework tasks:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/users/how-to-install-if"},"How to install Impact Framework")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/users/how-to-import-plugins"},"How to load plugins")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/users/how-to-write-manifests"},"How to write manifest files"))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0e384e19.96f019ec.js b/assets/js/0e384e19.96f019ec.js deleted file mode 100644 index 5214f502..00000000 --- a/assets/js/0e384e19.96f019ec.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[671],{4137:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var o=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=o.createContext({}),p=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=p(e.components);return o.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},d=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(n),h=a,m=d["".concat(l,".").concat(h)]||d[h]||u[h]||r;return n?o.createElement(m,i(i({ref:t},c),{},{components:n})):o.createElement(m,i({ref:t},c))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,i=new Array(r);i[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:a,i[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var o=n(7462),a=(n(7294),n(4137));const r={sidebar_position:1},i="Introduction",s={unversionedId:"intro",id:"intro",title:"Introduction",description:"Hackathon!",source:"@site/docs/intro.md",sourceDirName:".",slug:"/intro",permalink:"/intro",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/intro.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",next:{title:"Major Concepts",permalink:"/major-concepts/"}},l={},p=[{value:"Hackathon!",id:"hackathon",level:2},{value:"Impact Framework",id:"impact-framework",level:2},{value:"Motivation",id:"motivation",level:2},{value:"Background",id:"background",level:2},{value:"Project Structure",id:"project-structure",level:2},{value:"Navigating these docs",id:"navigating-these-docs",level:2}],c={toc:p};function u(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,o.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"introduction"},"Introduction"),(0,a.kt)("h2",{id:"hackathon"},"Hackathon!"),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(5846).Z,width:"1128",height:"191"})),(0,a.kt)("hr",null),(0,a.kt)("p",null,"From March 18 to April 8, 2024, participants will compete to showcase their best application of IF in measuring the environmental impacts of software. "),(0,a.kt)("p",null,"Carbon Hack is a dynamic competition that combines healthy rivalry with collaborative innovation. Hackers will push the limits of the framework, uncover potential weaknesses, and create innovations to enhance the tool."),(0,a.kt)("p",null,"CarbonHack is open to all, including software practitioners and those with a passion for Green Software.\nFind out more about CarbonHack 2024 on the ",(0,a.kt)("a",{parentName:"p",href:"https://grnsft.org/hack/github"},"CarbonHack website")),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"Registration opens 22nd January!")),(0,a.kt)("hr",null),(0,a.kt)("br",null),(0,a.kt)("h2",{id:"impact-framework"},"Impact Framework"),(0,a.kt)("p",null,"Impact Framework (IF) aims to make the environmental impacts of software easier to calculate ",(0,a.kt)("strong",{parentName:"p"},"and")," share."),(0,a.kt)("p",null,"IF allows you to calculate the environmental impacts, such as carbon, of your software applications without writing any code. All you have to do is write a simple ",(0,a.kt)("strong",{parentName:"p"},"manifest file")," and IF handles the rest."),(0,a.kt)("p",null,"The project is entirely open source and composability is a core design principle - we want you to be able to create your own plugins and plug them in to our framework, or pick from a broad universe of open source plugins created by others."),(0,a.kt)("h2",{id:"motivation"},"Motivation"),(0,a.kt)("p",null,"If you can't measure, you can't improve. Software has many negative environmental ",(0,a.kt)("strong",{parentName:"p"},"impacts")," which we need to optimize, carbon, water, and energy, to name just a few."),(0,a.kt)("p",null,"Unfortunately, measuring software impact metrics like carbon, water, and energy is complex and nuanced. "),(0,a.kt)("p",null,"Modern applications are composed of many smaller pieces of software (components) running on different environments, for example, private cloud, public cloud, bare-metal, virtualized, containerized, mobile, laptops, desktops, embedded, and IoT. Many components that make up a typical software application are run on something other than resources you own or control, which makes including the impact of managed services in your measurement especially hard. "),(0,a.kt)("p",null,"The impacts of software components also vary over time, so as well as understanding ",(0,a.kt)("strong",{parentName:"p"},"which")," components contribute most to the overall impacts, there is also a question of ",(0,a.kt)("strong",{parentName:"p"},"when")," they contribute the most."),(0,a.kt)("p",null,"Only through a granular analysis of the impacts of your software system can investments in reducing its impact be prioritized and verified. Measurement is the first and most crucial step in greening a software system, and the first step in that process with the Impact Framework is to create a tree."),(0,a.kt)("h2",{id:"background"},"Background"),(0,a.kt)("p",null,"This project has evolved over the two years of the GSF's existence. "),(0,a.kt)("p",null,"During the development of the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/sci/blob/dev/SPEC.md"},"SCI"),", we acknowledged that the biggest blocker to adoption was data regarding the emissions of software components on different platforms and runtimes."),(0,a.kt)("p",null,"We then launched the sci-data project to help create the data sets required to calculate an SCI score."),(0,a.kt)("p",null,"After some investigation, the original sci-data team quickly realized that there were several existing data sources, and many more were in development, free open source or private commercial. The future challenge wouldn't be to source them, it would be knowing which data set to use for which use case, how data sets differed in their methodology and interface and when to use one over the other, the pros/cons, and trade-offs."),(0,a.kt)("p",null,"The project evolved into the ",(0,a.kt)("a",{parentName:"p",href:"https://sci-guide.greensoftware.foundation/"},"sci-guide")," to document existing data sets, providing guidance for when to use one over another and how to use it to create your own software measurement reports."),(0,a.kt)("p",null,"Finally, we had enough information, and ",(0,a.kt)("a",{parentName:"p",href:"https://sci-guide.greensoftware.foundation/CaseStudies"},"SCI case studies")," started to be written. This was a milestone moment."),(0,a.kt)("p",null,"But now we are in the next evolution, to have software measurement be a mainstream activity. For this to be an industry with thousands of professionals working to decarbonize software, for businesses to grow and thrive in a commercial software measurement ecosystem, we need to formalize software measurement into a discipline with standards and tooling. The SCI Specification is the standard, and the ",(0,a.kt)("a",{parentName:"p",href:"./06-specification/impact-framework.md"},"Impact Framework")," is the tooling."),(0,a.kt)("h2",{id:"project-structure"},"Project Structure"),(0,a.kt)("p",null,"The ",(0,a.kt)("strong",{parentName:"p"},"IF source code")," can be found in the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if"},"IF Github repository"),". The code there covers the framework, which includes all the infrastructure for reading and writing input and output yamls, invoking plugins, running the command line tool and associated helper functions. However, it does not include the actual plugins themselves. Part of the IF design philosophy is that all plugins should be plugins, so that the IF is as composable and configurable as possible. Therefore, to use IF, you have to either create your own plugins or find some prebuilt ones and install them yourself. This also implies that you take responsibility for the plugins you choose to install."),(0,a.kt)("p",null,"We do provide a ",(0,a.kt)("strong",{parentName:"p"},"standard library of plugins")," built and maintained by the IF core team. These can be found in the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-plugins"},(0,a.kt)("inlineCode",{parentName:"a"},"if-plugins")," Github repository"),". You can install these into ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," by running ",(0,a.kt)("inlineCode",{parentName:"p"},"npm install https://github.com/Green-Software-Foundation/if-plugins")," from the ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," project directory."),(0,a.kt)("p",null,"There is also a second repository for ",(0,a.kt)("strong",{parentName:"p"},"plugins we expect community members to maintain"),". These can be found in the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins"},(0,a.kt)("inlineCode",{parentName:"a"},"if-unofficial-plugins")," Github repository"),". You can install these into ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," by running ",(0,a.kt)("inlineCode",{parentName:"p"},"npm install https://github.com/Green-Software-Foundation/if-unofficial-plugins")," from the ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," project directory."),(0,a.kt)("p",null,"Finally, the ",(0,a.kt)("strong",{parentName:"p"},"source code for this documentation")," website is available at the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-docs"},(0,a.kt)("inlineCode",{parentName:"a"},"if-docs")," Github repository"),"."),(0,a.kt)("h2",{id:"navigating-these-docs"},"Navigating these docs"),(0,a.kt)("p",null,"The lefthand sidebar contains links to all the information you need to understand Impact Framework. "),(0,a.kt)("p",null,"You can explore the key ideas underpinning Impact Framework in the ",(0,a.kt)("a",{parentName:"p",href:"./major-concepts/"},"Major Concepts section"),"."),(0,a.kt)("p",null,"Users can read our ",(0,a.kt)("a",{parentName:"p",href:"./users/"},"guides")," explaining how to use IF, including installation, using the CLI and loading plugins."),(0,a.kt)("p",null,"We also have ",(0,a.kt)("a",{parentName:"p",href:"./developers/"},"developer documentation")," to help you get started building with IF."),(0,a.kt)("p",null,"You will find documentation for the individual built-in plugin implementations in ",(0,a.kt)("a",{parentName:"p",href:"/reference/plugins"},(0,a.kt)("inlineCode",{parentName:"a"},"plugins")),"."))}u.isMDXComponent=!0},5846:(e,t,n)=>{n.d(t,{Z:()=>o});const o=n.p+"assets/images/hack-banner-8a9d7874b60972c27f9bb38f4170f34f.png"}}]); \ No newline at end of file diff --git a/assets/js/0e384e19.a31cf92b.js b/assets/js/0e384e19.a31cf92b.js new file mode 100644 index 00000000..0afc623d --- /dev/null +++ b/assets/js/0e384e19.a31cf92b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[671],{4137:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var o=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=o.createContext({}),p=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=p(e.components);return o.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},d=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(n),h=a,m=d["".concat(l,".").concat(h)]||d[h]||u[h]||r;return n?o.createElement(m,i(i({ref:t},c),{},{components:n})):o.createElement(m,i({ref:t},c))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,i=new Array(r);i[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:a,i[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var o=n(7462),a=(n(7294),n(4137));const r={sidebar_position:1},i="Introduction",s={unversionedId:"intro",id:"intro",title:"Introduction",description:"Hackathon!",source:"@site/docs/intro.md",sourceDirName:".",slug:"/intro",permalink:"/intro",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/intro.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",next:{title:"Major Concepts",permalink:"/major-concepts/"}},l={},p=[{value:"Hackathon!",id:"hackathon",level:2},{value:"Impact Framework",id:"impact-framework",level:2},{value:"Motivation",id:"motivation",level:2},{value:"Background",id:"background",level:2},{value:"Project Structure",id:"project-structure",level:2},{value:"Navigating these docs",id:"navigating-these-docs",level:2}],c={toc:p};function u(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,o.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"introduction"},"Introduction"),(0,a.kt)("h2",{id:"hackathon"},"Hackathon!"),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(5846).Z,width:"1128",height:"191"})),(0,a.kt)("hr",null),(0,a.kt)("p",null,"From March 18 to April 8, 2024, participants will compete to showcase their best application of IF in measuring the environmental impacts of software. "),(0,a.kt)("p",null,"Carbon Hack is a dynamic competition that combines healthy rivalry with collaborative innovation. Hackers will push the limits of the framework, uncover potential weaknesses, and create innovations to enhance the tool."),(0,a.kt)("p",null,"CarbonHack is open to all, including software practitioners and those with a passion for Green Software.\nFind out more about CarbonHack 2024 on the ",(0,a.kt)("a",{parentName:"p",href:"https://grnsft.org/hack/github"},"CarbonHack website")),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"Registration opens 22nd January!")),(0,a.kt)("hr",null),(0,a.kt)("br",null),(0,a.kt)("h2",{id:"impact-framework"},"Impact Framework"),(0,a.kt)("p",null,"Impact Framework (IF) aims to make the environmental impacts of software easier to calculate ",(0,a.kt)("strong",{parentName:"p"},"and")," share."),(0,a.kt)("p",null,"IF allows you to calculate the environmental impacts, such as carbon, of your software applications without writing any code. All you have to do is write a simple ",(0,a.kt)("strong",{parentName:"p"},"manifest file")," and IF handles the rest."),(0,a.kt)("p",null,"The project is entirely open source and composability is a core design principle - we want you to be able to create your own plugins and plug them in to our framework, or pick from a broad universe of open source plugins created by others."),(0,a.kt)("h2",{id:"motivation"},"Motivation"),(0,a.kt)("p",null,"If you can't measure, you can't improve. Software has many negative environmental ",(0,a.kt)("strong",{parentName:"p"},"impacts")," which we need to optimize, carbon, water, and energy, to name just a few."),(0,a.kt)("p",null,"Unfortunately, measuring software impact metrics like carbon, water, and energy is complex and nuanced. "),(0,a.kt)("p",null,"Modern applications are composed of many smaller pieces of software (components) running on different environments, for example, private cloud, public cloud, bare-metal, virtualized, containerized, mobile, laptops, desktops, embedded, and IoT. Many components that make up a typical software application are run on something other than resources you own or control, which makes including the impact of managed services in your measurement especially hard. "),(0,a.kt)("p",null,"The impacts of software components also vary over time, so as well as understanding ",(0,a.kt)("strong",{parentName:"p"},"which")," components contribute most to the overall impacts, there is also a question of ",(0,a.kt)("strong",{parentName:"p"},"when")," they contribute the most."),(0,a.kt)("p",null,"Only through a granular analysis of the impacts of your software system can investments in reducing its impact be prioritized and verified. Measurement is the first and most crucial step in greening a software system, and the first step in that process with the Impact Framework is to create a tree."),(0,a.kt)("h2",{id:"background"},"Background"),(0,a.kt)("p",null,"This project has evolved over the two years of the GSF's existence. "),(0,a.kt)("p",null,"During the development of the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/sci/blob/dev/SPEC.md"},"SCI"),", we acknowledged that the biggest blocker to adoption was data regarding the emissions of software components on different platforms and runtimes."),(0,a.kt)("p",null,"We then launched the sci-data project to help create the data sets required to calculate an SCI score."),(0,a.kt)("p",null,"After some investigation, the original sci-data team quickly realized that there were several existing data sources, and many more were in development, free open source or private commercial. The future challenge wouldn't be to source them, it would be knowing which data set to use for which use case, how data sets differed in their methodology and interface and when to use one over the other, the pros/cons, and trade-offs."),(0,a.kt)("p",null,"The project evolved into the ",(0,a.kt)("a",{parentName:"p",href:"https://sci-guide.greensoftware.foundation/"},"sci-guide")," to document existing data sets, providing guidance for when to use one over another and how to use it to create your own software measurement reports."),(0,a.kt)("p",null,"Finally, we had enough information, and ",(0,a.kt)("a",{parentName:"p",href:"https://sci-guide.greensoftware.foundation/CaseStudies"},"SCI case studies")," started to be written. This was a milestone moment."),(0,a.kt)("p",null,"But now we are in the next evolution, to have software measurement be a mainstream activity. For this to be an industry with thousands of professionals working to decarbonize software, for businesses to grow and thrive in a commercial software measurement ecosystem, we need to formalize software measurement into a discipline with standards and tooling. The SCI Specification is the standard, and the ",(0,a.kt)("a",{parentName:"p",href:"./06-specification/impact-framework.md"},"Impact Framework")," is the tooling."),(0,a.kt)("h2",{id:"project-structure"},"Project Structure"),(0,a.kt)("p",null,"The ",(0,a.kt)("strong",{parentName:"p"},"IF source code")," can be found in the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if"},"IF Github repository"),". The code there covers the framework, which includes all the infrastructure for reading and writing input and output yamls, invoking plugins, running the command line tool and associated helper functions. However, it does not include the actual plugins themselves. Part of the IF design philosophy is that all plugins should be plugins, so that the IF is as composable and configurable as possible. Therefore, to use IF, you have to either create your own plugins or find some prebuilt ones and install them yourself. This also implies that you take responsibility for the plugins you choose to install."),(0,a.kt)("p",null,"We do provide a ",(0,a.kt)("strong",{parentName:"p"},"standard library of plugins")," built and maintained by the IF core team. These can be found in the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-plugins"},(0,a.kt)("inlineCode",{parentName:"a"},"if-plugins")," Github repository"),". You can install these into ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," by running ",(0,a.kt)("inlineCode",{parentName:"p"},"npm install https://github.com/Green-Software-Foundation/if-plugins")," from the ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," project directory."),(0,a.kt)("p",null,"There is also a second repository for ",(0,a.kt)("strong",{parentName:"p"},"plugins we expect community members to maintain"),". These can be found in the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins"},(0,a.kt)("inlineCode",{parentName:"a"},"if-unofficial-plugins")," Github repository"),". You can install these into ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," by running ",(0,a.kt)("inlineCode",{parentName:"p"},"npm install https://github.com/Green-Software-Foundation/if-unofficial-plugins")," from the ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," project directory."),(0,a.kt)("p",null,"Finally, the ",(0,a.kt)("strong",{parentName:"p"},"source code for this documentation")," website is available at the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-docs"},(0,a.kt)("inlineCode",{parentName:"a"},"if-docs")," Github repository"),"."),(0,a.kt)("h2",{id:"navigating-these-docs"},"Navigating these docs"),(0,a.kt)("p",null,"The lefthand sidebar contains links to all the information you need to understand Impact Framework. "),(0,a.kt)("p",null,"You can explore the key ideas underpinning Impact Framework in the ",(0,a.kt)("a",{parentName:"p",href:"./major-concepts/"},"Major Concepts section"),"."),(0,a.kt)("p",null,"Users can read our ",(0,a.kt)("a",{parentName:"p",href:"./users/"},"guides")," explaining how to use IF, including installation, using the CLI and loading plugins."),(0,a.kt)("p",null,"We also have ",(0,a.kt)("a",{parentName:"p",href:"./developers/"},"developer documentation")," to help you get started building with IF."),(0,a.kt)("p",null,"You will find documentation for the individual built-in plugin implementations in ",(0,a.kt)("a",{parentName:"p",href:"/reference/plugins"},(0,a.kt)("inlineCode",{parentName:"a"},"plugins")),"."))}u.isMDXComponent=!0},5846:(e,t,n)=>{n.d(t,{Z:()=>o});const o=n.p+"assets/images/hack-banner-8a9d7874b60972c27f9bb38f4170f34f.png"}}]); \ No newline at end of file diff --git a/assets/js/1a3c9b31.dc917f97.js b/assets/js/1a3c9b31.c9def7af.js similarity index 68% rename from assets/js/1a3c9b31.dc917f97.js rename to assets/js/1a3c9b31.c9def7af.js index 4248f08a..0e3bc94a 100644 --- a/assets/js/1a3c9b31.dc917f97.js +++ b/assets/js/1a3c9b31.c9def7af.js @@ -1 +1 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[751],{4137:(e,r,t)=>{t.d(r,{Zo:()=>u,kt:()=>d});var n=t(7294);function o(e,r,t){return r in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}function i(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);r&&(n=n.filter((function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable}))),t.push.apply(t,n)}return t}function a(e){for(var r=1;r=0||(o[t]=e[t]);return o}(e,r);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var l=n.createContext({}),s=function(e){var r=n.useContext(l),t=r;return e&&(t="function"==typeof e?e(r):a(a({},r),e)),t},u=function(e){var r=s(e.components);return n.createElement(l.Provider,{value:r},e.children)},f={inlineCode:"code",wrapper:function(e){var r=e.children;return n.createElement(n.Fragment,{},r)}},p=n.forwardRef((function(e,r){var t=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),p=s(t),d=o,m=p["".concat(l,".").concat(d)]||p[d]||f[d]||i;return t?n.createElement(m,a(a({ref:r},u),{},{components:t})):n.createElement(m,a({ref:r},u))}));function d(e,r){var t=arguments,o=r&&r.mdxType;if("string"==typeof e||o){var i=t.length,a=new Array(i);a[0]=p;var c={};for(var l in r)hasOwnProperty.call(r,l)&&(c[l]=r[l]);c.originalType=e,c.mdxType="string"==typeof e?e:o,a[1]=c;for(var s=2;s{t.r(r),t.d(r,{assets:()=>l,contentTitle:()=>a,default:()=>f,frontMatter:()=>i,metadata:()=>c,toc:()=>s});var n=t(7462),o=(t(7294),t(4137));const i={sidebar_position:5},a="Reference",c={unversionedId:"reference/index",id:"reference/index",title:"Reference",description:"In this section you will find reference documentation for the core data structures and features used in the Impact Framework.",source:"@site/docs/reference/index.md",sourceDirName:"reference",slug:"/reference/",permalink:"/reference/",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/reference/index.md",tags:[],version:"current",sidebarPosition:5,frontMatter:{sidebar_position:5},sidebar:"tutorialSidebar",previous:{title:"How to write unit tests",permalink:"/developers/how-to-write-unit-tests"},next:{title:"Command line tool",permalink:"/reference/cli"}},l={},s=[],u={toc:s};function f(e){let{components:r,...t}=e;return(0,o.kt)("wrapper",(0,n.Z)({},u,t,{components:r,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"reference"},"Reference"),(0,o.kt)("p",null,"In this section you will find reference documentation for the core data structures and features used in the Impact Framework."),(0,o.kt)("p",null,"This includes:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/reference/cli"},"CLI (",(0,o.kt)("inlineCode",{parentName:"a"},"ie"),")")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/reference/plugins"},"Plugins"))),(0,o.kt)("p",null,"These are developer focused reference docs. If you are not a developer and looking for usage guides, please head over to the ",(0,o.kt)("a",{parentName:"p",href:"../using-if/"},(0,o.kt)("inlineCode",{parentName:"a"},"Using IF"))," section."))}f.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[751],{4137:(e,r,t)=>{t.d(r,{Zo:()=>u,kt:()=>d});var n=t(7294);function o(e,r,t){return r in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}function i(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);r&&(n=n.filter((function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable}))),t.push.apply(t,n)}return t}function a(e){for(var r=1;r=0||(o[t]=e[t]);return o}(e,r);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var l=n.createContext({}),s=function(e){var r=n.useContext(l),t=r;return e&&(t="function"==typeof e?e(r):a(a({},r),e)),t},u=function(e){var r=s(e.components);return n.createElement(l.Provider,{value:r},e.children)},f={inlineCode:"code",wrapper:function(e){var r=e.children;return n.createElement(n.Fragment,{},r)}},p=n.forwardRef((function(e,r){var t=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),p=s(t),d=o,m=p["".concat(l,".").concat(d)]||p[d]||f[d]||i;return t?n.createElement(m,a(a({ref:r},u),{},{components:t})):n.createElement(m,a({ref:r},u))}));function d(e,r){var t=arguments,o=r&&r.mdxType;if("string"==typeof e||o){var i=t.length,a=new Array(i);a[0]=p;var c={};for(var l in r)hasOwnProperty.call(r,l)&&(c[l]=r[l]);c.originalType=e,c.mdxType="string"==typeof e?e:o,a[1]=c;for(var s=2;s{t.r(r),t.d(r,{assets:()=>l,contentTitle:()=>a,default:()=>f,frontMatter:()=>i,metadata:()=>c,toc:()=>s});var n=t(7462),o=(t(7294),t(4137));const i={sidebar_position:5},a="Reference",c={unversionedId:"reference/index",id:"reference/index",title:"Reference",description:"In this section you will find reference documentation for the core data structures and features used in the Impact Framework.",source:"@site/docs/reference/index.md",sourceDirName:"reference",slug:"/reference/",permalink:"/reference/",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/reference/index.md",tags:[],version:"current",sidebarPosition:5,frontMatter:{sidebar_position:5},sidebar:"tutorialSidebar",previous:{title:"How to write unit tests",permalink:"/developers/how-to-write-unit-tests"},next:{title:"Command line tool",permalink:"/reference/cli"}},l={},s=[],u={toc:s};function f(e){let{components:r,...t}=e;return(0,o.kt)("wrapper",(0,n.Z)({},u,t,{components:r,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"reference"},"Reference"),(0,o.kt)("p",null,"In this section you will find reference documentation for the core data structures and features used in the Impact Framework."),(0,o.kt)("p",null,"This includes:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/reference/cli"},"CLI (",(0,o.kt)("inlineCode",{parentName:"a"},"ie"),")")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/reference/plugins"},"Plugins"))),(0,o.kt)("p",null,"These are developer focused reference docs. If you are not a developer and looking for usage guides, please head over to the ",(0,o.kt)("a",{parentName:"p",href:"../using-if/"},(0,o.kt)("inlineCode",{parentName:"a"},"Using IF"))," section."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/1ead5b6d.25860973.js b/assets/js/1ead5b6d.25860973.js deleted file mode 100644 index 662d8aef..00000000 --- a/assets/js/1ead5b6d.25860973.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[890],{4137:(e,n,t)=>{t.d(n,{Zo:()=>l,kt:()=>d});var i=t(7294);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function o(e){for(var n=1;n=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var s=i.createContext({}),u=function(e){var n=i.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},l=function(e){var n=u(e.components);return i.createElement(s.Provider,{value:n},e.children)},c={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},m=i.forwardRef((function(e,n){var t=e.components,r=e.mdxType,a=e.originalType,s=e.parentName,l=p(e,["components","mdxType","originalType","parentName"]),m=u(t),d=r,g=m["".concat(s,".").concat(d)]||m[d]||c[d]||a;return t?i.createElement(g,o(o({ref:n},l),{},{components:t})):i.createElement(g,o({ref:n},l))}));function d(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var a=t.length,o=new Array(a);o[0]=m;var p={};for(var s in n)hasOwnProperty.call(n,s)&&(p[s]=n[s]);p.originalType=e,p.mdxType="string"==typeof e?e:r,o[1]=p;for(var u=2;u{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>a,metadata:()=>p,toc:()=>u});var i=t(7462),r=(t(7294),t(4137));const a={"sidebar-position":7},o="Group-by",p={unversionedId:"major-concepts/groupby",id:"major-concepts/groupby",title:"Group-by",description:"Groupby is an IF plugin that reorganizes a tree according to keys provided by the user. This allows users to regroup their observations according to various properties of their application. For example, the following manifest file contains a flat array of observations. This is how you might expect data to arrive from an importer plugin, maybe one that hits a metrics API for a cloud service.",source:"@site/docs/major-concepts/groupby.md",sourceDirName:"major-concepts",slug:"/major-concepts/groupby",permalink:"/major-concepts/groupby",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/major-concepts/groupby.md",tags:[],version:"current",frontMatter:{"sidebar-position":7},sidebar:"tutorialSidebar",previous:{title:"Aggregation",permalink:"/major-concepts/aggregation"},next:{title:"Impact Engine (CLI)",permalink:"/major-concepts/if"}},s={},u=[{value:"Using group-by",id:"using-group-by",level:2}],l={toc:u};function c(e){let{components:n,...t}=e;return(0,r.kt)("wrapper",(0,i.Z)({},l,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"group-by"},"Group-by"),(0,r.kt)("p",null,"Groupby is an IF plugin that reorganizes a tree according to keys provided by the user. This allows users to regroup their observations according to various properties of their application. For example, the following manifest file contains a flat array of observations. This is how you might expect data to arrive from an importer plugin, maybe one that hits a metrics API for a cloud service."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"name: if-demo\ndescription: demo pipeline\ngraph:\n children:\n my-app:\n pipeline: \n - group-by\n - teads-curve\n config:\n group-by:\n - cloud-region\n - instance-type\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300 \n instance-type: A1 \n region: uk-west\n cpu-util: 99\n - timestamp: 2023-07-06T05:00 \n duration: 300 \n instance-type: A1 \n region: uk-west\n cpu-util: 23 \n - timestamp: 2023-07-06T10:00\n duration: 300\n instance-type: A1 \n region: uk-west\n cpu-util: 12\n - timestamp: 2023-07-06T00:00 # note this time restarts at the start timstamp\n duration: 300 \n instance-type: B1\n region: uk-west\n cpu-util: 11\n - timestamp: 2023-07-06T05:00 \n duration: 300 \n instance-type: B1\n region: uk-west\n cpu-util: 67\n - timestamp: 2023-07-06T10:00\n duration: 300 \n instance-type: B1\n region: uk-west\n cpu-util: 1 \n")),(0,r.kt)("p",null,"However, each observation contains an ",(0,r.kt)("inlineCode",{parentName:"p"},"instance-type")," field that varies between observations. There are two instance types being represented in this array of observations. This means there are duplicate entries for the same timestamp in this array. This is the problem that ",(0,r.kt)("inlineCode",{parentName:"p"},"group-by")," solves. You provide ",(0,r.kt)("inlineCode",{parentName:"p"},"instance-type")," as a key to the ",(0,r.kt)("inlineCode",{parentName:"p"},"group-by")," plugin and it extracts the data belonging to the different instances and separates them into independent arrays. The above example would be restructured so that instance types ",(0,r.kt)("inlineCode",{parentName:"p"},"A1")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"B1")," have their own data, as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"graph:\n children:\n my-app:\n pipeline:\n # - group-by\n - teads-curve\n config:\n group-by:\n groups:\n - cloud-region\n - instance-type\n children:\n A1:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n instance-type: A1\n region: uk-west\n cpu-util: 99\n - timestamp: 2023-07-06T05:00\n duration: 300\n instance-type: A1\n region: uk-west\n cpu-util: 23\n - timestamp: 2023-07-06T10:00\n duration: 300\n instance-type: A1\n region: uk-west\n cpu-util: 12\n B1:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n instance-type: B1\n region: uk-east\n cpu-util: 11\n - timestamp: 2023-07-06T05:00\n duration: 300\n instance-type: B1\n region: uk-east\n cpu-util: 67\n - timestamp: 2023-07-06T10:00\n duration: 300\n instance-type: B1\n region: uk-east\n cpu-util: 1 \n")),(0,r.kt)("h2",{id:"using-group-by"},"Using ",(0,r.kt)("inlineCode",{parentName:"h2"},"group-by")),(0,r.kt)("p",null,"To use ",(0,r.kt)("inlineCode",{parentName:"p"},"group-by"),", you have to initialize it as a plugin and invoke it in a pipeline."),(0,r.kt)("p",null,"The initialization looks as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\nplugins:\ngroup-by: \n path: 'builtin'\n method: GroupBy\n")),(0,r.kt)("p",null,"You then have to provide config defining which keys to group by in each component. This is done at the component level (i.e. not global config).\nFor example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n my-app:\n pipeline: \n - group-by\n config:\n group-by:\n group:\n - region\n - instance-type\n")),(0,r.kt)("p",null,"In the example above, the plugin would regroup the input data for the specific component by ",(0,r.kt)("inlineCode",{parentName:"p"},"region")," and by ",(0,r.kt)("inlineCode",{parentName:"p"},"instance-type"),"."),(0,r.kt)("p",null,"Assuming the values ",(0,r.kt)("inlineCode",{parentName:"p"},"A1")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"B1")," are found for ",(0,r.kt)("inlineCode",{parentName:"p"},"instance-type")," and the values ",(0,r.kt)("inlineCode",{parentName:"p"},"uk-east")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"uk-west")," are found for ",(0,r.kt)("inlineCode",{parentName:"p"},"region"),", the result of ",(0,r.kt)("inlineCode",{parentName:"p"},"group-by")," would look similar to the following:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n my-app:\n pipeline:\n - group-by\n config:\n group-by:\n groups:\n - region\n - instance-type\n children:\n uk-west:\n children:\n A1:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n instance-type: A1\n region: uk-west\n cpu-util: 99\n - timestamp: 2023-07-06T05:00\n duration: 300\n instance-type: A1\n region: uk-west\n cpu-util: 23\n - timestamp: 2023-07-06T10:00\n duration: 300\n instance-type: A1\n region: uk-west\n cpu-util: 12\n uk-east:\n children:\n B1:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n instance-type: B1\n region: uk-east\n cpu-util: 11\n - timestamp: 2023-07-06T05:00\n duration: 300\n instance-type: B1\n region: uk-east\n cpu-util: 67\n - timestamp: 2023-07-06T10:00\n duration: 300\n instance-type: B1\n region: uk-east\n cpu-util: 1 \n")),(0,r.kt)("p",null,"This reorganized data can then be used to feed the rest of a computation pipeline."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/1ead5b6d.e1c9946b.js b/assets/js/1ead5b6d.e1c9946b.js new file mode 100644 index 00000000..0e587355 --- /dev/null +++ b/assets/js/1ead5b6d.e1c9946b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[890],{4137:(e,n,t)=>{t.d(n,{Zo:()=>l,kt:()=>d});var i=t(7294);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function o(e){for(var n=1;n=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var s=i.createContext({}),u=function(e){var n=i.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},l=function(e){var n=u(e.components);return i.createElement(s.Provider,{value:n},e.children)},c={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},m=i.forwardRef((function(e,n){var t=e.components,r=e.mdxType,a=e.originalType,s=e.parentName,l=p(e,["components","mdxType","originalType","parentName"]),m=u(t),d=r,g=m["".concat(s,".").concat(d)]||m[d]||c[d]||a;return t?i.createElement(g,o(o({ref:n},l),{},{components:t})):i.createElement(g,o({ref:n},l))}));function d(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var a=t.length,o=new Array(a);o[0]=m;var p={};for(var s in n)hasOwnProperty.call(n,s)&&(p[s]=n[s]);p.originalType=e,p.mdxType="string"==typeof e?e:r,o[1]=p;for(var u=2;u{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>a,metadata:()=>p,toc:()=>u});var i=t(7462),r=(t(7294),t(4137));const a={"sidebar-position":7},o="Group-by",p={unversionedId:"major-concepts/groupby",id:"major-concepts/groupby",title:"Group-by",description:"Groupby is an IF plugin that reorganizes a tree according to keys provided by the user. This allows users to regroup their observations according to various properties of their application. For example, the following manifest file contains a flat array of observations. This is how you might expect data to arrive from an importer plugin, maybe one that hits a metrics API for a cloud service.",source:"@site/docs/major-concepts/groupby.md",sourceDirName:"major-concepts",slug:"/major-concepts/groupby",permalink:"/major-concepts/groupby",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/groupby.md",tags:[],version:"current",frontMatter:{"sidebar-position":7},sidebar:"tutorialSidebar",previous:{title:"Aggregation",permalink:"/major-concepts/aggregation"},next:{title:"Impact Engine (CLI)",permalink:"/major-concepts/if"}},s={},u=[{value:"Using group-by",id:"using-group-by",level:2}],l={toc:u};function c(e){let{components:n,...t}=e;return(0,r.kt)("wrapper",(0,i.Z)({},l,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"group-by"},"Group-by"),(0,r.kt)("p",null,"Groupby is an IF plugin that reorganizes a tree according to keys provided by the user. This allows users to regroup their observations according to various properties of their application. For example, the following manifest file contains a flat array of observations. This is how you might expect data to arrive from an importer plugin, maybe one that hits a metrics API for a cloud service."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"name: if-demo\ndescription: demo pipeline\ngraph:\n children:\n my-app:\n pipeline: \n - group-by\n - teads-curve\n config:\n group-by:\n - cloud-region\n - instance-type\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300 \n instance-type: A1 \n region: uk-west\n cpu-util: 99\n - timestamp: 2023-07-06T05:00 \n duration: 300 \n instance-type: A1 \n region: uk-west\n cpu-util: 23 \n - timestamp: 2023-07-06T10:00\n duration: 300\n instance-type: A1 \n region: uk-west\n cpu-util: 12\n - timestamp: 2023-07-06T00:00 # note this time restarts at the start timstamp\n duration: 300 \n instance-type: B1\n region: uk-west\n cpu-util: 11\n - timestamp: 2023-07-06T05:00 \n duration: 300 \n instance-type: B1\n region: uk-west\n cpu-util: 67\n - timestamp: 2023-07-06T10:00\n duration: 300 \n instance-type: B1\n region: uk-west\n cpu-util: 1 \n")),(0,r.kt)("p",null,"However, each observation contains an ",(0,r.kt)("inlineCode",{parentName:"p"},"instance-type")," field that varies between observations. There are two instance types being represented in this array of observations. This means there are duplicate entries for the same timestamp in this array. This is the problem that ",(0,r.kt)("inlineCode",{parentName:"p"},"group-by")," solves. You provide ",(0,r.kt)("inlineCode",{parentName:"p"},"instance-type")," as a key to the ",(0,r.kt)("inlineCode",{parentName:"p"},"group-by")," plugin and it extracts the data belonging to the different instances and separates them into independent arrays. The above example would be restructured so that instance types ",(0,r.kt)("inlineCode",{parentName:"p"},"A1")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"B1")," have their own data, as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"graph:\n children:\n my-app:\n pipeline:\n # - group-by\n - teads-curve\n config:\n group-by:\n groups:\n - cloud-region\n - instance-type\n children:\n A1:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n instance-type: A1\n region: uk-west\n cpu-util: 99\n - timestamp: 2023-07-06T05:00\n duration: 300\n instance-type: A1\n region: uk-west\n cpu-util: 23\n - timestamp: 2023-07-06T10:00\n duration: 300\n instance-type: A1\n region: uk-west\n cpu-util: 12\n B1:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n instance-type: B1\n region: uk-east\n cpu-util: 11\n - timestamp: 2023-07-06T05:00\n duration: 300\n instance-type: B1\n region: uk-east\n cpu-util: 67\n - timestamp: 2023-07-06T10:00\n duration: 300\n instance-type: B1\n region: uk-east\n cpu-util: 1 \n")),(0,r.kt)("h2",{id:"using-group-by"},"Using ",(0,r.kt)("inlineCode",{parentName:"h2"},"group-by")),(0,r.kt)("p",null,"To use ",(0,r.kt)("inlineCode",{parentName:"p"},"group-by"),", you have to initialize it as a plugin and invoke it in a pipeline."),(0,r.kt)("p",null,"The initialization looks as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\nplugins:\ngroup-by: \n path: 'builtin'\n method: GroupBy\n")),(0,r.kt)("p",null,"You then have to provide config defining which keys to group by in each component. This is done at the component level (i.e. not global config).\nFor example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n my-app:\n pipeline: \n - group-by\n config:\n group-by:\n group:\n - region\n - instance-type\n")),(0,r.kt)("p",null,"In the example above, the plugin would regroup the input data for the specific component by ",(0,r.kt)("inlineCode",{parentName:"p"},"region")," and by ",(0,r.kt)("inlineCode",{parentName:"p"},"instance-type"),"."),(0,r.kt)("p",null,"Assuming the values ",(0,r.kt)("inlineCode",{parentName:"p"},"A1")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"B1")," are found for ",(0,r.kt)("inlineCode",{parentName:"p"},"instance-type")," and the values ",(0,r.kt)("inlineCode",{parentName:"p"},"uk-east")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"uk-west")," are found for ",(0,r.kt)("inlineCode",{parentName:"p"},"region"),", the result of ",(0,r.kt)("inlineCode",{parentName:"p"},"group-by")," would look similar to the following:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n my-app:\n pipeline:\n - group-by\n config:\n group-by:\n groups:\n - region\n - instance-type\n children:\n uk-west:\n children:\n A1:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n instance-type: A1\n region: uk-west\n cpu-util: 99\n - timestamp: 2023-07-06T05:00\n duration: 300\n instance-type: A1\n region: uk-west\n cpu-util: 23\n - timestamp: 2023-07-06T10:00\n duration: 300\n instance-type: A1\n region: uk-west\n cpu-util: 12\n uk-east:\n children:\n B1:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n instance-type: B1\n region: uk-east\n cpu-util: 11\n - timestamp: 2023-07-06T05:00\n duration: 300\n instance-type: B1\n region: uk-east\n cpu-util: 67\n - timestamp: 2023-07-06T10:00\n duration: 300\n instance-type: B1\n region: uk-east\n cpu-util: 1 \n")),(0,r.kt)("p",null,"This reorganized data can then be used to feed the rest of a computation pipeline."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/244c9605.20b5e353.js b/assets/js/244c9605.20b5e353.js deleted file mode 100644 index 6364e060..00000000 --- a/assets/js/244c9605.20b5e353.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[426],{4137:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>d});var i=t(7294);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function a(e){for(var n=1;n=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var s=i.createContext({}),p=function(e){var n=i.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},u=function(e){var n=p(e.components);return i.createElement(s.Provider,{value:n},e.children)},m={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},c=i.forwardRef((function(e,n){var t=e.components,r=e.mdxType,o=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),c=p(t),d=r,g=c["".concat(s,".").concat(d)]||c[d]||m[d]||o;return t?i.createElement(g,a(a({ref:n},u),{},{components:t})):i.createElement(g,a({ref:n},u))}));function d(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var o=t.length,a=new Array(o);a[0]=c;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l.mdxType="string"==typeof e?e:r,a[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>a,default:()=>m,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var i=t(7462),r=(t(7294),t(4137));const o={"sidebar-position":2},a="How to make plugins production ready",l={unversionedId:"developers/how-to-refine-plugins",id:"developers/how-to-refine-plugins",title:"How to make plugins production ready",description:"Our How to build plugins guide covered the basics for how to construct an Impact Framework plugin. This guide will help you to refine your plugin to make it production-ready. These are best practice guidelines - if you intend to contribute your plugin to one of our repositories, following these guidelines will help your PR to get merged. Even if you are not aiming to have a plugin merged into one of our repositories, consistency with our norms is useful for debugging and maintaining and for making your plugin as useful as possible for other Impact Framework developers.",source:"@site/docs/developers/how-to-refine-plugins.md",sourceDirName:"developers",slug:"/developers/how-to-refine-plugins",permalink:"/developers/how-to-refine-plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/developers/how-to-refine-plugins.md",tags:[],version:"current",frontMatter:{"sidebar-position":2},sidebar:"tutorialSidebar",previous:{title:"How to build plugins",permalink:"/developers/how-to-build-plugins"},next:{title:"How to visualize results",permalink:"/developers/how-to-visualize-results"}},s={},p=[{value:"1. Naming conventions",id:"1-naming-conventions",level:2},{value:"2. Plugin code",id:"2-plugin-code",level:2},{value:"Imports",id:"imports",level:3},{value:"Comments",id:"comments",level:3},{value:"Error handling",id:"error-handling",level:3},{value:"Validation",id:"validation",level:3},{value:"Code Modularity",id:"code-modularity",level:3},{value:"3. Unit tests",id:"3-unit-tests",level:2},{value:"4. Linting",id:"4-linting",level:2},{value:"Summary",id:"summary",level:2}],u={toc:p};function m(e){let{components:n,...t}=e;return(0,r.kt)("wrapper",(0,i.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"how-to-make-plugins-production-ready"},"How to make plugins production ready"),(0,r.kt)("p",null,"Our ",(0,r.kt)("a",{parentName:"p",href:"/developers/how-to-build-plugins"},"How to build plugins")," guide covered the basics for how to construct an Impact Framework plugin. This guide will help you to refine your plugin to make it production-ready. These are best practice guidelines - if you intend to contribute your plugin to one of our repositories, following these guidelines will help your PR to get merged. Even if you are not aiming to have a plugin merged into one of our repositories, consistency with our norms is useful for debugging and maintaining and for making your plugin as useful as possible for other Impact Framework developers."),(0,r.kt)("h2",{id:"1-naming-conventions"},"1. Naming conventions"),(0,r.kt)("p",null,"We prefer not to use abbreviations of contractions in parameter names. Using fully descriptive names makes the code more readable, which in turn helps reviewers and anyone else aiming to understand how the plugin works. It also helps to avoid ambiguity and naming collisions within and across plugins. Your name should describe what an element does as precisely as practically possible."),(0,r.kt)("p",null,"For example, we prefer ",(0,r.kt)("inlineCode",{parentName:"p"},"cpu/energy")," to ",(0,r.kt)("inlineCode",{parentName:"p"},"e-cpu")," and we prefer ",(0,r.kt)("inlineCode",{parentName:"p"},"functionalUnit")," to ",(0,r.kt)("inlineCode",{parentName:"p"},"funcUnit"),", ",(0,r.kt)("inlineCode",{parentName:"p"},"fUnit"),", or any other abbreviation."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"In Typescript code")," we use lower Camel case (",(0,r.kt)("inlineCode",{parentName:"p"},"likeThis"),") for variable and function names and Pascal/Upper Camel case for class, type, enum, and interface names (",(0,r.kt)("inlineCode",{parentName:"p"},"LikeThis"),")."),(0,r.kt)("p",null,"For example:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"sci")," is the name for the SCI value normalized per second."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"energy")," is the name for the array of energy metrics available to be summed in the ",(0,r.kt)("inlineCode",{parentName:"li"},"sci-e")," plugin")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"In yaml files"),", we prefer to use kebab-case (",(0,r.kt)("inlineCode",{parentName:"p"},"like-this"),") for field names. For example:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"network/energy")," is the field name for the energy consumed by networking for an application"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"functional-unit")," is the unit in which to express an SCI value.")),(0,r.kt)("p",null,"Global constants can be given capitalized names, such as ",(0,r.kt)("inlineCode",{parentName:"p"},"TIME_UNITS_IN_SECONDS"),"."),(0,r.kt)("h2",{id:"2-plugin-code"},"2. Plugin code"),(0,r.kt)("h3",{id:"imports"},"Imports"),(0,r.kt)("p",null,"We prefer the following ordering of imports in your plugin code:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Node built-in modules (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"import fs from 'fs';"),")"),(0,r.kt)("li",{parentName:"ol"},"External modules (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"import {z} from 'zod';"),")"),(0,r.kt)("li",{parentName:"ol"},"Internal modules (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"import config from 'src/config';"),")"),(0,r.kt)("li",{parentName:"ol"},"Interfaces (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"import type {PluginInterface} from 'interfaces'"),")"),(0,r.kt)("li",{parentName:"ol"},"Types (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"import {PluginParams} from '../../types/common'"),";)")),(0,r.kt)("h3",{id:"comments"},"Comments"),(0,r.kt)("p",null,"Each logical unit in the code should be preceded by an appropriate explanatory comment. Sometimes it is useful to include short comments inside a function that clarifies the purpose of a particular statement. Here's an example from our codebase:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"}," /**\n * Calculates the energy consumption for a single input.\n */\n const calculateEnergy = (input: PluginParams) => {\n const {\n 'memory/capacity': totalMemory,\n 'memory/utilization': memoryUtil,\n 'energy-per-gb': energyPerGB,\n } = input;\n\n // GB * kWh/GB == kWh\n return totalMemory * (memoryUtil / 100) * energyPerGB;\n };\n")),(0,r.kt)("h3",{id:"error-handling"},"Error handling"),(0,r.kt)("p",null,"We use custom errors across our codebase to make it as easy as possible to understand the root cause of a problem.\nOverall, we aim to provide error messages that are as descriptive and precise as possible."),(0,r.kt)("p",null,"Some examples from our Impact Framework code are:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yml"},"FILE_IS_NOT_YAML: 'Provided manifest file is not in yaml format.',\nMANIFEST_IS_MISSING: 'Manifest file is missing.',\nMISSING_PATH: \"Initalization param 'path' is missing.\"\nINVALID_MODULE_PATH: (path: string) =>\n `Provided module path: '${path}' is invalid.`,\n")),(0,r.kt)("p",null,"Please try to use similarly precise error messages throughout your plugin."),(0,r.kt)("h3",{id:"validation"},"Validation"),(0,r.kt)("p",null,"Utilize validation techniques to ensure the integrity of input data.\nValidate input parameters against expected types, ranges, or constraints to prevent runtime errors and ensure data consistency.\nWe use ",(0,r.kt)("inlineCode",{parentName:"p"},"zod")," to validate data. Here's an example from our codebase:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"/**\n * Checks for required fields in input.\n */\nconst validateInput = (input: PluginParams) => {\n const schema = z\n .object({\n 'cpu/name': z.string(),\n })\n .refine(allDefined, {\n message: '`cpu/name` should be present.',\n });\n\n return validate>(schema, input);\n}\n")),(0,r.kt)("h3",{id:"code-modularity"},"Code Modularity"),(0,r.kt)("p",null,"Break down complex functionality into smaller, manageable methods with well-defined responsibilities.\nEncapsulate related functionality into private methods to promote code reusability and maintainability."),(0,r.kt)("h2",{id:"3-unit-tests"},"3. Unit tests"),(0,r.kt)("p",null,"Your plugin should have unit tests with 100% coverage. We use ",(0,r.kt)("inlineCode",{parentName:"p"},"jest")," to handle unit testing. We strive to have one ",(0,r.kt)("inlineCode",{parentName:"p"},"describe")," per function. Each possible outcome from each function is separated using ",(0,r.kt)("inlineCode",{parentName:"p"},"it")," with a precise and descriptive message."),(0,r.kt)("p",null,"Here's an example that covers plugin initialization and the happy path for the ",(0,r.kt)("inlineCode",{parentName:"p"},"execute()")," function."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"import {Sum} from '../../../../lib';\n\nimport {ERRORS} from '../../../../util/errors';\n\nconst {InputValidationError} = ERRORS;\n\ndescribe('lib/sum: ', () => {\n describe('Sum: ', () => {\n const globalConfig = {\n 'input-parameters': ['cpu/energy', 'network/energy', 'memory/energy'],\n 'output-parameter': 'energy',\n };\n const sum = Sum(globalConfig);\n\n describe('init: ', () => {\n it('successfully initalized.', () => {\n expect(sum).toHaveProperty('metadata');\n expect(sum).toHaveProperty('execute');\n });\n });\n\n describe('execute(): ', () => {\n it('successfully applies Sum strategy to given input.', async () => {\n expect.assertions(1);\n\n const expectedResult = [\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n energy: 3,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ];\n\n const result = await sum.execute([\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ]);\n\n expect(result).toStrictEqual(expectedResult);\n });\n }\n })\n})\n")),(0,r.kt)("p",null,"We have a ",(0,r.kt)("a",{parentName:"p",href:"/developers/how-to-write-unit-tests"},"dedicated page")," explaining in more detail how to write great unit tests for Impact Framework plugins."),(0,r.kt)("h2",{id:"4-linting"},"4. Linting"),(0,r.kt)("p",null,"We use ESLint to format our code. We use a very simple configuration file (",(0,r.kt)("inlineCode",{parentName:"p"},"eslintrc.json"),"), as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-json"},'{\n "extends": "./node_modules/gts/",\n "rules": {\n "@typescript-eslint/no-explicit-any": ["off"]\n }\n}\n')),(0,r.kt)("p",null,"For our repositories we use Github CI to enforce the linting rules for any pull requests."),(0,r.kt)("h2",{id:"summary"},"Summary"),(0,r.kt)("p",null,"On this page, we have outlined best practices for refining your plugins so that they conform to our expected norms. This will help you write clean, efficient, and understandable code!"))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/244c9605.98fd4bd8.js b/assets/js/244c9605.98fd4bd8.js new file mode 100644 index 00000000..64d446c1 --- /dev/null +++ b/assets/js/244c9605.98fd4bd8.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[426],{4137:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>d});var i=t(7294);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function a(e){for(var n=1;n=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var s=i.createContext({}),p=function(e){var n=i.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},u=function(e){var n=p(e.components);return i.createElement(s.Provider,{value:n},e.children)},m={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},c=i.forwardRef((function(e,n){var t=e.components,r=e.mdxType,o=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),c=p(t),d=r,g=c["".concat(s,".").concat(d)]||c[d]||m[d]||o;return t?i.createElement(g,a(a({ref:n},u),{},{components:t})):i.createElement(g,a({ref:n},u))}));function d(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var o=t.length,a=new Array(o);a[0]=c;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l.mdxType="string"==typeof e?e:r,a[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>a,default:()=>m,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var i=t(7462),r=(t(7294),t(4137));const o={"sidebar-position":2},a="How to make plugins production ready",l={unversionedId:"developers/how-to-refine-plugins",id:"developers/how-to-refine-plugins",title:"How to make plugins production ready",description:"Our How to build plugins guide covered the basics for how to construct an Impact Framework plugin. This guide will help you to refine your plugin to make it production-ready. These are best practice guidelines - if you intend to contribute your plugin to one of our repositories, following these guidelines will help your PR to get merged. Even if you are not aiming to have a plugin merged into one of our repositories, consistency with our norms is useful for debugging and maintaining and for making your plugin as useful as possible for other Impact Framework developers.",source:"@site/docs/developers/how-to-refine-plugins.md",sourceDirName:"developers",slug:"/developers/how-to-refine-plugins",permalink:"/developers/how-to-refine-plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/developers/how-to-refine-plugins.md",tags:[],version:"current",frontMatter:{"sidebar-position":2},sidebar:"tutorialSidebar",previous:{title:"How to build plugins",permalink:"/developers/how-to-build-plugins"},next:{title:"How to visualize results",permalink:"/developers/how-to-visualize-results"}},s={},p=[{value:"1. Naming conventions",id:"1-naming-conventions",level:2},{value:"2. Plugin code",id:"2-plugin-code",level:2},{value:"Imports",id:"imports",level:3},{value:"Comments",id:"comments",level:3},{value:"Error handling",id:"error-handling",level:3},{value:"Validation",id:"validation",level:3},{value:"Code Modularity",id:"code-modularity",level:3},{value:"3. Unit tests",id:"3-unit-tests",level:2},{value:"4. Linting",id:"4-linting",level:2},{value:"Summary",id:"summary",level:2}],u={toc:p};function m(e){let{components:n,...t}=e;return(0,r.kt)("wrapper",(0,i.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"how-to-make-plugins-production-ready"},"How to make plugins production ready"),(0,r.kt)("p",null,"Our ",(0,r.kt)("a",{parentName:"p",href:"/developers/how-to-build-plugins"},"How to build plugins")," guide covered the basics for how to construct an Impact Framework plugin. This guide will help you to refine your plugin to make it production-ready. These are best practice guidelines - if you intend to contribute your plugin to one of our repositories, following these guidelines will help your PR to get merged. Even if you are not aiming to have a plugin merged into one of our repositories, consistency with our norms is useful for debugging and maintaining and for making your plugin as useful as possible for other Impact Framework developers."),(0,r.kt)("h2",{id:"1-naming-conventions"},"1. Naming conventions"),(0,r.kt)("p",null,"We prefer not to use abbreviations of contractions in parameter names. Using fully descriptive names makes the code more readable, which in turn helps reviewers and anyone else aiming to understand how the plugin works. It also helps to avoid ambiguity and naming collisions within and across plugins. Your name should describe what an element does as precisely as practically possible."),(0,r.kt)("p",null,"For example, we prefer ",(0,r.kt)("inlineCode",{parentName:"p"},"cpu/energy")," to ",(0,r.kt)("inlineCode",{parentName:"p"},"e-cpu")," and we prefer ",(0,r.kt)("inlineCode",{parentName:"p"},"functionalUnit")," to ",(0,r.kt)("inlineCode",{parentName:"p"},"funcUnit"),", ",(0,r.kt)("inlineCode",{parentName:"p"},"fUnit"),", or any other abbreviation."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"In Typescript code")," we use lower Camel case (",(0,r.kt)("inlineCode",{parentName:"p"},"likeThis"),") for variable and function names and Pascal/Upper Camel case for class, type, enum, and interface names (",(0,r.kt)("inlineCode",{parentName:"p"},"LikeThis"),")."),(0,r.kt)("p",null,"For example:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"sci")," is the name for the SCI value normalized per second."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"energy")," is the name for the array of energy metrics available to be summed in the ",(0,r.kt)("inlineCode",{parentName:"li"},"sci-e")," plugin")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"In yaml files"),", we prefer to use kebab-case (",(0,r.kt)("inlineCode",{parentName:"p"},"like-this"),") for field names. For example:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"network/energy")," is the field name for the energy consumed by networking for an application"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"functional-unit")," is the unit in which to express an SCI value.")),(0,r.kt)("p",null,"Global constants can be given capitalized names, such as ",(0,r.kt)("inlineCode",{parentName:"p"},"TIME_UNITS_IN_SECONDS"),"."),(0,r.kt)("h2",{id:"2-plugin-code"},"2. Plugin code"),(0,r.kt)("h3",{id:"imports"},"Imports"),(0,r.kt)("p",null,"We prefer the following ordering of imports in your plugin code:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Node built-in modules (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"import fs from 'fs';"),")"),(0,r.kt)("li",{parentName:"ol"},"External modules (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"import {z} from 'zod';"),")"),(0,r.kt)("li",{parentName:"ol"},"Internal modules (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"import config from 'src/config';"),")"),(0,r.kt)("li",{parentName:"ol"},"Interfaces (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"import type {PluginInterface} from 'interfaces'"),")"),(0,r.kt)("li",{parentName:"ol"},"Types (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"import {PluginParams} from '../../types/common'"),";)")),(0,r.kt)("h3",{id:"comments"},"Comments"),(0,r.kt)("p",null,"Each logical unit in the code should be preceded by an appropriate explanatory comment. Sometimes it is useful to include short comments inside a function that clarifies the purpose of a particular statement. Here's an example from our codebase:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"}," /**\n * Calculates the energy consumption for a single input.\n */\n const calculateEnergy = (input: PluginParams) => {\n const {\n 'memory/capacity': totalMemory,\n 'memory/utilization': memoryUtil,\n 'energy-per-gb': energyPerGB,\n } = input;\n\n // GB * kWh/GB == kWh\n return totalMemory * (memoryUtil / 100) * energyPerGB;\n };\n")),(0,r.kt)("h3",{id:"error-handling"},"Error handling"),(0,r.kt)("p",null,"We use custom errors across our codebase to make it as easy as possible to understand the root cause of a problem.\nOverall, we aim to provide error messages that are as descriptive and precise as possible."),(0,r.kt)("p",null,"Some examples from our Impact Framework code are:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yml"},"FILE_IS_NOT_YAML: 'Provided manifest file is not in yaml format.',\nMANIFEST_IS_MISSING: 'Manifest file is missing.',\nMISSING_PATH: \"Initalization param 'path' is missing.\"\nINVALID_MODULE_PATH: (path: string) =>\n `Provided module path: '${path}' is invalid.`,\n")),(0,r.kt)("p",null,"Please try to use similarly precise error messages throughout your plugin."),(0,r.kt)("h3",{id:"validation"},"Validation"),(0,r.kt)("p",null,"Utilize validation techniques to ensure the integrity of input data.\nValidate input parameters against expected types, ranges, or constraints to prevent runtime errors and ensure data consistency.\nWe use ",(0,r.kt)("inlineCode",{parentName:"p"},"zod")," to validate data. Here's an example from our codebase:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"/**\n * Checks for required fields in input.\n */\nconst validateInput = (input: PluginParams) => {\n const schema = z\n .object({\n 'cpu/name': z.string(),\n })\n .refine(allDefined, {\n message: '`cpu/name` should be present.',\n });\n\n return validate>(schema, input);\n}\n")),(0,r.kt)("h3",{id:"code-modularity"},"Code Modularity"),(0,r.kt)("p",null,"Break down complex functionality into smaller, manageable methods with well-defined responsibilities.\nEncapsulate related functionality into private methods to promote code reusability and maintainability."),(0,r.kt)("h2",{id:"3-unit-tests"},"3. Unit tests"),(0,r.kt)("p",null,"Your plugin should have unit tests with 100% coverage. We use ",(0,r.kt)("inlineCode",{parentName:"p"},"jest")," to handle unit testing. We strive to have one ",(0,r.kt)("inlineCode",{parentName:"p"},"describe")," per function. Each possible outcome from each function is separated using ",(0,r.kt)("inlineCode",{parentName:"p"},"it")," with a precise and descriptive message."),(0,r.kt)("p",null,"Here's an example that covers plugin initialization and the happy path for the ",(0,r.kt)("inlineCode",{parentName:"p"},"execute()")," function."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"import {Sum} from '../../../../lib';\n\nimport {ERRORS} from '../../../../util/errors';\n\nconst {InputValidationError} = ERRORS;\n\ndescribe('lib/sum: ', () => {\n describe('Sum: ', () => {\n const globalConfig = {\n 'input-parameters': ['cpu/energy', 'network/energy', 'memory/energy'],\n 'output-parameter': 'energy',\n };\n const sum = Sum(globalConfig);\n\n describe('init: ', () => {\n it('successfully initalized.', () => {\n expect(sum).toHaveProperty('metadata');\n expect(sum).toHaveProperty('execute');\n });\n });\n\n describe('execute(): ', () => {\n it('successfully applies Sum strategy to given input.', async () => {\n expect.assertions(1);\n\n const expectedResult = [\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n energy: 3,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ];\n\n const result = await sum.execute([\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ]);\n\n expect(result).toStrictEqual(expectedResult);\n });\n }\n })\n})\n")),(0,r.kt)("p",null,"We have a ",(0,r.kt)("a",{parentName:"p",href:"/developers/how-to-write-unit-tests"},"dedicated page")," explaining in more detail how to write great unit tests for Impact Framework plugins."),(0,r.kt)("h2",{id:"4-linting"},"4. Linting"),(0,r.kt)("p",null,"We use ESLint to format our code. We use a very simple configuration file (",(0,r.kt)("inlineCode",{parentName:"p"},"eslintrc.json"),"), as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-json"},'{\n "extends": "./node_modules/gts/",\n "rules": {\n "@typescript-eslint/no-explicit-any": ["off"]\n }\n}\n')),(0,r.kt)("p",null,"For our repositories we use Github CI to enforce the linting rules for any pull requests."),(0,r.kt)("h2",{id:"summary"},"Summary"),(0,r.kt)("p",null,"On this page, we have outlined best practices for refining your plugins so that they conform to our expected norms. This will help you write clean, efficient, and understandable code!"))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/28f080da.f1939e8e.js b/assets/js/28f080da.f1939e8e.js deleted file mode 100644 index dca28a6f..00000000 --- a/assets/js/28f080da.f1939e8e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[806],{4137:(e,n,t)=>{t.d(n,{Zo:()=>s,kt:()=>c});var a=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function l(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var p=a.createContext({}),u=function(e){var n=a.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},s=function(e){var n=u(e.components);return a.createElement(p.Provider,{value:n},e.children)},m={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},d=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,l=e.originalType,p=e.parentName,s=r(e,["components","mdxType","originalType","parentName"]),d=u(t),c=i,g=d["".concat(p,".").concat(c)]||d[c]||m[c]||l;return t?a.createElement(g,o(o({ref:n},s),{},{components:t})):a.createElement(g,o({ref:n},s))}));function c(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var l=t.length,o=new Array(l);o[0]=d;var r={};for(var p in n)hasOwnProperty.call(n,p)&&(r[p]=n[p]);r.originalType=e,r.mdxType="string"==typeof e?e:i,o[1]=r;for(var u=2;u{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>o,default:()=>m,frontMatter:()=>l,metadata:()=>r,toc:()=>u});var a=t(7462),i=(t(7294),t(4137));const l={"sidebar-position":1},o="How to build plugins",r={unversionedId:"developers/how-to-build-plugins",id:"developers/how-to-build-plugins",title:"How to build plugins",description:"The IF is designed to be as composable as possible. This means you can develop your own plugins and use them in a pipeline.",source:"@site/docs/developers/how-to-build-plugins.md",sourceDirName:"developers",slug:"/developers/how-to-build-plugins",permalink:"/developers/how-to-build-plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/developers/how-to-build-plugins.md",tags:[],version:"current",frontMatter:{"sidebar-position":1},sidebar:"tutorialSidebar",previous:{title:"Developers",permalink:"/developers/"},next:{title:"How to make plugins production ready",permalink:"/developers/how-to-refine-plugins"}},p={},u=[{value:"Step 1: Use our template repository",id:"step-1-use-our-template-repository",level:2},{value:"Step 2: Writing your plugin code",id:"step-2-writing-your-plugin-code",level:2},{value:"The plugin interface",id:"the-plugin-interface",level:3},{value:"Global config",id:"global-config",level:3},{value:"Methods",id:"methods",level:3},{value:"execute",id:"execute",level:4},{value:"Params",id:"params",level:4},{value:"Returns",id:"returns",level:4},{value:"What are PluginParams?",id:"what-are-pluginparams",level:3},{value:"What are PluginParams?",id:"what-are-pluginparams-1",level:2},{value:"Step 3: Install your plugin",id:"step-3-install-your-plugin",level:2},{value:"Step 4: Load your plugin into IF",id:"step-4-load-your-plugin-into-if",level:2},{value:"Step 5: Publishing your plugin",id:"step-5-publishing-your-plugin",level:2},{value:"Summary of steps",id:"summary-of-steps",level:2},{value:"Next steps",id:"next-steps",level:2},{value:"Appendix: Walk-through of the Sum plugin",id:"appendix-walk-through-of-the-sum-plugin",level:2}],s={toc:u};function m(e){let{components:n,...l}=e;return(0,i.kt)("wrapper",(0,a.Z)({},s,l,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"how-to-build-plugins"},"How to build plugins"),(0,i.kt)("p",null,"The IF is designed to be as composable as possible. This means you can develop your own plugins and use them in a pipeline.\nTo help developers write Typescript plugins to integrate easily into IF, we provide the ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginInterface")," interface. Here's an overview of the stages you need to follow to integrate your plugin:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"create a Typescript file that implements the ",(0,i.kt)("inlineCode",{parentName:"li"},"PluginInterface")),(0,i.kt)("li",{parentName:"ul"},"install the plugin"),(0,i.kt)("li",{parentName:"ul"},"initialize and invoke the plugin in your manifest file")),(0,i.kt)("h2",{id:"step-1-use-our-template-repository"},"Step 1: Use our template repository"),(0,i.kt)("p",null,"Instead of building up your plugin repository and all the configuration from scratch, you can use our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-plugin-template"},"plugin template repository"),". To use the template, visit the Github repository and click the ",(0,i.kt)("inlineCode",{parentName:"p"},"Use this template")," button. You will have the option to ",(0,i.kt)("inlineCode",{parentName:"p"},"create a new repository")," under your own account. Then, you can clone that repository to your local machine."),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"use our template repository",src:t(8312).Z,width:"3024",height:"1184"})),(0,i.kt)("p",null,"Inside that repository, all you have to do is run ",(0,i.kt)("inlineCode",{parentName:"p"},"npm install typescript")," in the template folder, rename the project in ",(0,i.kt)("inlineCode",{parentName:"p"},"package.json")," and write your plugin code inside ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts"),". All the configuration and setup is taken care of for you. "),(0,i.kt)("h2",{id:"step-2-writing-your-plugin-code"},"Step 2: Writing your plugin code"),(0,i.kt)("p",null,"Now your project is setup, you can focus on your plugin logic. The entry point for your plugin is ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts"),". In this guide it is assumed that all your plugin logic is in ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts")," but depending on the copmplexity of your plugin you might want to split the code across multiple files. ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts")," should always be your entry point, though."),(0,i.kt)("p",null,"The following sections describe the rules your plugin code should conform to. We also have an ",(0,i.kt)("a",{parentName:"p",href:"#appendix-walk-through-of-the-sum-plugin"},"appendix")," that deep dives a real plugin."),(0,i.kt)("h3",{id:"the-plugin-interface"},"The plugin interface"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginInterface")," is structured as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"export type PluginInterface = {\n execute: (\n inputs: PluginParams[],\n config?: Record\n ) => PluginParams[];\n metadata: {\n kind: string;\n };\n [key: string]: any;\n};\n")),(0,i.kt)("p",null,"The interface requires an execute function where your plugin logic is implemented. It should also return metadata. This can include any relevant metadata you want to include, with a minimum requirement being ",(0,i.kt)("inlineCode",{parentName:"p"},"kind: execute"),". "),(0,i.kt)("h3",{id:"global-config"},"Global config"),(0,i.kt)("p",null,"Global config is passed as an argument to the plugin. In your plugin code you can handle it as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"// Here's the function definition - notice that global config is passed in here!\nexport const Plugin = (globalConfig: YourConfig): PluginInterface => {\n\n// in here you have access to globalConfig[your-params]\n\n}\n")),(0,i.kt)("p",null,"The parameters available to you in ",(0,i.kt)("inlineCode",{parentName:"p"},"globalConfig")," depends upon the parameters you pass in the manifest file. For example, the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin has access to ",(0,i.kt)("inlineCode",{parentName:"p"},"input-parameters")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"output-parameter")," in its global config, and it is defined in the ",(0,i.kt)("inlineCode",{parentName:"p"},"Initialize")," block in the manifest file as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n sum:\n method: Sum\n path: '@grnsft/if-plugins'\n global-config:\n input-parameters: ['cpu/energy', 'network/energy']\n output-parameter: 'energy'\n")),(0,i.kt)("h3",{id:"methods"},"Methods"),(0,i.kt)("h4",{id:"execute"},"execute"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"execute()")," is where the main calculation logic of the plugin is implemented. It always takes ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," (an array of ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginParams"),") as an argument and returns an updated set of ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs"),"."),(0,i.kt)("h4",{id:"params"},"Params"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Param"),(0,i.kt)("th",{parentName:"tr",align:null},"Type"),(0,i.kt)("th",{parentName:"tr",align:null},"Purpose"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"inputs")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"PluginParams[]")),(0,i.kt)("td",{parentName:"tr",align:null},"Array of data provided in the ",(0,i.kt)("inlineCode",{parentName:"td"},"inputs")," field of a component in a manifest file")))),(0,i.kt)("h4",{id:"returns"},"Returns"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Return value"),(0,i.kt)("th",{parentName:"tr",align:null},"Type"),(0,i.kt)("th",{parentName:"tr",align:null},"Purpose"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"outputs")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"Promise")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"Promise")," resolving to an array of updated ",(0,i.kt)("inlineCode",{parentName:"td"},"PluginParams[]"))))),(0,i.kt)("h3",{id:"what-are-pluginparams"},"What are ",(0,i.kt)("inlineCode",{parentName:"h3"},"PluginParams"),"?"),(0,i.kt)("h2",{id:"what-are-pluginparams-1"},"What are ",(0,i.kt)("inlineCode",{parentName:"h2"},"PluginParams"),"?"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"PluginParams")," are a fundamental data type in the Impact Framework. The type is defined as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"export type PluginParams = {\n [key: string]: any;\n};\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginParams")," type therefore defines an array of key-value pairs."),(0,i.kt)("p",null,"IF needs to know about all the parameters used in each pipeline. The default behaviour is that it grabs parameters from a local file, ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts"),". This file defines the standard set of parameter names, their units, a descriptiona nd the method used to aggregate them across time or across a tree."),(0,i.kt)("p",null,"If your new plugin uses new parameters that are not included in ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts"),", you can simply add them to your manifest file in a section named ",(0,i.kt)("inlineCode",{parentName:"p"},"params"),". For example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: params-demo\ndescription: null\ntags:\nparams: \n - name: new-param-1\n description: dummy\n aggregation: sum\n unit: MT\n - name: new-param-2\n description: dummy\n aggregation: sum\n unit: s\n")),(0,i.kt)("p",null,"This will append the new parameter informatrion to the object loaded from ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts")," and you can use your plugin as normal. In effect, you have append-only access to ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts")," via your manifest file without ever having to change any IF source code."),(0,i.kt)("p",null,"However, if you are an advanced user and you want to use something other than out recommended standard set of parameters, you can provide a replacement ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts")," file on the command line. This file should be a ",(0,i.kt)("inlineCode",{parentName:"p"},"json")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"js"),"/",(0,i.kt)("inlineCode",{parentName:"p"},"ts")," file with the ame structure as our ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts"),". You can rename the file. You then pass the path to the file to the ",(0,i.kt)("inlineCode",{parentName:"p"},"override-params")," command."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest --override-params \n")),(0,i.kt)("h2",{id:"step-3-install-your-plugin"},"Step 3: Install your plugin"),(0,i.kt)("p",null,"Now your plugin code is written, you can install it to make it available to IF."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm run build\n")),(0,i.kt)("p",null,"Then use ",(0,i.kt)("inlineCode",{parentName:"p"},"npm link")," to create a package that can be installed into IF:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm link\n")),(0,i.kt)("h2",{id:"step-4-load-your-plugin-into-if"},"Step 4: Load your plugin into IF"),(0,i.kt)("p",null,"Now your plugin is ready to run in IF. First install your plugin by navigating to the ",(0,i.kt)("inlineCode",{parentName:"p"},"if")," project folder and running:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm link new-plugin\n")),(0,i.kt)("p",null,"replacing ",(0,i.kt)("inlineCode",{parentName:"p"},"new-plugin")," with your plugin name as defined in the plugin's ",(0,i.kt)("inlineCode",{parentName:"p"},"package.json"),". If you are not sure, the name can be checked by running ",(0,i.kt)("inlineCode",{parentName:"p"},"npm ls -g --depth=0 --link=true"),"."),(0,i.kt)("p",null,"Your plugin is now ready to be run in IF. All that remains is to add your plugin to your manifest file. This means adding it to the ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize block")," and adding it to the component pipelines where you want your plugin to be executed. For example, an ",(0,i.kt)("inlineCode",{parentName:"p"},"initilize")," block might look as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n new-plugin:\n method: YourFunctionName\n path: 'new-plugin'\n global-config:\n something: true \n")),(0,i.kt)("p",null,"Run your manifest uisng"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"np run ie -- --manifest \n")),(0,i.kt)("p",null,"If you have to link more than one local plugin, for example to test your plugin in a pipeline, you can do so with"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm link new-plugin --save\n")),(0,i.kt)("p",null,"This will create an entry like ",(0,i.kt)("inlineCode",{parentName:"p"},'"new-plugin": "file:path/to/your/plugin"')," in the ",(0,i.kt)("inlineCode",{parentName:"p"},"package.json")," which links to your local plugin. This way, multiple plugins can be linked at once. Of course, these changes should not be committed, but they can be helpful for local testing."),(0,i.kt)("h2",{id:"step-5-publishing-your-plugin"},"Step 5: Publishing your plugin"),(0,i.kt)("p",null,"Now you have run your plugin locally and you are happy with how it works, you can make it public by publishing it to a public Github repository. Now all you have to do to use it in a manifest file is ",(0,i.kt)("inlineCode",{parentName:"p"},"npm install")," it and pass the path to the Github repository in the plugin ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block."),(0,i.kt)("p",null,"For example, for a plugin saved in ",(0,i.kt)("inlineCode",{parentName:"p"},"github.com/my-repo/new-plugin")," you can do the following:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"npm install https://github.com/my-repo/new-plugin\n")),(0,i.kt)("p",null,"Then, in your manifest file, provide the path in the plugin instantiation. You also need to specify which function the plugin instantiates. Let's say you are using the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin from the example above:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: plugin-demo\ndescription: loads plugin\ntags: null\ninitialize:\n plugins:\n - name: new-plugin\n kind: plugin\n method: FunctionName\n path: https://github.com/my-repo/new-plugin\ntree:\n children:\n child:\n config:\n inputs:\n")),(0,i.kt)("p",null,"Now, when you run the manifest file, it will load the plugin automatically."),(0,i.kt)("p",null,"You can run this using the globally installed IF as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest \n")),(0,i.kt)("h2",{id:"summary-of-steps"},"Summary of steps"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Copy our template repository and update ",(0,i.kt)("inlineCode",{parentName:"li"},"package.json")),(0,i.kt)("li",{parentName:"ul"},"Add your plugin code to ",(0,i.kt)("inlineCode",{parentName:"li"},"index.ts")),(0,i.kt)("li",{parentName:"ul"},"Build and link the plugin using ",(0,i.kt)("inlineCode",{parentName:"li"},"npm run build && npm link")),(0,i.kt)("li",{parentName:"ul"},"Load your plugin into ",(0,i.kt)("inlineCode",{parentName:"li"},"if")," using ",(0,i.kt)("inlineCode",{parentName:"li"},"npm link")),(0,i.kt)("li",{parentName:"ul"},"Initialize your plugin and add it to a pipeline in your manifest file."),(0,i.kt)("li",{parentName:"ul"},"Publish your plugin to Github")),(0,i.kt)("p",null,"You should also create unit tests for your plugin to demonstrate correct execution and handling of corner cases."),(0,i.kt)("h2",{id:"next-steps"},"Next steps"),(0,i.kt)("p",null,"You can read our more advanced guide on ",(0,i.kt)("a",{parentName:"p",href:"/developers/how-to-refine-plugins"},"how to refine your plugins"),"."),(0,i.kt)("h2",{id:"appendix-walk-through-of-the-sum-plugin"},"Appendix: Walk-through of the Sum plugin"),(0,i.kt)("p",null,"To demonstrate how to build a plugin that conforms to the ",(0,i.kt)("inlineCode",{parentName:"p"},"pluginInterface"),", let's examine the ",(0,i.kt)("inlineCode",{parentName:"p"},"sum")," plugin."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"sum")," plugin implements the following logic:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"sum whatever is provided in the ",(0,i.kt)("inlineCode",{parentName:"li"},"input-parameters")," field from ",(0,i.kt)("inlineCode",{parentName:"li"},"globalConfig"),"."),(0,i.kt)("li",{parentName:"ul"},"append the result to each element in the output array with the name provided as ",(0,i.kt)("inlineCode",{parentName:"li"},"output-parameter")," in ",(0,i.kt)("inlineCode",{parentName:"li"},"globalConfig"),".")),(0,i.kt)("p",null,"Let's look at how you would implement this from scratch:"),(0,i.kt)("p",null,"The plugin must be a function conforming to ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginInterface"),". You can call the function ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum"),", and inside the body you can add the signature for the ",(0,i.kt)("inlineCode",{parentName:"p"},"execute")," method:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"export const Sum = (globalConfig: SumConfig): PluginInterface => {\n const errorBuilder = buildErrorMessage(Sum.name);\n const metadata = {\n kind: 'execute',\n };\n\n /**\n * Calculate the sum of each input.\n */\n const execute = async (inputs: PluginParams[]): Promise => {\n\n };\n\n return {\n metadata,\n execute,\n };\n\n}\n")),(0,i.kt)("p",null,"Your plugin now has the basic structure required for IF integration. Your next task is to add code to the body of ",(0,i.kt)("inlineCode",{parentName:"p"},"execute")," to enable the actual plugin logic to be implemented."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"execute")," function should grab the ",(0,i.kt)("inlineCode",{parentName:"p"},"input-parameters")," (the values to sum) from ",(0,i.kt)("inlineCode",{parentName:"p"},"globalConfig"),". it should then iterate over the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array, get the values for each of the ",(0,i.kt)("inlineCode",{parentName:"p"},"input-parameters")," and append them to the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array, using the name from the ",(0,i.kt)("inlineCode",{parentName:"p"},"output-parameter")," value in ",(0,i.kt)("inlineCode",{parentName:"p"},"globalConfig"),". Here's what this can look like, with the actual calculation pushed to a separate function, ",(0,i.kt)("inlineCode",{parentName:"p"},"calculateSum"),". "),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"}," /**\n * Calculate the sum of each input.\n */\n const execute = async (inputs: PluginParams[]): Promise => {\n const inputParameters = globalConfig['input-parameters'];\n const outputParameter = globalConfig['output-parameter'];\n\n return inputs.map(input => {\n return {\n ...input,\n [outputParameter]: calculateSum(input, inputParameters),\n };\n });\n\n return {\n metadata,\n execute,\n };\n}\n")),(0,i.kt)("p",null,"Now we just need to define what happens in ",(0,i.kt)("inlineCode",{parentName:"p"},"calculateSum")," - this can be a simple ",(0,i.kt)("inlineCode",{parentName:"p"},"reduce"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"}," /**\n * Calculates the sum of the energy components.\n */\n const calculateSum = (input: PluginParams, inputParameters: string[]) =>\n inputParameters.reduce(\n (accumulator, metricToSum) => accumulator + input[metricToSum],\n 0\n );\n")),(0,i.kt)("p",null,"Note that this example did not include any validation or error handling - you will likely want to add some for a real plugin."),(0,i.kt)("p",null,"Finally, if your plugin used any fields in ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," or created new ",(0,i.kt)("inlineCode",{parentName:"p"},"outputs")," that have not been used in the Impact Framework before, then you should add them to ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts"),"."),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"params.ts")," can be found in the path ",(0,i.kt)("inlineCode",{parentName:"p"},"src/config"),"."),(0,i.kt)("p",null,"Each entry in ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts")," looks as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"carbon:\n description: an amount of carbon emitted into the atmosphere\n unit: gCO2e\n aggregation: sum\n")),(0,i.kt)("p",null,"This information allows IF to programmatically make decisions about how to handle values in features such as aggregation, time normalization and visualizations, and also acts as a global reference document for understanding IF data. The example above is for ",(0,i.kt)("inlineCode",{parentName:"p"},"carbon"),"."),(0,i.kt)("p",null,"You should add your new data, give a name, define a unit and short description. The ",(0,i.kt)("inlineCode",{parentName:"p"},"aggregation")," field determines how the value is treated when some manipulation has to be done to spread the value over time or aggregate it."),(0,i.kt)("p",null,"For absolute metrics like carbon, the right value is ",(0,i.kt)("inlineCode",{parentName:"p"},"sum")," because you would want to add carbon emissions from each timestep when you aggregate over time."),(0,i.kt)("p",null,"For proportional metrics, the right value is ",(0,i.kt)("inlineCode",{parentName:"p"},"avg"),". For example, you would want to calculate the average ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/utilization")," - it would not make sense to sum it when aggregating over multiple timesteps."),(0,i.kt)("p",null,"Finally, values that should always be presented identically regardless of any aggregation, such as names or global constants, should be given the ",(0,i.kt)("inlineCode",{parentName:"p"},"aggregation-method")," value ",(0,i.kt)("inlineCode",{parentName:"p"},"none"),"."),(0,i.kt)("p",null,"Now you are ready to run your plugin using the ",(0,i.kt)("inlineCode",{parentName:"p"},"ie")," CLI tool!"))}m.isMDXComponent=!0},8312:(e,n,t)=>{t.d(n,{Z:()=>a});const a=t.p+"assets/images/template-repo-6cc54a91c4b00717cf92334e56c8ec1b.png"}}]); \ No newline at end of file diff --git a/assets/js/28f080da.f3fdf67d.js b/assets/js/28f080da.f3fdf67d.js new file mode 100644 index 00000000..e8ae60ae --- /dev/null +++ b/assets/js/28f080da.f3fdf67d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[806],{4137:(e,n,t)=>{t.d(n,{Zo:()=>s,kt:()=>c});var a=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function l(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var p=a.createContext({}),u=function(e){var n=a.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},s=function(e){var n=u(e.components);return a.createElement(p.Provider,{value:n},e.children)},m={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},d=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,l=e.originalType,p=e.parentName,s=r(e,["components","mdxType","originalType","parentName"]),d=u(t),c=i,g=d["".concat(p,".").concat(c)]||d[c]||m[c]||l;return t?a.createElement(g,o(o({ref:n},s),{},{components:t})):a.createElement(g,o({ref:n},s))}));function c(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var l=t.length,o=new Array(l);o[0]=d;var r={};for(var p in n)hasOwnProperty.call(n,p)&&(r[p]=n[p]);r.originalType=e,r.mdxType="string"==typeof e?e:i,o[1]=r;for(var u=2;u{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>o,default:()=>m,frontMatter:()=>l,metadata:()=>r,toc:()=>u});var a=t(7462),i=(t(7294),t(4137));const l={"sidebar-position":1},o="How to build plugins",r={unversionedId:"developers/how-to-build-plugins",id:"developers/how-to-build-plugins",title:"How to build plugins",description:"The IF is designed to be as composable as possible. This means you can develop your own plugins and use them in a pipeline.",source:"@site/docs/developers/how-to-build-plugins.md",sourceDirName:"developers",slug:"/developers/how-to-build-plugins",permalink:"/developers/how-to-build-plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/developers/how-to-build-plugins.md",tags:[],version:"current",frontMatter:{"sidebar-position":1},sidebar:"tutorialSidebar",previous:{title:"Developers",permalink:"/developers/"},next:{title:"How to make plugins production ready",permalink:"/developers/how-to-refine-plugins"}},p={},u=[{value:"Step 1: Use our template repository",id:"step-1-use-our-template-repository",level:2},{value:"Step 2: Writing your plugin code",id:"step-2-writing-your-plugin-code",level:2},{value:"The plugin interface",id:"the-plugin-interface",level:3},{value:"Global config",id:"global-config",level:3},{value:"Methods",id:"methods",level:3},{value:"execute",id:"execute",level:4},{value:"Params",id:"params",level:4},{value:"Returns",id:"returns",level:4},{value:"What are PluginParams?",id:"what-are-pluginparams",level:3},{value:"What are PluginParams?",id:"what-are-pluginparams-1",level:2},{value:"Step 3: Install your plugin",id:"step-3-install-your-plugin",level:2},{value:"Step 4: Load your plugin into IF",id:"step-4-load-your-plugin-into-if",level:2},{value:"Step 5: Publishing your plugin",id:"step-5-publishing-your-plugin",level:2},{value:"Summary of steps",id:"summary-of-steps",level:2},{value:"Next steps",id:"next-steps",level:2},{value:"Appendix: Walk-through of the Sum plugin",id:"appendix-walk-through-of-the-sum-plugin",level:2}],s={toc:u};function m(e){let{components:n,...l}=e;return(0,i.kt)("wrapper",(0,a.Z)({},s,l,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"how-to-build-plugins"},"How to build plugins"),(0,i.kt)("p",null,"The IF is designed to be as composable as possible. This means you can develop your own plugins and use them in a pipeline.\nTo help developers write Typescript plugins to integrate easily into IF, we provide the ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginInterface")," interface. Here's an overview of the stages you need to follow to integrate your plugin:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"create a Typescript file that implements the ",(0,i.kt)("inlineCode",{parentName:"li"},"PluginInterface")),(0,i.kt)("li",{parentName:"ul"},"install the plugin"),(0,i.kt)("li",{parentName:"ul"},"initialize and invoke the plugin in your manifest file")),(0,i.kt)("h2",{id:"step-1-use-our-template-repository"},"Step 1: Use our template repository"),(0,i.kt)("p",null,"Instead of building up your plugin repository and all the configuration from scratch, you can use our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-plugin-template"},"plugin template repository"),". To use the template, visit the Github repository and click the ",(0,i.kt)("inlineCode",{parentName:"p"},"Use this template")," button. You will have the option to ",(0,i.kt)("inlineCode",{parentName:"p"},"create a new repository")," under your own account. Then, you can clone that repository to your local machine."),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"use our template repository",src:t(8312).Z,width:"3024",height:"1184"})),(0,i.kt)("p",null,"Inside that repository, all you have to do is run ",(0,i.kt)("inlineCode",{parentName:"p"},"npm install typescript")," in the template folder, rename the project in ",(0,i.kt)("inlineCode",{parentName:"p"},"package.json")," and write your plugin code inside ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts"),". All the configuration and setup is taken care of for you. "),(0,i.kt)("h2",{id:"step-2-writing-your-plugin-code"},"Step 2: Writing your plugin code"),(0,i.kt)("p",null,"Now your project is setup, you can focus on your plugin logic. The entry point for your plugin is ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts"),". In this guide it is assumed that all your plugin logic is in ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts")," but depending on the copmplexity of your plugin you might want to split the code across multiple files. ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts")," should always be your entry point, though."),(0,i.kt)("p",null,"The following sections describe the rules your plugin code should conform to. We also have an ",(0,i.kt)("a",{parentName:"p",href:"#appendix-walk-through-of-the-sum-plugin"},"appendix")," that deep dives a real plugin."),(0,i.kt)("h3",{id:"the-plugin-interface"},"The plugin interface"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginInterface")," is structured as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"export type PluginInterface = {\n execute: (\n inputs: PluginParams[],\n config?: Record\n ) => PluginParams[];\n metadata: {\n kind: string;\n };\n [key: string]: any;\n};\n")),(0,i.kt)("p",null,"The interface requires an execute function where your plugin logic is implemented. It should also return metadata. This can include any relevant metadata you want to include, with a minimum requirement being ",(0,i.kt)("inlineCode",{parentName:"p"},"kind: execute"),". "),(0,i.kt)("h3",{id:"global-config"},"Global config"),(0,i.kt)("p",null,"Global config is passed as an argument to the plugin. In your plugin code you can handle it as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"// Here's the function definition - notice that global config is passed in here!\nexport const Plugin = (globalConfig: YourConfig): PluginInterface => {\n\n// in here you have access to globalConfig[your-params]\n\n}\n")),(0,i.kt)("p",null,"The parameters available to you in ",(0,i.kt)("inlineCode",{parentName:"p"},"globalConfig")," depends upon the parameters you pass in the manifest file. For example, the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin has access to ",(0,i.kt)("inlineCode",{parentName:"p"},"input-parameters")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"output-parameter")," in its global config, and it is defined in the ",(0,i.kt)("inlineCode",{parentName:"p"},"Initialize")," block in the manifest file as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n sum:\n method: Sum\n path: '@grnsft/if-plugins'\n global-config:\n input-parameters: ['cpu/energy', 'network/energy']\n output-parameter: 'energy'\n")),(0,i.kt)("h3",{id:"methods"},"Methods"),(0,i.kt)("h4",{id:"execute"},"execute"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"execute()")," is where the main calculation logic of the plugin is implemented. It always takes ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," (an array of ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginParams"),") as an argument and returns an updated set of ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs"),"."),(0,i.kt)("h4",{id:"params"},"Params"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Param"),(0,i.kt)("th",{parentName:"tr",align:null},"Type"),(0,i.kt)("th",{parentName:"tr",align:null},"Purpose"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"inputs")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"PluginParams[]")),(0,i.kt)("td",{parentName:"tr",align:null},"Array of data provided in the ",(0,i.kt)("inlineCode",{parentName:"td"},"inputs")," field of a component in a manifest file")))),(0,i.kt)("h4",{id:"returns"},"Returns"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Return value"),(0,i.kt)("th",{parentName:"tr",align:null},"Type"),(0,i.kt)("th",{parentName:"tr",align:null},"Purpose"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"outputs")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"Promise")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"Promise")," resolving to an array of updated ",(0,i.kt)("inlineCode",{parentName:"td"},"PluginParams[]"))))),(0,i.kt)("h3",{id:"what-are-pluginparams"},"What are ",(0,i.kt)("inlineCode",{parentName:"h3"},"PluginParams"),"?"),(0,i.kt)("h2",{id:"what-are-pluginparams-1"},"What are ",(0,i.kt)("inlineCode",{parentName:"h2"},"PluginParams"),"?"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"PluginParams")," are a fundamental data type in the Impact Framework. The type is defined as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"export type PluginParams = {\n [key: string]: any;\n};\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginParams")," type therefore defines an array of key-value pairs."),(0,i.kt)("p",null,"IF needs to know about all the parameters used in each pipeline. The default behaviour is that it grabs parameters from a local file, ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts"),". This file defines the standard set of parameter names, their units, a descriptiona nd the method used to aggregate them across time or across a tree."),(0,i.kt)("p",null,"If your new plugin uses new parameters that are not included in ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts"),", you can simply add them to your manifest file in a section named ",(0,i.kt)("inlineCode",{parentName:"p"},"params"),". For example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: params-demo\ndescription: null\ntags:\nparams: \n - name: new-param-1\n description: dummy\n aggregation: sum\n unit: MT\n - name: new-param-2\n description: dummy\n aggregation: sum\n unit: s\n")),(0,i.kt)("p",null,"This will append the new parameter informatrion to the object loaded from ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts")," and you can use your plugin as normal. In effect, you have append-only access to ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts")," via your manifest file without ever having to change any IF source code."),(0,i.kt)("p",null,"However, if you are an advanced user and you want to use something other than out recommended standard set of parameters, you can provide a replacement ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts")," file on the command line. This file should be a ",(0,i.kt)("inlineCode",{parentName:"p"},"json")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"js"),"/",(0,i.kt)("inlineCode",{parentName:"p"},"ts")," file with the ame structure as our ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts"),". You can rename the file. You then pass the path to the file to the ",(0,i.kt)("inlineCode",{parentName:"p"},"override-params")," command."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest --override-params \n")),(0,i.kt)("h2",{id:"step-3-install-your-plugin"},"Step 3: Install your plugin"),(0,i.kt)("p",null,"Now your plugin code is written, you can install it to make it available to IF."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm run build\n")),(0,i.kt)("p",null,"Then use ",(0,i.kt)("inlineCode",{parentName:"p"},"npm link")," to create a package that can be installed into IF:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm link\n")),(0,i.kt)("h2",{id:"step-4-load-your-plugin-into-if"},"Step 4: Load your plugin into IF"),(0,i.kt)("p",null,"Now your plugin is ready to run in IF. First install your plugin by navigating to the ",(0,i.kt)("inlineCode",{parentName:"p"},"if")," project folder and running:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm link new-plugin\n")),(0,i.kt)("p",null,"replacing ",(0,i.kt)("inlineCode",{parentName:"p"},"new-plugin")," with your plugin name as defined in the plugin's ",(0,i.kt)("inlineCode",{parentName:"p"},"package.json"),". If you are not sure, the name can be checked by running ",(0,i.kt)("inlineCode",{parentName:"p"},"npm ls -g --depth=0 --link=true"),"."),(0,i.kt)("p",null,"Your plugin is now ready to be run in IF. All that remains is to add your plugin to your manifest file. This means adding it to the ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize block")," and adding it to the component pipelines where you want your plugin to be executed. For example, an ",(0,i.kt)("inlineCode",{parentName:"p"},"initilize")," block might look as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n new-plugin:\n method: YourFunctionName\n path: 'new-plugin'\n global-config:\n something: true \n")),(0,i.kt)("p",null,"Run your manifest uisng"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"np run ie -- --manifest \n")),(0,i.kt)("p",null,"If you have to link more than one local plugin, for example to test your plugin in a pipeline, you can do so with"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm link new-plugin --save\n")),(0,i.kt)("p",null,"This will create an entry like ",(0,i.kt)("inlineCode",{parentName:"p"},'"new-plugin": "file:path/to/your/plugin"')," in the ",(0,i.kt)("inlineCode",{parentName:"p"},"package.json")," which links to your local plugin. This way, multiple plugins can be linked at once. Of course, these changes should not be committed, but they can be helpful for local testing."),(0,i.kt)("h2",{id:"step-5-publishing-your-plugin"},"Step 5: Publishing your plugin"),(0,i.kt)("p",null,"Now you have run your plugin locally and you are happy with how it works, you can make it public by publishing it to a public Github repository. Now all you have to do to use it in a manifest file is ",(0,i.kt)("inlineCode",{parentName:"p"},"npm install")," it and pass the path to the Github repository in the plugin ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block."),(0,i.kt)("p",null,"For example, for a plugin saved in ",(0,i.kt)("inlineCode",{parentName:"p"},"github.com/my-repo/new-plugin")," you can do the following:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"npm install https://github.com/my-repo/new-plugin\n")),(0,i.kt)("p",null,"Then, in your manifest file, provide the path in the plugin instantiation. You also need to specify which function the plugin instantiates. Let's say you are using the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin from the example above:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: plugin-demo\ndescription: loads plugin\ntags: null\ninitialize:\n plugins:\n - name: new-plugin\n kind: plugin\n method: FunctionName\n path: https://github.com/my-repo/new-plugin\ntree:\n children:\n child:\n config:\n inputs:\n")),(0,i.kt)("p",null,"Now, when you run the manifest file, it will load the plugin automatically."),(0,i.kt)("p",null,"You can run this using the globally installed IF as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest \n")),(0,i.kt)("h2",{id:"summary-of-steps"},"Summary of steps"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Copy our template repository and update ",(0,i.kt)("inlineCode",{parentName:"li"},"package.json")),(0,i.kt)("li",{parentName:"ul"},"Add your plugin code to ",(0,i.kt)("inlineCode",{parentName:"li"},"index.ts")),(0,i.kt)("li",{parentName:"ul"},"Build and link the plugin using ",(0,i.kt)("inlineCode",{parentName:"li"},"npm run build && npm link")),(0,i.kt)("li",{parentName:"ul"},"Load your plugin into ",(0,i.kt)("inlineCode",{parentName:"li"},"if")," using ",(0,i.kt)("inlineCode",{parentName:"li"},"npm link")),(0,i.kt)("li",{parentName:"ul"},"Initialize your plugin and add it to a pipeline in your manifest file."),(0,i.kt)("li",{parentName:"ul"},"Publish your plugin to Github")),(0,i.kt)("p",null,"You should also create unit tests for your plugin to demonstrate correct execution and handling of corner cases."),(0,i.kt)("h2",{id:"next-steps"},"Next steps"),(0,i.kt)("p",null,"You can read our more advanced guide on ",(0,i.kt)("a",{parentName:"p",href:"/developers/how-to-refine-plugins"},"how to refine your plugins"),"."),(0,i.kt)("h2",{id:"appendix-walk-through-of-the-sum-plugin"},"Appendix: Walk-through of the Sum plugin"),(0,i.kt)("p",null,"To demonstrate how to build a plugin that conforms to the ",(0,i.kt)("inlineCode",{parentName:"p"},"pluginInterface"),", let's examine the ",(0,i.kt)("inlineCode",{parentName:"p"},"sum")," plugin."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"sum")," plugin implements the following logic:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"sum whatever is provided in the ",(0,i.kt)("inlineCode",{parentName:"li"},"input-parameters")," field from ",(0,i.kt)("inlineCode",{parentName:"li"},"globalConfig"),"."),(0,i.kt)("li",{parentName:"ul"},"append the result to each element in the output array with the name provided as ",(0,i.kt)("inlineCode",{parentName:"li"},"output-parameter")," in ",(0,i.kt)("inlineCode",{parentName:"li"},"globalConfig"),".")),(0,i.kt)("p",null,"Let's look at how you would implement this from scratch:"),(0,i.kt)("p",null,"The plugin must be a function conforming to ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginInterface"),". You can call the function ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum"),", and inside the body you can add the signature for the ",(0,i.kt)("inlineCode",{parentName:"p"},"execute")," method:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"export const Sum = (globalConfig: SumConfig): PluginInterface => {\n const errorBuilder = buildErrorMessage(Sum.name);\n const metadata = {\n kind: 'execute',\n };\n\n /**\n * Calculate the sum of each input.\n */\n const execute = async (inputs: PluginParams[]): Promise => {\n\n };\n\n return {\n metadata,\n execute,\n };\n\n}\n")),(0,i.kt)("p",null,"Your plugin now has the basic structure required for IF integration. Your next task is to add code to the body of ",(0,i.kt)("inlineCode",{parentName:"p"},"execute")," to enable the actual plugin logic to be implemented."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"execute")," function should grab the ",(0,i.kt)("inlineCode",{parentName:"p"},"input-parameters")," (the values to sum) from ",(0,i.kt)("inlineCode",{parentName:"p"},"globalConfig"),". it should then iterate over the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array, get the values for each of the ",(0,i.kt)("inlineCode",{parentName:"p"},"input-parameters")," and append them to the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array, using the name from the ",(0,i.kt)("inlineCode",{parentName:"p"},"output-parameter")," value in ",(0,i.kt)("inlineCode",{parentName:"p"},"globalConfig"),". Here's what this can look like, with the actual calculation pushed to a separate function, ",(0,i.kt)("inlineCode",{parentName:"p"},"calculateSum"),". "),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"}," /**\n * Calculate the sum of each input.\n */\n const execute = async (inputs: PluginParams[]): Promise => {\n const inputParameters = globalConfig['input-parameters'];\n const outputParameter = globalConfig['output-parameter'];\n\n return inputs.map(input => {\n return {\n ...input,\n [outputParameter]: calculateSum(input, inputParameters),\n };\n });\n\n return {\n metadata,\n execute,\n };\n}\n")),(0,i.kt)("p",null,"Now we just need to define what happens in ",(0,i.kt)("inlineCode",{parentName:"p"},"calculateSum")," - this can be a simple ",(0,i.kt)("inlineCode",{parentName:"p"},"reduce"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"}," /**\n * Calculates the sum of the energy components.\n */\n const calculateSum = (input: PluginParams, inputParameters: string[]) =>\n inputParameters.reduce(\n (accumulator, metricToSum) => accumulator + input[metricToSum],\n 0\n );\n")),(0,i.kt)("p",null,"Note that this example did not include any validation or error handling - you will likely want to add some for a real plugin."),(0,i.kt)("p",null,"Finally, if your plugin used any fields in ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," or created new ",(0,i.kt)("inlineCode",{parentName:"p"},"outputs")," that have not been used in the Impact Framework before, then you should add them to ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts"),"."),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"params.ts")," can be found in the path ",(0,i.kt)("inlineCode",{parentName:"p"},"src/config"),"."),(0,i.kt)("p",null,"Each entry in ",(0,i.kt)("inlineCode",{parentName:"p"},"params.ts")," looks as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"carbon:\n description: an amount of carbon emitted into the atmosphere\n unit: gCO2e\n aggregation: sum\n")),(0,i.kt)("p",null,"This information allows IF to programmatically make decisions about how to handle values in features such as aggregation, time normalization and visualizations, and also acts as a global reference document for understanding IF data. The example above is for ",(0,i.kt)("inlineCode",{parentName:"p"},"carbon"),"."),(0,i.kt)("p",null,"You should add your new data, give a name, define a unit and short description. The ",(0,i.kt)("inlineCode",{parentName:"p"},"aggregation")," field determines how the value is treated when some manipulation has to be done to spread the value over time or aggregate it."),(0,i.kt)("p",null,"For absolute metrics like carbon, the right value is ",(0,i.kt)("inlineCode",{parentName:"p"},"sum")," because you would want to add carbon emissions from each timestep when you aggregate over time."),(0,i.kt)("p",null,"For proportional metrics, the right value is ",(0,i.kt)("inlineCode",{parentName:"p"},"avg"),". For example, you would want to calculate the average ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/utilization")," - it would not make sense to sum it when aggregating over multiple timesteps."),(0,i.kt)("p",null,"Finally, values that should always be presented identically regardless of any aggregation, such as names or global constants, should be given the ",(0,i.kt)("inlineCode",{parentName:"p"},"aggregation-method")," value ",(0,i.kt)("inlineCode",{parentName:"p"},"none"),"."),(0,i.kt)("p",null,"Now you are ready to run your plugin using the ",(0,i.kt)("inlineCode",{parentName:"p"},"ie")," CLI tool!"))}m.isMDXComponent=!0},8312:(e,n,t)=>{t.d(n,{Z:()=>a});const a=t.p+"assets/images/template-repo-6cc54a91c4b00717cf92334e56c8ec1b.png"}}]); \ No newline at end of file diff --git a/assets/js/29966d47.72f5fed4.js b/assets/js/29966d47.96892e78.js similarity index 65% rename from assets/js/29966d47.72f5fed4.js rename to assets/js/29966d47.96892e78.js index 0ffffa8b..ccf3e3b6 100644 --- a/assets/js/29966d47.72f5fed4.js +++ b/assets/js/29966d47.96892e78.js @@ -1 +1 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[117],{4137:(e,t,r)=>{r.d(t,{Zo:()=>s,kt:()=>f});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var p=n.createContext({}),l=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},s=function(e){var t=l(e.components);return n.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=l(r),f=a,d=u["".concat(p,".").concat(f)]||u[f]||m[f]||o;return r?n.createElement(d,i(i({ref:t},s),{},{components:r})):n.createElement(d,i({ref:t},s))}));function f(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,i=new Array(o);i[0]=u;var c={};for(var p in t)hasOwnProperty.call(t,p)&&(c[p]=t[p]);c.originalType=e,c.mdxType="string"==typeof e?e:a,i[1]=c;for(var l=2;l{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>m,frontMatter:()=>o,metadata:()=>c,toc:()=>l});var n=r(7462),a=(r(7294),r(4137));const o={sidebar_position:2},i="Major Concepts",c={unversionedId:"major-concepts/index",id:"major-concepts/index",title:"Major Concepts",description:"Here you will find explanations for the fundamnetal Impact Framework concepts. This includes:",source:"@site/docs/major-concepts/index.md",sourceDirName:"major-concepts",slug:"/major-concepts/",permalink:"/major-concepts/",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/major-concepts/index.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Introduction",permalink:"/intro"},next:{title:"Design philosophy",permalink:"/major-concepts/design-philosophy"}},p={},l=[],s={toc:l};function m(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},s,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"major-concepts"},"Major Concepts"),(0,a.kt)("p",null,"Here you will find explanations for the fundamnetal Impact Framework concepts. This includes: "),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/major-concepts/design-philosophy"},"Design philosophy")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/major-concepts/manifest-file"},"Manifest files")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"./impact-enbgine.md"},"IF")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"./durations.md"},"Time")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/major-concepts/aggregation"},"Aggregation")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/major-concepts/plugins"},"Plugins")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/major-concepts/groupby"},"Group-by")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/major-concepts/parameters"},"Parameters"))))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[117],{4137:(e,t,r)=>{r.d(t,{Zo:()=>s,kt:()=>f});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var p=n.createContext({}),l=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},s=function(e){var t=l(e.components);return n.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=l(r),f=a,d=u["".concat(p,".").concat(f)]||u[f]||m[f]||o;return r?n.createElement(d,i(i({ref:t},s),{},{components:r})):n.createElement(d,i({ref:t},s))}));function f(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,i=new Array(o);i[0]=u;var c={};for(var p in t)hasOwnProperty.call(t,p)&&(c[p]=t[p]);c.originalType=e,c.mdxType="string"==typeof e?e:a,i[1]=c;for(var l=2;l{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>m,frontMatter:()=>o,metadata:()=>c,toc:()=>l});var n=r(7462),a=(r(7294),r(4137));const o={sidebar_position:2},i="Major Concepts",c={unversionedId:"major-concepts/index",id:"major-concepts/index",title:"Major Concepts",description:"Here you will find explanations for the fundamnetal Impact Framework concepts. This includes:",source:"@site/docs/major-concepts/index.md",sourceDirName:"major-concepts",slug:"/major-concepts/",permalink:"/major-concepts/",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/index.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Introduction",permalink:"/intro"},next:{title:"Design philosophy",permalink:"/major-concepts/design-philosophy"}},p={},l=[],s={toc:l};function m(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},s,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"major-concepts"},"Major Concepts"),(0,a.kt)("p",null,"Here you will find explanations for the fundamnetal Impact Framework concepts. This includes: "),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/major-concepts/design-philosophy"},"Design philosophy")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/major-concepts/manifest-file"},"Manifest files")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"./impact-enbgine.md"},"IF")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"./durations.md"},"Time")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/major-concepts/aggregation"},"Aggregation")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/major-concepts/plugins"},"Plugins")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/major-concepts/groupby"},"Group-by")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/major-concepts/parameters"},"Parameters"))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/2d14298d.cf4df2f6.js b/assets/js/2d14298d.cf4df2f6.js new file mode 100644 index 00000000..5f400ca5 --- /dev/null +++ b/assets/js/2d14298d.cf4df2f6.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[754],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>c});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),p=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=p(e.components);return r.createElement(s.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=p(n),c=a,h=d["".concat(s,".").concat(c)]||d[c]||m[c]||i;return n?r.createElement(h,o(o({ref:t},u),{},{components:n})):r.createElement(h,o({ref:t},u))}));function c(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,o[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>m,frontMatter:()=>i,metadata:()=>l,toc:()=>p});var r=n(7462),a=(n(7294),n(4137));const i={"sidebar-position":4},o="How to visualize results",l={unversionedId:"developers/how-to-visualize-results",id:"developers/how-to-visualize-results",title:"How to visualize results",description:"(for any questions / help needed on IF visualization please raise an issue here: IF issues)",source:"@site/docs/developers/how-to-visualize-results.md",sourceDirName:"developers",slug:"/developers/how-to-visualize-results",permalink:"/developers/how-to-visualize-results",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/developers/how-to-visualize-results.md",tags:[],version:"current",frontMatter:{"sidebar-position":4},sidebar:"tutorialSidebar",previous:{title:"How to make plugins production ready",permalink:"/developers/how-to-refine-plugins"},next:{title:"How to write unit tests",permalink:"/developers/how-to-write-unit-tests"}},s={},p=[{value:"Simple HTML Exporter plugin",id:"simple-html-exporter-plugin",level:2},{value:"Input",id:"input",level:3},{value:"Output",id:"output",level:3},{value:"Example manifest",id:"example-manifest",level:3},{value:"Visualization example",id:"visualization-example",level:3},{value:"Grafana",id:"grafana",level:2},{value:"Visualization example",id:"visualization-example-1",level:3}],u={toc:p};function m(e){let{components:t,...i}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,i,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"how-to-visualize-results"},"How to visualize results"),(0,a.kt)("p",null,(0,a.kt)("em",{parentName:"p"},"(for any questions / help needed on IF visualization please raise an issue here: ",(0,a.kt)("a",{parentName:"em",href:"https://github.com/Green-Software-Foundation/if/issues"},"IF issues"),")")),(0,a.kt)("p",null,"There are currently 2 ways to visualize Impact Framework outputs:"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"Using the ",(0,a.kt)("strong",{parentName:"li"},"Simple HTML Exporter plugin"),"."),(0,a.kt)("li",{parentName:"ol"},"Using ",(0,a.kt)("strong",{parentName:"li"},"Grafana"),".")),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"Grafana")," is the more standardized method for visualization. It also provides much more control over what's being visualized and how. Nevertheless, it requires some setting up."),(0,a.kt)("p",null,"Using the ",(0,a.kt)("strong",{parentName:"p"},"Simple HTML Exporter plugin")," is much simpler as it only requires a single configuration in the manifest yml file. However, it only supports visualization of Energy and Carbon values over time.\nIf you need to visualize other output parameters you can either use ",(0,a.kt)("strong",{parentName:"p"},"Grafana")," or alternatively enhance the ",(0,a.kt)("strong",{parentName:"p"},"Simple HTML Exporter plugin")," to be a bit less simple..."),(0,a.kt)("h2",{id:"simple-html-exporter-plugin"},"Simple HTML Exporter plugin"),(0,a.kt)("h3",{id:"input"},"Input"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"energy")," (kWh), ",(0,a.kt)("inlineCode",{parentName:"p"},"carbon")," (gCO2) and ",(0,a.kt)("inlineCode",{parentName:"p"},"timestamp")," should be included in the input array items."),(0,a.kt)("h3",{id:"output"},"Output"),(0,a.kt)("p",null,'This plugin acts as a relay, returning the input as-is, with the generated HTML acting as a "side effect".'),(0,a.kt)("h3",{id:"example-manifest"},"Example manifest"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name: simple-html-exporter-demo\ndescription:\ntags:\ninitialize:\n plugins:\n 'simple-html-exporter':\n method: SimpleHtmlExporter\n path: \"@grnsft/if-plugins\"\ntree:\n children:\n child:\n pipeline:\n - simple-html-exporter\n config:\n simple-html-exporter:\n html-path: /usr/local/data/html-export.html\n inputs:\n - timestamp: '2021-01-01T00:00:00Z',\n energy: 0.00001841,\n carbon: 0.0104062,\n")),(0,a.kt)("h3",{id:"visualization-example"},"Visualization example"),(0,a.kt)("p",null,"The resulting HTML file can then be viewed using a web-browser"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"img.png",src:n(231).Z,width:"1912",height:"688"})),(0,a.kt)("h2",{id:"grafana"},"Grafana"),(0,a.kt)("p",null,"(See also ",(0,a.kt)("a",{parentName:"p",href:"https://grafana.com/"},"https://grafana.com/"),")"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"Grafana")," is an open source analytics & monitoring solution for every database."),(0,a.kt)("p",null,"One of its main features is the ability to create dashboards with various types of data visualizations."),(0,a.kt)("p",null,"Please follow these instructions ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/blob/dev/grafana/IF_GRAFANA_SETUP.md"},"here")," to set up a ",(0,a.kt)("strong",{parentName:"p"},"Grafana")," dashboard."),(0,a.kt)("p",null,"This method requires converting the resulting output yml into a CSV. The standard way to do so would be to use the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/csv-export"},"CSV export")," plugin."),(0,a.kt)("h3",{id:"visualization-example-1"},"Visualization example"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"img.png",src:n(3127).Z,width:"1087",height:"671"})))}m.isMDXComponent=!0},3127:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/grafana-sample-396404157784310b5e568dd13bbcf331.png"},231:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/simple-html-exporter-sample-436d438eb2d013fcac5d65256656ea4b.png"}}]); \ No newline at end of file diff --git a/assets/js/2d14298d.d3baaf66.js b/assets/js/2d14298d.d3baaf66.js deleted file mode 100644 index e8382022..00000000 --- a/assets/js/2d14298d.d3baaf66.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[754],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>c});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),p=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=p(e.components);return r.createElement(s.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=p(n),c=a,h=d["".concat(s,".").concat(c)]||d[c]||m[c]||i;return n?r.createElement(h,o(o({ref:t},u),{},{components:n})):r.createElement(h,o({ref:t},u))}));function c(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,o[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>m,frontMatter:()=>i,metadata:()=>l,toc:()=>p});var r=n(7462),a=(n(7294),n(4137));const i={"sidebar-position":4},o="How to visualize results",l={unversionedId:"developers/how-to-visualize-results",id:"developers/how-to-visualize-results",title:"How to visualize results",description:"(for any questions / help needed on IF visualization please raise an issue here: IF issues)",source:"@site/docs/developers/how-to-visualize-results.md",sourceDirName:"developers",slug:"/developers/how-to-visualize-results",permalink:"/developers/how-to-visualize-results",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/developers/how-to-visualize-results.md",tags:[],version:"current",frontMatter:{"sidebar-position":4},sidebar:"tutorialSidebar",previous:{title:"How to make plugins production ready",permalink:"/developers/how-to-refine-plugins"},next:{title:"How to write unit tests",permalink:"/developers/how-to-write-unit-tests"}},s={},p=[{value:"Simple HTML Exporter plugin",id:"simple-html-exporter-plugin",level:2},{value:"Input",id:"input",level:3},{value:"Output",id:"output",level:3},{value:"Example manifest",id:"example-manifest",level:3},{value:"Visualization example",id:"visualization-example",level:3},{value:"Grafana",id:"grafana",level:2},{value:"Visualization example",id:"visualization-example-1",level:3}],u={toc:p};function m(e){let{components:t,...i}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,i,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"how-to-visualize-results"},"How to visualize results"),(0,a.kt)("p",null,(0,a.kt)("em",{parentName:"p"},"(for any questions / help needed on IF visualization please raise an issue here: ",(0,a.kt)("a",{parentName:"em",href:"https://github.com/Green-Software-Foundation/if/issues"},"IF issues"),")")),(0,a.kt)("p",null,"There are currently 2 ways to visualize Impact Framework outputs:"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"Using the ",(0,a.kt)("strong",{parentName:"li"},"Simple HTML Exporter plugin"),"."),(0,a.kt)("li",{parentName:"ol"},"Using ",(0,a.kt)("strong",{parentName:"li"},"Grafana"),".")),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"Grafana")," is the more standardized method for visualization. It also provides much more control over what's being visualized and how. Nevertheless, it requires some setting up."),(0,a.kt)("p",null,"Using the ",(0,a.kt)("strong",{parentName:"p"},"Simple HTML Exporter plugin")," is much simpler as it only requires a single configuration in the manifest yml file. However, it only supports visualization of Energy and Carbon values over time.\nIf you need to visualize other output parameters you can either use ",(0,a.kt)("strong",{parentName:"p"},"Grafana")," or alternatively enhance the ",(0,a.kt)("strong",{parentName:"p"},"Simple HTML Exporter plugin")," to be a bit less simple..."),(0,a.kt)("h2",{id:"simple-html-exporter-plugin"},"Simple HTML Exporter plugin"),(0,a.kt)("h3",{id:"input"},"Input"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"energy")," (kWh), ",(0,a.kt)("inlineCode",{parentName:"p"},"carbon")," (gCO2) and ",(0,a.kt)("inlineCode",{parentName:"p"},"timestamp")," should be included in the input array items."),(0,a.kt)("h3",{id:"output"},"Output"),(0,a.kt)("p",null,'This plugin acts as a relay, returning the input as-is, with the generated HTML acting as a "side effect".'),(0,a.kt)("h3",{id:"example-manifest"},"Example manifest"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name: simple-html-exporter-demo\ndescription:\ntags:\ninitialize:\n plugins:\n 'simple-html-exporter':\n method: SimpleHtmlExporter\n path: \"@grnsft/if-plugins\"\ntree:\n children:\n child:\n pipeline:\n - simple-html-exporter\n config:\n simple-html-exporter:\n html-path: /usr/local/data/html-export.html\n inputs:\n - timestamp: '2021-01-01T00:00:00Z',\n energy: 0.00001841,\n carbon: 0.0104062,\n")),(0,a.kt)("h3",{id:"visualization-example"},"Visualization example"),(0,a.kt)("p",null,"The resulting HTML file can then be viewed using a web-browser"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"img.png",src:n(231).Z,width:"1912",height:"688"})),(0,a.kt)("h2",{id:"grafana"},"Grafana"),(0,a.kt)("p",null,"(See also ",(0,a.kt)("a",{parentName:"p",href:"https://grafana.com/"},"https://grafana.com/"),")"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"Grafana")," is an open source analytics & monitoring solution for every database."),(0,a.kt)("p",null,"One of its main features is the ability to create dashboards with various types of data visualizations."),(0,a.kt)("p",null,"Please follow these instructions ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/blob/dev/grafana/IF_GRAFANA_SETUP.md"},"here")," to set up a ",(0,a.kt)("strong",{parentName:"p"},"Grafana")," dashboard."),(0,a.kt)("p",null,"This method requires converting the resulting output yml into a CSV. The standard way to do so would be to use the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/csv-export"},"CSV export")," plugin."),(0,a.kt)("h3",{id:"visualization-example-1"},"Visualization example"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"img.png",src:n(3127).Z,width:"1087",height:"671"})))}m.isMDXComponent=!0},3127:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/grafana-sample-396404157784310b5e568dd13bbcf331.png"},231:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/simple-html-exporter-sample-436d438eb2d013fcac5d65256656ea4b.png"}}]); \ No newline at end of file diff --git a/assets/js/3c29e2ef.85deae6f.js b/assets/js/3c29e2ef.85deae6f.js deleted file mode 100644 index 1896f298..00000000 --- a/assets/js/3c29e2ef.85deae6f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[702],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>d});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=i.createContext({}),p=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=p(e.components);return i.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=p(n),d=a,g=m["".concat(s,".").concat(d)]||m[d]||c[d]||r;return n?i.createElement(g,o(o({ref:t},u),{},{components:n})):i.createElement(g,o({ref:t},u))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,o[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>l,toc:()=>p});var i=n(7462),a=(n(7294),n(4137));const r={},o="Contribution Guidelines",l={unversionedId:"contributions",id:"contributions",title:"Contribution Guidelines",description:"First off, thanks for taking the time to contribute! \ud83c\udf89",source:"@site/docs/contributions.md",sourceDirName:".",slug:"/contributions",permalink:"/contributions",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/contributions.md",tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"FAQs",permalink:"/FAQ"}},s={},p=[{value:"Table of Contents omit from toc ",id:"table-of-contents--omit-from-toc-",level:2},{value:"Code Contributions",id:"code-contributions",level:2},{value:"Step 1: Fork",id:"step-1-fork",level:4},{value:"Step 2: Branch",id:"step-2-branch",level:4},{value:"Step 3: Commit",id:"step-3-commit",level:4},{value:"Step 4: Sync",id:"step-4-sync",level:4},{value:"Step 5: Push",id:"step-5-push",level:4},{value:"Step 6: Pull Request",id:"step-6-pull-request",level:4},{value:"Commit message guidelines",id:"commit-message-guidelines",level:2},{value:"Coding guidelines",id:"coding-guidelines",level:2},{value:"Code structuring patterns",id:"code-structuring-patterns",level:4},{value:"Object Oriented Programming",id:"object-oriented-programming",level:6},{value:"Functional Programming",id:"functional-programming",level:6},{value:"Naming patterns",id:"naming-patterns",level:4},{value:"Documentation",id:"documentation",level:4},{value:"Writing tests",id:"writing-tests",level:4}],u={toc:p};function c(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,i.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"contribution-guidelines"},"Contribution Guidelines"),(0,a.kt)("p",null,"First off, thanks for taking the time to contribute! \ud83c\udf89"),(0,a.kt)("p",null,"The following document is a rule set of guidelines for contributing."),(0,a.kt)("h2",{id:"table-of-contents--omit-from-toc-"},"Table of Contents "),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#contribution-guidelines"},"Contribution Guidelines"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#code-contributions"},"Code Contributions"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#step-1-fork"},"Step 1: Fork")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#step-2-branch"},"Step 2: Branch")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#step-3-commit"},"Step 3: Commit")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#step-4-sync"},"Step 4: Sync")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#step-5-push"},"Step 5: Push")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#step-6-pull-request"},"Step 6: Pull Request")))),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#commit-message-guidelines"},"Commit message guidelines")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#coding-guidelines"},"Coding guidelines"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#code-structuring-patterns"},"Code structuring patterns"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#object-oriented-programming"},"Object Oriented Programming")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#functional-programming"},"Functional Programming")))),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#naming-patterns"},"Naming patterns")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#documentation"},"Documentation")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#writing-tests"},"Writing tests"))))))),(0,a.kt)("h2",{id:"code-contributions"},"Code Contributions"),(0,a.kt)("h4",{id:"step-1-fork"},"Step 1: Fork"),(0,a.kt)("p",null,"Fork the project on ",(0,a.kt)("a",{parentName:"p",href:"git@github.com:Green-Software-Foundation/if.git"},"GitHub")),(0,a.kt)("p",null,"You then have your own copy of the repository that you can change. "),(0,a.kt)("h4",{id:"step-2-branch"},"Step 2: Branch"),(0,a.kt)("p",null,"Create new branch in your forked copy of the ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," repository, which will contain your new feature, fix or change. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"```bash\n$ git checkout -b \n```\n")),(0,a.kt)("h4",{id:"step-3-commit"},"Step 3: Commit"),(0,a.kt)("p",null,"Make sure git knows your name and email address:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'$ git config --global user.name "Example User"\n$ git config --global user.email "user@example.com"\n')),(0,a.kt)("p",null,"Commiting multiple files with changes on multiple resources is not allowed.\nCommit message should clearly describe on which resource changes are made."),(0,a.kt)("p",null,"Add and commit:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"$ git add my/changed/files\n$ git commit\n")),(0,a.kt)("p",null,"Commit your changes in logical chunks. Please do not push all changes in one commit."),(0,a.kt)("blockquote",null,(0,a.kt)("p",{parentName:"blockquote"},"Run ",(0,a.kt)("inlineCode",{parentName:"p"},"npm run fix")," before commiting for not having conflict with CI linter.")),(0,a.kt)("p",null,"Please adhere to these ",(0,a.kt)("a",{parentName:"p",href:"#commit-message-guidelines"},"Commit message guidelines"),"\nor your code is unlikely be merged into the main project."),(0,a.kt)("h4",{id:"step-4-sync"},"Step 4: Sync"),(0,a.kt)("p",null,"Use git pull/merge to synchronize your work with the IF repository."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"$ git pull upstream dev\n")),(0,a.kt)("h4",{id:"step-5-push"},"Step 5: Push"),(0,a.kt)("p",null,"Push your topic branch to your fork:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"$ git push origin \n")),(0,a.kt)("h4",{id:"step-6-pull-request"},"Step 6: Pull Request"),(0,a.kt)("p",null,"Open a Pull Request from your fork of the repository to the ",(0,a.kt)("inlineCode",{parentName:"p"},"dev")," branch of the IF repository with a clear title and description according to ",(0,a.kt)("a",{parentName:"p",href:".github/PULL_REQUEST_TEMPLATE.md"},"template"),"."),(0,a.kt)("p",null,"Pull request should pass all CI which are defined, should have at least one approve. It should adher to the specification for getting approved."),(0,a.kt)("p",null,"CI included lint checks, running tests, and etc."),(0,a.kt)("h2",{id:"commit-message-guidelines"},"Commit message guidelines"),(0,a.kt)("p",null,"The commit message should describe what changed and why."),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"The first line should:"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"contain a short description of the change")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"be 60 characters or less")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"be prefixed with the name of the changed subsystem")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"be entirely in lowercase with the exception of proper nouns, acronyms, and the words that refer to code,\nlike function/variable names"),(0,a.kt)("p",{parentName:"li"},"Examples:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"}," util: add getInitializedPlugin method to plugin.\n deps: add express package to dependencies.\n service: refactor get user.\n"))))),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"Keep the second line blank.\n")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"Wrap all other lines at 72 columns:"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"describe each line in logical chunks"),(0,a.kt)("li",{parentName:"ul"},"start each line with: space hyphen space ( - ...)"),(0,a.kt)("li",{parentName:"ul"},"be entirely in lowercase with the exception of proper nouns, acronyms, and the words that refer to code,\nlike function/variable names")),(0,a.kt)("p",{parentName:"li"},"Examples:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"}," - remove deprecated logger\n - refactor some method\n - add JSDoc on existing function\n")))),(0,a.kt)("h2",{id:"coding-guidelines"},"Coding guidelines"),(0,a.kt)("h4",{id:"code-structuring-patterns"},"Code structuring patterns"),(0,a.kt)("p",null,"Avoid having functions which are responsible to do multiple things at the same time. Make sure one function/method does one thing, and does it well. "),(0,a.kt)("h6",{id:"object-oriented-programming"},"Object Oriented Programming"),(0,a.kt)("p",null,"While following ",(0,a.kt)("inlineCode",{parentName:"p"},"Object Oriented Programming")," paradigm, it's important to follow ",(0,a.kt)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/SOLID"},"SOLID")," principles."),(0,a.kt)("h6",{id:"functional-programming"},"Functional Programming"),(0,a.kt)("p",null,"When designing module of the application in ",(0,a.kt)("inlineCode",{parentName:"p"},"Functional Programming")," paradigm, the key to follow ",(0,a.kt)("a",{parentName:"p",href:"https://dev.to/jamesrweb/principles-of-functional-programming-4b7c"},"basic")," principles."),(0,a.kt)("h4",{id:"naming-patterns"},"Naming patterns"),(0,a.kt)("p",null,"Make sure your ",(0,a.kt)("inlineCode",{parentName:"p"},"class/function/variable")," describes exactly what it does. Avoid using shortened words like txt, arr while doing naming decision. As a naming pattern ",(0,a.kt)("inlineCode",{parentName:"p"},"camel case")," should be used."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'\u274c const a = ""\n\u2705 const mockValue = ""\n\n\u274c const a = (txt: string) => console.log(txt)\n\u2705 const logMessage = (message: string) => console.log(message)\n')),(0,a.kt)("h4",{id:"documentation"},"Documentation"),(0,a.kt)("p",null,"Every logical unit (",(0,a.kt)("inlineCode",{parentName:"p"},"Class, function, method"),") should be covered with appropriate documentation. For documenting such, multi-line comment style is used."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"\u274c const a = (message: string) => console.log(message)\n\n\u2705\n/**\n * Logs given `message` to console.\n **/\nconst logMessage = (message: string) => console.log(message)\n")),(0,a.kt)("p",null,"For documenting variable, expression, single line comments can be used after declaration."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'class MockClass {\n // this is a mock field\n \u274c private mockField: string = "mock-field"\n \u2705 private mockField: string = "mock-field" // Single line documenting style is used.\n}\n')),(0,a.kt)("h4",{id:"writing-tests"},"Writing tests"),(0,a.kt)("p",null,"One test file should be responsible for one module. ",(0,a.kt)("inlineCode",{parentName:"p"},"describe")," blocks should be used for module and function/method description. First ",(0,a.kt)("inlineCode",{parentName:"p"},"describe")," should follow ",(0,a.kt)("inlineCode",{parentName:"p"},"resource/module: ")," pattern. Second describe title should follow ",(0,a.kt)("inlineCode",{parentName:"p"},"method(): ")," pattern. Test units can use either ",(0,a.kt)("inlineCode",{parentName:"p"},"test")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"it"),", title should exactly describe behaviour and input argument. Make sure each test case covers one branch."),(0,a.kt)("p",null,"See example: "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"describe('util/args: ', () => {\n describe('parseProcessArgument(): ', () => {\n it('logs help message if property present in env.', () => {\n ...\n })\n })\n})\n")),(0,a.kt)("p",null,(0,a.kt)("em",{parentName:"p"},(0,a.kt)("a",{parentName:"em",href:"/README.md#ief"},"\u2b05\ufe0f back to the root"))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/3c29e2ef.a772b7cb.js b/assets/js/3c29e2ef.a772b7cb.js new file mode 100644 index 00000000..02f86115 --- /dev/null +++ b/assets/js/3c29e2ef.a772b7cb.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[702],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>d});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=i.createContext({}),p=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=p(e.components);return i.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=p(n),d=a,g=m["".concat(s,".").concat(d)]||m[d]||c[d]||r;return n?i.createElement(g,o(o({ref:t},u),{},{components:n})):i.createElement(g,o({ref:t},u))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,o[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>l,toc:()=>p});var i=n(7462),a=(n(7294),n(4137));const r={},o="Contribution Guidelines",l={unversionedId:"contributions",id:"contributions",title:"Contribution Guidelines",description:"First off, thanks for taking the time to contribute! \ud83c\udf89",source:"@site/docs/contributions.md",sourceDirName:".",slug:"/contributions",permalink:"/contributions",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/contributions.md",tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"FAQs",permalink:"/FAQ"}},s={},p=[{value:"Table of Contents omit from toc ",id:"table-of-contents--omit-from-toc-",level:2},{value:"Code Contributions",id:"code-contributions",level:2},{value:"Step 1: Fork",id:"step-1-fork",level:4},{value:"Step 2: Branch",id:"step-2-branch",level:4},{value:"Step 3: Commit",id:"step-3-commit",level:4},{value:"Step 4: Sync",id:"step-4-sync",level:4},{value:"Step 5: Push",id:"step-5-push",level:4},{value:"Step 6: Pull Request",id:"step-6-pull-request",level:4},{value:"Commit message guidelines",id:"commit-message-guidelines",level:2},{value:"Coding guidelines",id:"coding-guidelines",level:2},{value:"Code structuring patterns",id:"code-structuring-patterns",level:4},{value:"Object Oriented Programming",id:"object-oriented-programming",level:6},{value:"Functional Programming",id:"functional-programming",level:6},{value:"Naming patterns",id:"naming-patterns",level:4},{value:"Documentation",id:"documentation",level:4},{value:"Writing tests",id:"writing-tests",level:4}],u={toc:p};function c(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,i.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"contribution-guidelines"},"Contribution Guidelines"),(0,a.kt)("p",null,"First off, thanks for taking the time to contribute! \ud83c\udf89"),(0,a.kt)("p",null,"The following document is a rule set of guidelines for contributing."),(0,a.kt)("h2",{id:"table-of-contents--omit-from-toc-"},"Table of Contents "),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#contribution-guidelines"},"Contribution Guidelines"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#code-contributions"},"Code Contributions"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#step-1-fork"},"Step 1: Fork")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#step-2-branch"},"Step 2: Branch")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#step-3-commit"},"Step 3: Commit")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#step-4-sync"},"Step 4: Sync")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#step-5-push"},"Step 5: Push")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#step-6-pull-request"},"Step 6: Pull Request")))),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#commit-message-guidelines"},"Commit message guidelines")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#coding-guidelines"},"Coding guidelines"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#code-structuring-patterns"},"Code structuring patterns"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#object-oriented-programming"},"Object Oriented Programming")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#functional-programming"},"Functional Programming")))),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#naming-patterns"},"Naming patterns")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#documentation"},"Documentation")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"#writing-tests"},"Writing tests"))))))),(0,a.kt)("h2",{id:"code-contributions"},"Code Contributions"),(0,a.kt)("h4",{id:"step-1-fork"},"Step 1: Fork"),(0,a.kt)("p",null,"Fork the project on ",(0,a.kt)("a",{parentName:"p",href:"git@github.com:Green-Software-Foundation/if.git"},"GitHub")),(0,a.kt)("p",null,"You then have your own copy of the repository that you can change. "),(0,a.kt)("h4",{id:"step-2-branch"},"Step 2: Branch"),(0,a.kt)("p",null,"Create new branch in your forked copy of the ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," repository, which will contain your new feature, fix or change. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"```bash\n$ git checkout -b \n```\n")),(0,a.kt)("h4",{id:"step-3-commit"},"Step 3: Commit"),(0,a.kt)("p",null,"Make sure git knows your name and email address:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'$ git config --global user.name "Example User"\n$ git config --global user.email "user@example.com"\n')),(0,a.kt)("p",null,"Commiting multiple files with changes on multiple resources is not allowed.\nCommit message should clearly describe on which resource changes are made."),(0,a.kt)("p",null,"Add and commit:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"$ git add my/changed/files\n$ git commit\n")),(0,a.kt)("p",null,"Commit your changes in logical chunks. Please do not push all changes in one commit."),(0,a.kt)("blockquote",null,(0,a.kt)("p",{parentName:"blockquote"},"Run ",(0,a.kt)("inlineCode",{parentName:"p"},"npm run fix")," before commiting for not having conflict with CI linter.")),(0,a.kt)("p",null,"Please adhere to these ",(0,a.kt)("a",{parentName:"p",href:"#commit-message-guidelines"},"Commit message guidelines"),"\nor your code is unlikely be merged into the main project."),(0,a.kt)("h4",{id:"step-4-sync"},"Step 4: Sync"),(0,a.kt)("p",null,"Use git pull/merge to synchronize your work with the IF repository."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"$ git pull upstream dev\n")),(0,a.kt)("h4",{id:"step-5-push"},"Step 5: Push"),(0,a.kt)("p",null,"Push your topic branch to your fork:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"$ git push origin \n")),(0,a.kt)("h4",{id:"step-6-pull-request"},"Step 6: Pull Request"),(0,a.kt)("p",null,"Open a Pull Request from your fork of the repository to the ",(0,a.kt)("inlineCode",{parentName:"p"},"dev")," branch of the IF repository with a clear title and description according to ",(0,a.kt)("a",{parentName:"p",href:".github/PULL_REQUEST_TEMPLATE.md"},"template"),"."),(0,a.kt)("p",null,"Pull request should pass all CI which are defined, should have at least one approve. It should adher to the specification for getting approved."),(0,a.kt)("p",null,"CI included lint checks, running tests, and etc."),(0,a.kt)("h2",{id:"commit-message-guidelines"},"Commit message guidelines"),(0,a.kt)("p",null,"The commit message should describe what changed and why."),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"The first line should:"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"contain a short description of the change")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"be 60 characters or less")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"be prefixed with the name of the changed subsystem")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"be entirely in lowercase with the exception of proper nouns, acronyms, and the words that refer to code,\nlike function/variable names"),(0,a.kt)("p",{parentName:"li"},"Examples:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"}," util: add getInitializedPlugin method to plugin.\n deps: add express package to dependencies.\n service: refactor get user.\n"))))),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"Keep the second line blank.\n")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"Wrap all other lines at 72 columns:"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"describe each line in logical chunks"),(0,a.kt)("li",{parentName:"ul"},"start each line with: space hyphen space ( - ...)"),(0,a.kt)("li",{parentName:"ul"},"be entirely in lowercase with the exception of proper nouns, acronyms, and the words that refer to code,\nlike function/variable names")),(0,a.kt)("p",{parentName:"li"},"Examples:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"}," - remove deprecated logger\n - refactor some method\n - add JSDoc on existing function\n")))),(0,a.kt)("h2",{id:"coding-guidelines"},"Coding guidelines"),(0,a.kt)("h4",{id:"code-structuring-patterns"},"Code structuring patterns"),(0,a.kt)("p",null,"Avoid having functions which are responsible to do multiple things at the same time. Make sure one function/method does one thing, and does it well. "),(0,a.kt)("h6",{id:"object-oriented-programming"},"Object Oriented Programming"),(0,a.kt)("p",null,"While following ",(0,a.kt)("inlineCode",{parentName:"p"},"Object Oriented Programming")," paradigm, it's important to follow ",(0,a.kt)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/SOLID"},"SOLID")," principles."),(0,a.kt)("h6",{id:"functional-programming"},"Functional Programming"),(0,a.kt)("p",null,"When designing module of the application in ",(0,a.kt)("inlineCode",{parentName:"p"},"Functional Programming")," paradigm, the key to follow ",(0,a.kt)("a",{parentName:"p",href:"https://dev.to/jamesrweb/principles-of-functional-programming-4b7c"},"basic")," principles."),(0,a.kt)("h4",{id:"naming-patterns"},"Naming patterns"),(0,a.kt)("p",null,"Make sure your ",(0,a.kt)("inlineCode",{parentName:"p"},"class/function/variable")," describes exactly what it does. Avoid using shortened words like txt, arr while doing naming decision. As a naming pattern ",(0,a.kt)("inlineCode",{parentName:"p"},"camel case")," should be used."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'\u274c const a = ""\n\u2705 const mockValue = ""\n\n\u274c const a = (txt: string) => console.log(txt)\n\u2705 const logMessage = (message: string) => console.log(message)\n')),(0,a.kt)("h4",{id:"documentation"},"Documentation"),(0,a.kt)("p",null,"Every logical unit (",(0,a.kt)("inlineCode",{parentName:"p"},"Class, function, method"),") should be covered with appropriate documentation. For documenting such, multi-line comment style is used."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"\u274c const a = (message: string) => console.log(message)\n\n\u2705\n/**\n * Logs given `message` to console.\n **/\nconst logMessage = (message: string) => console.log(message)\n")),(0,a.kt)("p",null,"For documenting variable, expression, single line comments can be used after declaration."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'class MockClass {\n // this is a mock field\n \u274c private mockField: string = "mock-field"\n \u2705 private mockField: string = "mock-field" // Single line documenting style is used.\n}\n')),(0,a.kt)("h4",{id:"writing-tests"},"Writing tests"),(0,a.kt)("p",null,"One test file should be responsible for one module. ",(0,a.kt)("inlineCode",{parentName:"p"},"describe")," blocks should be used for module and function/method description. First ",(0,a.kt)("inlineCode",{parentName:"p"},"describe")," should follow ",(0,a.kt)("inlineCode",{parentName:"p"},"resource/module: ")," pattern. Second describe title should follow ",(0,a.kt)("inlineCode",{parentName:"p"},"method(): ")," pattern. Test units can use either ",(0,a.kt)("inlineCode",{parentName:"p"},"test")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"it"),", title should exactly describe behaviour and input argument. Make sure each test case covers one branch."),(0,a.kt)("p",null,"See example: "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"describe('util/args: ', () => {\n describe('parseProcessArgument(): ', () => {\n it('logs help message if property present in env.', () => {\n ...\n })\n })\n})\n")),(0,a.kt)("p",null,(0,a.kt)("em",{parentName:"p"},(0,a.kt)("a",{parentName:"em",href:"/README.md#ief"},"\u2b05\ufe0f back to the root"))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/425ff8ea.44661c58.js b/assets/js/425ff8ea.44661c58.js deleted file mode 100644 index 7cc1121a..00000000 --- a/assets/js/425ff8ea.44661c58.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[681],{4137:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>u});var r=a(7294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function o(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,r)}return a}function i(e){for(var t=1;t=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var p=r.createContext({}),l=function(e){var t=r.useContext(p),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},c=function(e){var t=l(e.components);return r.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var a=e.components,n=e.mdxType,o=e.originalType,p=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=l(a),u=n,h=d["".concat(p,".").concat(u)]||d[u]||m[u]||o;return a?r.createElement(h,i(i({ref:t},c),{},{components:a})):r.createElement(h,i({ref:t},c))}));function u(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var o=a.length,i=new Array(o);i[0]=d;var s={};for(var p in t)hasOwnProperty.call(t,p)&&(s[p]=t[p]);s.originalType=e,s.mdxType="string"==typeof e?e:n,i[1]=s;for(var l=2;l{a.r(t),a.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>m,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var r=a(7462),n=(a(7294),a(4137));const o={"sidebar-position":8},i="Parameters",s={unversionedId:"major-concepts/parameters",id:"major-concepts/parameters",title:"Parameters",description:"Parameters are handled carefully in IF because of the risk of errors propagating through plugin pipelines. We have a set of canonical parameters that are stored in a file called params.ts in the IF repository.",source:"@site/docs/major-concepts/parameters.md",sourceDirName:"major-concepts",slug:"/major-concepts/parameters",permalink:"/major-concepts/parameters",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/major-concepts/parameters.md",tags:[],version:"current",frontMatter:{"sidebar-position":8},sidebar:"tutorialSidebar",previous:{title:"Manifest File",permalink:"/major-concepts/manifest-file"},next:{title:"Plugins",permalink:"/major-concepts/plugins"}},p={},l=[{value:"Adding parameters",id:"adding-parameters",level:2},{value:"Overriding parameters",id:"overriding-parameters",level:2}],c={toc:l};function m(e){let{components:t,...a}=e;return(0,n.kt)("wrapper",(0,r.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"parameters"},"Parameters"),(0,n.kt)("p",null,"Parameters are handled carefully in IF because of the risk of errors propagating through plugin pipelines. We have a set of canonical parameters that are stored in a file called ",(0,n.kt)("inlineCode",{parentName:"p"},"params.ts")," in the IF repository. "),(0,n.kt)("p",null,"Each entry in ",(0,n.kt)("inlineCode",{parentName:"p"},"params.ts")," has the following structure:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"name: {\ndescription: \nunit: \naggregation:\n}\n")),(0,n.kt)("p",null,"The ",(0,n.kt)("inlineCode",{parentName:"p"},"name")," is the accepted name for a particular parameter. For example, IF has a parameter, ",(0,n.kt)("inlineCode",{parentName:"p"},"carbon")," that represents carbon in gCO2eq. Anywhere ",(0,n.kt)("inlineCode",{parentName:"p"},"carbon")," exists as a parameter anywhere across the IF and its plugins, the name ",(0,n.kt)("inlineCode",{parentName:"p"},"carbon")," is reserved for carbon with this specific unit. "),(0,n.kt)("p",null,"The ",(0,n.kt)("inlineCode",{parentName:"p"},"description")," is a short sentence describing the parameter. "),(0,n.kt)("p",null,"The ",(0,n.kt)("inlineCode",{parentName:"p"},"unit")," is the unit the parameter is expected to be expressed in. Prescribing this here is important because it allows everyone to know what units to expect from a plugin that returns this parameter, protecting against unit errors in pipelines."),(0,n.kt)("p",null,"The ",(0,n.kt)("inlineCode",{parentName:"p"},"aggregation")," parameter can accept ",(0,n.kt)("inlineCode",{parentName:"p"},"sum"),", ",(0,n.kt)("inlineCode",{parentName:"p"},"avg")," or ",(0,n.kt)("inlineCode",{parentName:"p"},"none"),". This is the method IF should use to aggregate values over time or across a tree. Some values should be summed to aggregate them over time, such as ",(0,n.kt)("inlineCode",{parentName:"p"},"carbon")," or ",(0,n.kt)("inlineCode",{parentName:"p"},"energy")," - it makes sense to add these up as they are absolute values that can be added up over time or across components. However, it does not make sense to aggregate metrics expressed as percentages or proportions - these cases are better aggregated by averaging their values. Some other values should not be manipulated when they are aggregated and instead they are treated as constants that should simply be copied into the aggregate, such as names."),(0,n.kt)("h2",{id:"adding-parameters"},"Adding parameters"),(0,n.kt)("p",null,"The canonical set of parameters protects users against introducing unit errors into plugin pipelines by asserting the units and aggregation methods associated with a specific named parameter. However, this also means that if you create a new plugin that deals with a parameter not currently available in ",(0,n.kt)("inlineCode",{parentName:"p"},"params.ts"),", IF with throw an error. To handle this, you can add your plugin's new parameters to a block of config in the manifest file, and they will be added to the canonical set of parameters at runtime. This effectively gives you read-only access to ",(0,n.kt)("inlineCode",{parentName:"p"},"params.ts")," via the manifest file."),(0,n.kt)("p",null,"Here's an example of the config block that could be added to a manifest to enable a plugin that uses the ",(0,n.kt)("inlineCode",{parentName:"p"},"dummy")," parameter:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-yaml"},"params:\n - name: \n description:\n unit:\n aggregaton:\n")),(0,n.kt)("p",null,"Later, you could raise a pull request to IF to add your parameters to the canonical set in ",(0,n.kt)("inlineCode",{parentName:"p"},"params.ts"),", where it would be discussed and agreed among the community. Eventually, we would like to move the governance of ",(0,n.kt)("inlineCode",{parentName:"p"},"params.ts")," to a separate repository with independent custodians and a dedicated discussion forum."),(0,n.kt)("h2",{id:"overriding-parameters"},"Overriding parameters"),(0,n.kt)("p",null,"If you want to, you can also reject our canonical set of parameters and use your own instead. We do not recommend this, but we do make it possible via a CLI command, ",(0,n.kt)("inlineCode",{parentName:"p"},"override-params"),". In this case, you can provide the path to a new file, structured the same way as ",(0,n.kt)("inlineCode",{parentName:"p"},"params.ts")," that will be used instead of ours. For example:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest --override-params my-params.ts\n")),(0,n.kt)("p",null,"The file must parse as JSON or a Javascript object."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/425ff8ea.c1d1c789.js b/assets/js/425ff8ea.c1d1c789.js new file mode 100644 index 00000000..b3801269 --- /dev/null +++ b/assets/js/425ff8ea.c1d1c789.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[681],{4137:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>u});var r=a(7294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function o(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,r)}return a}function i(e){for(var t=1;t=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var p=r.createContext({}),l=function(e){var t=r.useContext(p),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},c=function(e){var t=l(e.components);return r.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var a=e.components,n=e.mdxType,o=e.originalType,p=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=l(a),u=n,h=d["".concat(p,".").concat(u)]||d[u]||m[u]||o;return a?r.createElement(h,i(i({ref:t},c),{},{components:a})):r.createElement(h,i({ref:t},c))}));function u(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var o=a.length,i=new Array(o);i[0]=d;var s={};for(var p in t)hasOwnProperty.call(t,p)&&(s[p]=t[p]);s.originalType=e,s.mdxType="string"==typeof e?e:n,i[1]=s;for(var l=2;l{a.r(t),a.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>m,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var r=a(7462),n=(a(7294),a(4137));const o={"sidebar-position":8},i="Parameters",s={unversionedId:"major-concepts/parameters",id:"major-concepts/parameters",title:"Parameters",description:"Parameters are handled carefully in IF because of the risk of errors propagating through plugin pipelines. We have a set of canonical parameters that are stored in a file called params.ts in the IF repository.",source:"@site/docs/major-concepts/parameters.md",sourceDirName:"major-concepts",slug:"/major-concepts/parameters",permalink:"/major-concepts/parameters",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/parameters.md",tags:[],version:"current",frontMatter:{"sidebar-position":8},sidebar:"tutorialSidebar",previous:{title:"Manifest File",permalink:"/major-concepts/manifest-file"},next:{title:"Plugins",permalink:"/major-concepts/plugins"}},p={},l=[{value:"Adding parameters",id:"adding-parameters",level:2},{value:"Overriding parameters",id:"overriding-parameters",level:2}],c={toc:l};function m(e){let{components:t,...a}=e;return(0,n.kt)("wrapper",(0,r.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"parameters"},"Parameters"),(0,n.kt)("p",null,"Parameters are handled carefully in IF because of the risk of errors propagating through plugin pipelines. We have a set of canonical parameters that are stored in a file called ",(0,n.kt)("inlineCode",{parentName:"p"},"params.ts")," in the IF repository. "),(0,n.kt)("p",null,"Each entry in ",(0,n.kt)("inlineCode",{parentName:"p"},"params.ts")," has the following structure:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"name: {\ndescription: \nunit: \naggregation:\n}\n")),(0,n.kt)("p",null,"The ",(0,n.kt)("inlineCode",{parentName:"p"},"name")," is the accepted name for a particular parameter. For example, IF has a parameter, ",(0,n.kt)("inlineCode",{parentName:"p"},"carbon")," that represents carbon in gCO2eq. Anywhere ",(0,n.kt)("inlineCode",{parentName:"p"},"carbon")," exists as a parameter anywhere across the IF and its plugins, the name ",(0,n.kt)("inlineCode",{parentName:"p"},"carbon")," is reserved for carbon with this specific unit. "),(0,n.kt)("p",null,"The ",(0,n.kt)("inlineCode",{parentName:"p"},"description")," is a short sentence describing the parameter. "),(0,n.kt)("p",null,"The ",(0,n.kt)("inlineCode",{parentName:"p"},"unit")," is the unit the parameter is expected to be expressed in. Prescribing this here is important because it allows everyone to know what units to expect from a plugin that returns this parameter, protecting against unit errors in pipelines."),(0,n.kt)("p",null,"The ",(0,n.kt)("inlineCode",{parentName:"p"},"aggregation")," parameter can accept ",(0,n.kt)("inlineCode",{parentName:"p"},"sum"),", ",(0,n.kt)("inlineCode",{parentName:"p"},"avg")," or ",(0,n.kt)("inlineCode",{parentName:"p"},"none"),". This is the method IF should use to aggregate values over time or across a tree. Some values should be summed to aggregate them over time, such as ",(0,n.kt)("inlineCode",{parentName:"p"},"carbon")," or ",(0,n.kt)("inlineCode",{parentName:"p"},"energy")," - it makes sense to add these up as they are absolute values that can be added up over time or across components. However, it does not make sense to aggregate metrics expressed as percentages or proportions - these cases are better aggregated by averaging their values. Some other values should not be manipulated when they are aggregated and instead they are treated as constants that should simply be copied into the aggregate, such as names."),(0,n.kt)("h2",{id:"adding-parameters"},"Adding parameters"),(0,n.kt)("p",null,"The canonical set of parameters protects users against introducing unit errors into plugin pipelines by asserting the units and aggregation methods associated with a specific named parameter. However, this also means that if you create a new plugin that deals with a parameter not currently available in ",(0,n.kt)("inlineCode",{parentName:"p"},"params.ts"),", IF with throw an error. To handle this, you can add your plugin's new parameters to a block of config in the manifest file, and they will be added to the canonical set of parameters at runtime. This effectively gives you read-only access to ",(0,n.kt)("inlineCode",{parentName:"p"},"params.ts")," via the manifest file."),(0,n.kt)("p",null,"Here's an example of the config block that could be added to a manifest to enable a plugin that uses the ",(0,n.kt)("inlineCode",{parentName:"p"},"dummy")," parameter:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-yaml"},"params:\n - name: \n description:\n unit:\n aggregaton:\n")),(0,n.kt)("p",null,"Later, you could raise a pull request to IF to add your parameters to the canonical set in ",(0,n.kt)("inlineCode",{parentName:"p"},"params.ts"),", where it would be discussed and agreed among the community. Eventually, we would like to move the governance of ",(0,n.kt)("inlineCode",{parentName:"p"},"params.ts")," to a separate repository with independent custodians and a dedicated discussion forum."),(0,n.kt)("h2",{id:"overriding-parameters"},"Overriding parameters"),(0,n.kt)("p",null,"If you want to, you can also reject our canonical set of parameters and use your own instead. We do not recommend this, but we do make it possible via a CLI command, ",(0,n.kt)("inlineCode",{parentName:"p"},"override-params"),". In this case, you can provide the path to a new file, structured the same way as ",(0,n.kt)("inlineCode",{parentName:"p"},"params.ts")," that will be used instead of ours. For example:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest --override-params my-params.ts\n")),(0,n.kt)("p",null,"The file must parse as JSON or a Javascript object."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/449bc0d9.4592c902.js b/assets/js/449bc0d9.27b81fa6.js similarity index 61% rename from assets/js/449bc0d9.4592c902.js rename to assets/js/449bc0d9.27b81fa6.js index 0507dcd8..6471684f 100644 --- a/assets/js/449bc0d9.4592c902.js +++ b/assets/js/449bc0d9.27b81fa6.js @@ -1 +1 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[99],{4137:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>f});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var u=n.createContext({}),l=function(e){var t=n.useContext(u),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},p=function(e){var t=l(e.components);return n.createElement(u.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,u=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),m=l(r),f=o,d=m["".concat(u,".").concat(f)]||m[f]||c[f]||a;return r?n.createElement(d,i(i({ref:t},p),{},{components:r})):n.createElement(d,i({ref:t},p))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=m;var s={};for(var u in t)hasOwnProperty.call(t,u)&&(s[u]=t[u]);s.originalType=e,s.mdxType="string"==typeof e?e:o,i[1]=s;for(var l=2;l{r.r(t),r.d(t,{assets:()=>u,contentTitle:()=>i,default:()=>c,frontMatter:()=>a,metadata:()=>s,toc:()=>l});var n=r(7462),o=(r(7294),r(4137));const a={sidebar_position:3},i="Users",s={unversionedId:"users/index",id:"users/index",title:"Users",description:"This section contains information for Impact Framework users. You are a user if you want to apply the Impact Framework to your own use-case, such as using it to measure the environmental impact of your own apps running on some cloud platform.",source:"@site/docs/users/index.md",sourceDirName:"users",slug:"/users/",permalink:"/users/",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/users/index.md",tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"Time",permalink:"/major-concepts/time"},next:{title:"Quick start",permalink:"/users/quick-start"}},u={},l=[],p={toc:l};function c(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"users"},"Users"),(0,o.kt)("p",null,"This section contains information for Impact Framework users. You are a user if you want to apply the Impact Framework to your own use-case, such as using it to measure the environmental impact of your own apps running on some cloud platform. "),(0,o.kt)("p",null,"The user documentation includes:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/users/quick-start"},"Quick start guide")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/users/how-to-install-if"},"How to install and run IF")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/users/how-to-import-plugins"},"How to load plugins")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/users/how-to-write-manifests"},"How to write manifest files")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/users/how-to-export-to-csv"},"How to export to CSV files"))),(0,o.kt)("p",null,"If you are looking for guidance for how to ",(0,o.kt)("em",{parentName:"p"},"change or update")," the Impact Framework by adding new features, fixing bugs or building new plugins, you should go to our ",(0,o.kt)("a",{parentName:"p",href:"../developers/"},(0,o.kt)("inlineCode",{parentName:"a"},"developers")," documentation")," instead."))}c.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[99],{4137:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>f});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var u=n.createContext({}),l=function(e){var t=n.useContext(u),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},p=function(e){var t=l(e.components);return n.createElement(u.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,u=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),m=l(r),f=o,d=m["".concat(u,".").concat(f)]||m[f]||c[f]||a;return r?n.createElement(d,i(i({ref:t},p),{},{components:r})):n.createElement(d,i({ref:t},p))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=m;var s={};for(var u in t)hasOwnProperty.call(t,u)&&(s[u]=t[u]);s.originalType=e,s.mdxType="string"==typeof e?e:o,i[1]=s;for(var l=2;l{r.r(t),r.d(t,{assets:()=>u,contentTitle:()=>i,default:()=>c,frontMatter:()=>a,metadata:()=>s,toc:()=>l});var n=r(7462),o=(r(7294),r(4137));const a={sidebar_position:3},i="Users",s={unversionedId:"users/index",id:"users/index",title:"Users",description:"This section contains information for Impact Framework users. You are a user if you want to apply the Impact Framework to your own use-case, such as using it to measure the environmental impact of your own apps running on some cloud platform.",source:"@site/docs/users/index.md",sourceDirName:"users",slug:"/users/",permalink:"/users/",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/index.md",tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"Time",permalink:"/major-concepts/time"},next:{title:"Quick start",permalink:"/users/quick-start"}},u={},l=[],p={toc:l};function c(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"users"},"Users"),(0,o.kt)("p",null,"This section contains information for Impact Framework users. You are a user if you want to apply the Impact Framework to your own use-case, such as using it to measure the environmental impact of your own apps running on some cloud platform. "),(0,o.kt)("p",null,"The user documentation includes:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/users/quick-start"},"Quick start guide")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/users/how-to-install-if"},"How to install and run IF")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/users/how-to-import-plugins"},"How to load plugins")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/users/how-to-write-manifests"},"How to write manifest files")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/users/how-to-export-to-csv"},"How to export to CSV files"))),(0,o.kt)("p",null,"If you are looking for guidance for how to ",(0,o.kt)("em",{parentName:"p"},"change or update")," the Impact Framework by adding new features, fixing bugs or building new plugins, you should go to our ",(0,o.kt)("a",{parentName:"p",href:"../developers/"},(0,o.kt)("inlineCode",{parentName:"a"},"developers")," documentation")," instead."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/486bc80e.4b12c136.js b/assets/js/486bc80e.4b12c136.js deleted file mode 100644 index 5c471b75..00000000 --- a/assets/js/486bc80e.4b12c136.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[162],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var a=n(7294);function l(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(l[n]=e[n]);return l}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(l[n]=e[n])}return l}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,l=e.mdxType,r=e.originalType,s=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),m=p(n),f=l,g=m["".concat(s,".").concat(f)]||m[f]||c[f]||r;return n?a.createElement(g,o(o({ref:t},u),{},{components:n})):a.createElement(g,o({ref:t},u))}));function f(e,t){var n=arguments,l=t&&t.mdxType;if("string"==typeof e||l){var r=n.length,o=new Array(r);o[0]=m;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i.mdxType="string"==typeof e?e:l,o[1]=i;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>i,toc:()=>p});var a=n(7462),l=(n(7294),n(4137));const r={sidebar_position:2},o="How to install Impact Framework",i={unversionedId:"users/how-to-install-if",id:"users/how-to-install-if",title:"How to install Impact Framework",description:"You can install Impact Framework either globally or locally. For most users, we recommend installing our official releases globally using npm. You can do this using the following command:",source:"@site/docs/users/how-to-install-if.md",sourceDirName:"users",slug:"/users/how-to-install-if",permalink:"/users/how-to-install-if",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/users/how-to-install-if.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Quick start",permalink:"/users/quick-start"},next:{title:"How to load plugins",permalink:"/users/how-to-import-plugins"}},s={},p=[{value:"Installing locally",id:"installing-locally",level:2}],u={toc:p};function c(e){let{components:t,...n}=e;return(0,l.kt)("wrapper",(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,l.kt)("h1",{id:"how-to-install-impact-framework"},"How to install Impact Framework"),(0,l.kt)("p",null,"You can install Impact Framework either globally or locally. For most users, we recommend installing our official releases globally using ",(0,l.kt)("inlineCode",{parentName:"p"},"npm"),". You can do this using the following command:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"npm install -g @grnsft/if\n")),(0,l.kt)("p",null,"Then, run the package using the ",(0,l.kt)("inlineCode",{parentName:"p"},"ie")," command:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest \n")),(0,l.kt)("p",null,"There is only one plugin that is built in to the core Impact Framework codebase (",(0,l.kt)("inlineCode",{parentName:"p"},"time-sync"),"). If you only want to use Impact Framework with your own, locally-developed plugins or plugins loaded from remote Github repositories, then you have already installed everything you need to get started. However, most likely you will want to install some of our plugins too."),(0,l.kt)("p",null,"There are two plugin packages you will probably want to install: ",(0,l.kt)("inlineCode",{parentName:"p"},"if-plugins")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"if-unofficial-plugins"),". "),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"if-plugins")," is our standard library of plugins that we maintain. Generally, these are used for calculating a Software Carbon Intensity score."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"if-unofficial-plugins")," is a collection of plugins that rely on some third party API or that reimplement a pre-existing plugin originally developed by another group. For example, our Azure data importer and Co2js.")),(0,l.kt)("p",null,"You can install the latest releases of these plugin packages using npm:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"npm install -g @grnsft/if-plugins\nnpm install -g @grnsft/if-unofficial-plugins\n")),(0,l.kt)("p",null,"Now you have globally installed the framework and a set of plugins and can start using IF for calculating the impact of your applications."),(0,l.kt)("h2",{id:"installing-locally"},"Installing locally"),(0,l.kt)("p",null,"You can also clone the Impact Framework repositories and install them locally, useful for developers who want to make changes or build new plugins. Use the following command for local installation:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"git clone https://github.com/Green-Software-Foundation/if && cd if\nnpm install\n")),(0,l.kt)("p",null,"Then, use the following command to run Impact Framework:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"npm run ie -- --manifest \n")),(0,l.kt)("p",null,"Next, install local plugin repositories using ",(0,l.kt)("inlineCode",{parentName:"p"},"npm link"),". You can do this by entering the plugin folder and running the following command:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"npm link\n")),(0,l.kt)("p",null,"This creates a global package with the same name as your project root directory which you can then load by passing the path in your manifest file."),(0,l.kt)("p",null,"Read our detailed guide to ",(0,l.kt)("a",{parentName:"p",href:"/users/how-to-import-plugins"},"installing plugins"),"."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/486bc80e.7898ccbb.js b/assets/js/486bc80e.7898ccbb.js new file mode 100644 index 00000000..4244bd27 --- /dev/null +++ b/assets/js/486bc80e.7898ccbb.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[162],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var a=n(7294);function l(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(l[n]=e[n]);return l}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(l[n]=e[n])}return l}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,l=e.mdxType,r=e.originalType,s=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),m=p(n),f=l,g=m["".concat(s,".").concat(f)]||m[f]||c[f]||r;return n?a.createElement(g,o(o({ref:t},u),{},{components:n})):a.createElement(g,o({ref:t},u))}));function f(e,t){var n=arguments,l=t&&t.mdxType;if("string"==typeof e||l){var r=n.length,o=new Array(r);o[0]=m;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i.mdxType="string"==typeof e?e:l,o[1]=i;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>i,toc:()=>p});var a=n(7462),l=(n(7294),n(4137));const r={sidebar_position:2},o="How to install Impact Framework",i={unversionedId:"users/how-to-install-if",id:"users/how-to-install-if",title:"How to install Impact Framework",description:"You can install Impact Framework either globally or locally. For most users, we recommend installing our official releases globally using npm. You can do this using the following command:",source:"@site/docs/users/how-to-install-if.md",sourceDirName:"users",slug:"/users/how-to-install-if",permalink:"/users/how-to-install-if",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-install-if.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Quick start",permalink:"/users/quick-start"},next:{title:"How to load plugins",permalink:"/users/how-to-import-plugins"}},s={},p=[{value:"Installing locally",id:"installing-locally",level:2}],u={toc:p};function c(e){let{components:t,...n}=e;return(0,l.kt)("wrapper",(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,l.kt)("h1",{id:"how-to-install-impact-framework"},"How to install Impact Framework"),(0,l.kt)("p",null,"You can install Impact Framework either globally or locally. For most users, we recommend installing our official releases globally using ",(0,l.kt)("inlineCode",{parentName:"p"},"npm"),". You can do this using the following command:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"npm install -g @grnsft/if\n")),(0,l.kt)("p",null,"Then, run the package using the ",(0,l.kt)("inlineCode",{parentName:"p"},"ie")," command:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest \n")),(0,l.kt)("p",null,"There is only one plugin that is built in to the core Impact Framework codebase (",(0,l.kt)("inlineCode",{parentName:"p"},"time-sync"),"). If you only want to use Impact Framework with your own, locally-developed plugins or plugins loaded from remote Github repositories, then you have already installed everything you need to get started. However, most likely you will want to install some of our plugins too."),(0,l.kt)("p",null,"There are two plugin packages you will probably want to install: ",(0,l.kt)("inlineCode",{parentName:"p"},"if-plugins")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"if-unofficial-plugins"),". "),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"if-plugins")," is our standard library of plugins that we maintain. Generally, these are used for calculating a Software Carbon Intensity score."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"if-unofficial-plugins")," is a collection of plugins that rely on some third party API or that reimplement a pre-existing plugin originally developed by another group. For example, our Azure data importer and Co2js.")),(0,l.kt)("p",null,"You can install the latest releases of these plugin packages using npm:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"npm install -g @grnsft/if-plugins\nnpm install -g @grnsft/if-unofficial-plugins\n")),(0,l.kt)("p",null,"Now you have globally installed the framework and a set of plugins and can start using IF for calculating the impact of your applications."),(0,l.kt)("h2",{id:"installing-locally"},"Installing locally"),(0,l.kt)("p",null,"You can also clone the Impact Framework repositories and install them locally, useful for developers who want to make changes or build new plugins. Use the following command for local installation:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"git clone https://github.com/Green-Software-Foundation/if && cd if\nnpm install\n")),(0,l.kt)("p",null,"Then, use the following command to run Impact Framework:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"npm run ie -- --manifest \n")),(0,l.kt)("p",null,"Next, install local plugin repositories using ",(0,l.kt)("inlineCode",{parentName:"p"},"npm link"),". You can do this by entering the plugin folder and running the following command:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"npm link\n")),(0,l.kt)("p",null,"This creates a global package with the same name as your project root directory which you can then load by passing the path in your manifest file."),(0,l.kt)("p",null,"Read our detailed guide to ",(0,l.kt)("a",{parentName:"p",href:"/users/how-to-import-plugins"},"installing plugins"),"."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/5c5a410f.5c2cc41d.js b/assets/js/5c5a410f.5c2cc41d.js new file mode 100644 index 00000000..a37751a4 --- /dev/null +++ b/assets/js/5c5a410f.5c2cc41d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[468],{4137:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>d});var a=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function l(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var p=a.createContext({}),s=function(e){var n=a.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):l(l({},n),e)),t},u=function(e){var n=s(e.components);return a.createElement(p.Provider,{value:n},e.children)},m={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},c=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,p=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),c=s(t),d=i,f=c["".concat(p,".").concat(d)]||c[d]||m[d]||r;return t?a.createElement(f,l(l({ref:n},u),{},{components:t})):a.createElement(f,l({ref:n},u))}));function d(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,l=new Array(r);l[0]=c;var o={};for(var p in n)hasOwnProperty.call(n,p)&&(o[p]=n[p]);o.originalType=e,o.mdxType="string"==typeof e?e:i,l[1]=o;for(var s=2;s{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>l,default:()=>m,frontMatter:()=>r,metadata:()=>o,toc:()=>s});var a=t(7462),i=(t(7294),t(4137));const r={"sidebar-position":2},l="Manifest File",o={unversionedId:"major-concepts/manifest-file",id:"major-concepts/manifest-file",title:"Manifest File",description:"Manifest files are fundamental to Impact Framework and they serve multiple important purposes, including:",source:"@site/docs/major-concepts/manifest-file.md",sourceDirName:"major-concepts",slug:"/major-concepts/manifest-file",permalink:"/major-concepts/manifest-file",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/manifest-file.md",tags:[],version:"current",frontMatter:{"sidebar-position":2},sidebar:"tutorialSidebar",previous:{title:"Impact Engine (CLI)",permalink:"/major-concepts/if"},next:{title:"Parameters",permalink:"/major-concepts/parameters"}},p={},s=[{value:"Structure of a manifest file",id:"structure-of-a-manifest-file",level:2},{value:"Overview",id:"overview",level:3},{value:"Global metadata",id:"global-metadata",level:3},{value:"Plugin initialization",id:"plugin-initialization",level:3},{value:"Tree",id:"tree",level:3},{value:"Inputs",id:"inputs",level:3},{value:"Computing a manifest file",id:"computing-a-manifest-file",level:2},{value:"Outputs",id:"outputs",level:2}],u={toc:s};function m(e){let{components:n,...r}=e;return(0,i.kt)("wrapper",(0,a.Z)({},u,r,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"manifest-file"},"Manifest File"),(0,i.kt)("p",null,"Manifest files are fundamental to Impact Framework and they serve multiple important purposes, including:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"They contain all the necessary configurations for Impact Framework"),(0,i.kt)("li",{parentName:"ul"},"They define your application architecture"),(0,i.kt)("li",{parentName:"ul"},"They hold your input data"),(0,i.kt)("li",{parentName:"ul"},"They are shareable, portable and human-readable"),(0,i.kt)("li",{parentName:"ul"},"They can be used as verifiable audits form your application")),(0,i.kt)("p",null,"The manifest is a ",(0,i.kt)("a",{parentName:"p",href:"https://circleci.com/blog/what-is-yaml-a-beginner-s-guide/"},"yaml")," file with a particular structure.\nIt can be thought of as an ",(0,i.kt)("strong",{parentName:"p"},(0,i.kt)("em",{parentName:"strong"},"executable audit"))," because the file itself can be shared with others and re-executed to verify your environmental impact calculations. "),(0,i.kt)("p",null,"It is a formal report detailing not just the end impact but all the assumptions, inputs, and plugins used in calculating the impact."),(0,i.kt)("p",null,"This is possible because ",(0,i.kt)("em",{parentName:"p"},"all the configuration and data required to run Impact Framework is contained in the manifest file"),". "),(0,i.kt)("p",null,"Anyone can download Impact Framework and execute a manifest file to verify the results. "),(0,i.kt)("h2",{id:"structure-of-a-manifest-file"},"Structure of a manifest file"),(0,i.kt)("h3",{id:"overview"},"Overview"),(0,i.kt)("p",null,"Manifest files can be simple or very intricate, depending on the plugin pipeline you want to use and the complexity of your application. However, all manifest files conform to a basic structure that looks as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name:\ndescription:\ntags:\ninitialize:\n plugins:\n : \n method: \n path: \n outputs:\ntree:\n children:\n child:\n pipeline:\n config:\n defaults:\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n")),(0,i.kt)("h3",{id:"global-metadata"},"Global metadata"),(0,i.kt)("p",null,"The global metadata includes the ",(0,i.kt)("inlineCode",{parentName:"p"},"name"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"description"),", and ",(0,i.kt)("inlineCode",{parentName:"p"},"tags")," that can be used to describe the nature of the manifest file. For example, you might name the file ",(0,i.kt)("inlineCode",{parentName:"p"},"Carbon Jan 2024")," or similar. A short description might briefly outline the scope of the manifest file, e.g. ",(0,i.kt)("inlineCode",{parentName:"p"},"company x's carbon emissions due to web serves from Jab 24 - July 24"),". Tags can be used to group manifest files (we do not explicitly use this field for anything currently)."),(0,i.kt)("h3",{id:"plugin-initialization"},"Plugin initialization"),(0,i.kt)("p",null,"The initialize section is where you define which plugins will be used in your manifest file and provide the global configuration for them. Below is sample for initialization: "),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n :\n method: \n outputs: ['csv', 'yaml', 'log']\n")),(0,i.kt)("p",null,"Where required values are:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"method"),": the name of the function exported by the plugin."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"path"),": the path to the plugin code. For example, for a plugin from our standard library installed from npm, this value would be ",(0,i.kt)("inlineCode",{parentName:"li"},"@grnsft/if-plugins"))),(0,i.kt)("p",null,"There is also an optional ",(0,i.kt)("inlineCode",{parentName:"p"},"global-config")," field that can be used to set ",(0,i.kt)("em",{parentName:"p"},"global")," configuration that is common to a plugin wherever it is invoked across the entire manifest file."),(0,i.kt)("p",null,"Impact Framework uses the ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," section to instantiate each plugin. A plugin cannot be invoked elsewhere in the manifest file unless it is included in this section."),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"outputs")," is a list of possible export types (currently ",(0,i.kt)("inlineCode",{parentName:"p"},"csv"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"yaml"),", and ",(0,i.kt)("inlineCode",{parentName:"p"},"log")," are supported)."),(0,i.kt)("h3",{id:"tree"},"Tree"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"tree")," section of a manifest file defines the topology of all the components being measured. The shape of the ",(0,i.kt)("inlineCode",{parentName:"p"},"tree")," defines the grouping of components. It describes the architecture of the application being studied and contains all the usage observations for each component. The tree has individual components such as leaves, intermediate nodes representing groupings, and the top level is the root."),(0,i.kt)("p",null,(0,i.kt)("img",{src:t(403).Z,width:"614",height:"569"})),(0,i.kt)("p",null,"For example, a web application could be organized as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"tree:\n children:\n front-end:\n children:\n build-pipeline:\n children:\n vercel:\n github-pages:\n backend-database:\n children:\n server1:\n server2:\n server3:\n front-end:\n networking:\n")),(0,i.kt)("p",null,"This example has a relatively straightforward structure with a maximum of 3 levels of nesting. You can continue to nest components to any depth."),(0,i.kt)("p",null,"Each component has some configuration, some input data, and a plugin pipeline."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"pipeline"),": a list of plugins that should be executed for a specific component"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"config"),": contains configuration for each plugin that applies just inside this specific component."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"defaults"),": fallback values that IF defaults to if they are not present in an input observation."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"inputs"),": an array of ",(0,i.kt)("inlineCode",{parentName:"li"},"observation")," data, with each ",(0,i.kt)("inlineCode",{parentName:"li"},"observation")," containing usage data for a given timestep.")),(0,i.kt)("p",null,"If a component ",(0,i.kt)("em",{parentName:"p"},"does not")," include its own ",(0,i.kt)("inlineCode",{parentName:"p"},"pipeline"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"config"),", or ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," values, they are inherited from the closest parent."),(0,i.kt)("p",null,"Here's an example of a moderately complex tree:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child-0:\n pipeline:\n - sci-e\n children:\n child-0-1:\n pipeline:\n - sci-e\n config: null\n defaults: null\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n energy: 0.000811\n child-0-2:\n children:\n child-0-2-1:\n pipeline:\n - sci-e\n config: null\n defaults: null\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n energy: 0.000811\n")),(0,i.kt)("h3",{id:"inputs"},"Inputs"),(0,i.kt)("p",null,"Every component includes an ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," field that gets read into plugins as an array. ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," are divided into ",(0,i.kt)("inlineCode",{parentName:"p"},"observations"),", each having a ",(0,i.kt)("inlineCode",{parentName:"p"},"timestamp")," and a ",(0,i.kt)("inlineCode",{parentName:"p"},"duration"),". Every ",(0,i.kt)("inlineCode",{parentName:"p"},"observation")," refers to an element in ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," representing some snapshot in time."),(0,i.kt)("p",null,"Each plugin takes the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array and applies some calculation or transformation to each ",(0,i.kt)("inlineCode",{parentName:"p"},"observation")," in the array."),(0,i.kt)("p",null,"Observations can include any type of data, including human judgment, assumptions, other plugins, APIs, survey data or telemetry."),(0,i.kt)("p",null,"The separation of timestamps in the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array determines the temporal granularity of your impact calculations. The more frequent your observations, the more accurate your impact assessment."),(0,i.kt)("h2",{id:"computing-a-manifest-file"},"Computing a manifest file"),(0,i.kt)("p",null,"Impact Framework computes manifest files. For each component in the tree, the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array is passed to each plugin in the pipeline in sequence. "),(0,i.kt)("p",null,"Each plugin ",(0,i.kt)("em",{parentName:"p"},"enriches")," the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array in some specific way, typically by adding a new ",(0,i.kt)("inlineCode",{parentName:"p"},"key-value")," pair to each observation in the array. For example, the ",(0,i.kt)("inlineCode",{parentName:"p"},"teads-curve")," plugin takes in CPU utilization expressed as a percentage as an input and appends ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," expressed in kWh. ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," is then available to be passed as an input to, for example, the ",(0,i.kt)("inlineCode",{parentName:"p"},"sci-e")," plugin."),(0,i.kt)("p",null,"This implies a sequence of plugins where the inputs for some plugins must either be present in the original manifest file or be outputs of the preceding plugins in the pipeline."),(0,i.kt)("p",null,"There are also plugins and built-in features that can synchronize time series of ",(0,i.kt)("inlineCode",{parentName:"p"},"observations")," across an entire tree and aggregate data across time or across components."),(0,i.kt)("h2",{id:"outputs"},"Outputs"),(0,i.kt)("p",null,"When Impact Framework computes a manifest file, it appends new data to the manifest file and the final result is an enriched manifest that includes all the configuration and contextual data, the input data, and the results of executing each plugin. This means the output file is completely auditable - the manifest file can be recovered simply by deleting the ",(0,i.kt)("inlineCode",{parentName:"p"},"outputs")," section of the output file."),(0,i.kt)("p",null,"Here's an example output file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},'name: e-mem\ndescription: null\ntags: null\ninitialize:\n plugins:\n e-mem:\n path: "@grnsft/if-plugins"\n method: EMem\ntree:\n children:\n child:\n pipeline:\n - e-mem\n config: null\n defaults:\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n memory/utilization: 40\n memory/capacity: 1\n outputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n mem-util: 40\n memory/capacity: 1\n memory/energy: 0.15200000000000002\n')))}m.isMDXComponent=!0},403:(e,n,t)=>{t.d(n,{Z:()=>a});const a=t.p+"assets/images/3f18767c1a55cee416e3de70314609e3-c7fa9feaf0993c3ed2b5a34b8b82432c.png"}}]); \ No newline at end of file diff --git a/assets/js/5c5a410f.75606c3d.js b/assets/js/5c5a410f.75606c3d.js deleted file mode 100644 index 3dc252f1..00000000 --- a/assets/js/5c5a410f.75606c3d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[468],{4137:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>d});var a=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function l(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var p=a.createContext({}),s=function(e){var n=a.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):l(l({},n),e)),t},u=function(e){var n=s(e.components);return a.createElement(p.Provider,{value:n},e.children)},m={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},c=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,p=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),c=s(t),d=i,f=c["".concat(p,".").concat(d)]||c[d]||m[d]||r;return t?a.createElement(f,l(l({ref:n},u),{},{components:t})):a.createElement(f,l({ref:n},u))}));function d(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,l=new Array(r);l[0]=c;var o={};for(var p in n)hasOwnProperty.call(n,p)&&(o[p]=n[p]);o.originalType=e,o.mdxType="string"==typeof e?e:i,l[1]=o;for(var s=2;s{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>l,default:()=>m,frontMatter:()=>r,metadata:()=>o,toc:()=>s});var a=t(7462),i=(t(7294),t(4137));const r={"sidebar-position":2},l="Manifest File",o={unversionedId:"major-concepts/manifest-file",id:"major-concepts/manifest-file",title:"Manifest File",description:"Manifest files are fundamental to Impact Framework and they serve multiple important purposes, including:",source:"@site/docs/major-concepts/manifest-file.md",sourceDirName:"major-concepts",slug:"/major-concepts/manifest-file",permalink:"/major-concepts/manifest-file",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/major-concepts/manifest-file.md",tags:[],version:"current",frontMatter:{"sidebar-position":2},sidebar:"tutorialSidebar",previous:{title:"Impact Engine (CLI)",permalink:"/major-concepts/if"},next:{title:"Parameters",permalink:"/major-concepts/parameters"}},p={},s=[{value:"Structure of a manifest file",id:"structure-of-a-manifest-file",level:2},{value:"Overview",id:"overview",level:3},{value:"Global metadata",id:"global-metadata",level:3},{value:"Plugin initialization",id:"plugin-initialization",level:3},{value:"Tree",id:"tree",level:3},{value:"Inputs",id:"inputs",level:3},{value:"Computing a manifest file",id:"computing-a-manifest-file",level:2},{value:"Outputs",id:"outputs",level:2}],u={toc:s};function m(e){let{components:n,...r}=e;return(0,i.kt)("wrapper",(0,a.Z)({},u,r,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"manifest-file"},"Manifest File"),(0,i.kt)("p",null,"Manifest files are fundamental to Impact Framework and they serve multiple important purposes, including:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"They contain all the necessary configurations for Impact Framework"),(0,i.kt)("li",{parentName:"ul"},"They define your application architecture"),(0,i.kt)("li",{parentName:"ul"},"They hold your input data"),(0,i.kt)("li",{parentName:"ul"},"They are shareable, portable and human-readable"),(0,i.kt)("li",{parentName:"ul"},"They can be used as verifiable audits form your application")),(0,i.kt)("p",null,"The manifest is a ",(0,i.kt)("a",{parentName:"p",href:"https://circleci.com/blog/what-is-yaml-a-beginner-s-guide/"},"yaml")," file with a particular structure.\nIt can be thought of as an ",(0,i.kt)("strong",{parentName:"p"},(0,i.kt)("em",{parentName:"strong"},"executable audit"))," because the file itself can be shared with others and re-executed to verify your environmental impact calculations. "),(0,i.kt)("p",null,"It is a formal report detailing not just the end impact but all the assumptions, inputs, and plugins used in calculating the impact."),(0,i.kt)("p",null,"This is possible because ",(0,i.kt)("em",{parentName:"p"},"all the configuration and data required to run Impact Framework is contained in the manifest file"),". "),(0,i.kt)("p",null,"Anyone can download Impact Framework and execute a manifest file to verify the results. "),(0,i.kt)("h2",{id:"structure-of-a-manifest-file"},"Structure of a manifest file"),(0,i.kt)("h3",{id:"overview"},"Overview"),(0,i.kt)("p",null,"Manifest files can be simple or very intricate, depending on the plugin pipeline you want to use and the complexity of your application. However, all manifest files conform to a basic structure that looks as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name:\ndescription:\ntags:\ninitialize:\n plugins:\n : \n method: \n path: \n outputs:\ntree:\n children:\n child:\n pipeline:\n config:\n defaults:\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n")),(0,i.kt)("h3",{id:"global-metadata"},"Global metadata"),(0,i.kt)("p",null,"The global metadata includes the ",(0,i.kt)("inlineCode",{parentName:"p"},"name"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"description"),", and ",(0,i.kt)("inlineCode",{parentName:"p"},"tags")," that can be used to describe the nature of the manifest file. For example, you might name the file ",(0,i.kt)("inlineCode",{parentName:"p"},"Carbon Jan 2024")," or similar. A short description might briefly outline the scope of the manifest file, e.g. ",(0,i.kt)("inlineCode",{parentName:"p"},"company x's carbon emissions due to web serves from Jab 24 - July 24"),". Tags can be used to group manifest files (we do not explicitly use this field for anything currently)."),(0,i.kt)("h3",{id:"plugin-initialization"},"Plugin initialization"),(0,i.kt)("p",null,"The initialize section is where you define which plugins will be used in your manifest file and provide the global configuration for them. Below is sample for initialization: "),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n :\n method: \n outputs: ['csv', 'yaml', 'log']\n")),(0,i.kt)("p",null,"Where required values are:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"method"),": the name of the function exported by the plugin."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"path"),": the path to the plugin code. For example, for a plugin from our standard library installed from npm, this value would be ",(0,i.kt)("inlineCode",{parentName:"li"},"@grnsft/if-plugins"))),(0,i.kt)("p",null,"There is also an optional ",(0,i.kt)("inlineCode",{parentName:"p"},"global-config")," field that can be used to set ",(0,i.kt)("em",{parentName:"p"},"global")," configuration that is common to a plugin wherever it is invoked across the entire manifest file."),(0,i.kt)("p",null,"Impact Framework uses the ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," section to instantiate each plugin. A plugin cannot be invoked elsewhere in the manifest file unless it is included in this section."),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"outputs")," is a list of possible export types (currently ",(0,i.kt)("inlineCode",{parentName:"p"},"csv"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"yaml"),", and ",(0,i.kt)("inlineCode",{parentName:"p"},"log")," are supported)."),(0,i.kt)("h3",{id:"tree"},"Tree"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"tree")," section of a manifest file defines the topology of all the components being measured. The shape of the ",(0,i.kt)("inlineCode",{parentName:"p"},"tree")," defines the grouping of components. It describes the architecture of the application being studied and contains all the usage observations for each component. The tree has individual components such as leaves, intermediate nodes representing groupings, and the top level is the root."),(0,i.kt)("p",null,(0,i.kt)("img",{src:t(403).Z,width:"614",height:"569"})),(0,i.kt)("p",null,"For example, a web application could be organized as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"tree:\n children:\n front-end:\n children:\n build-pipeline:\n children:\n vercel:\n github-pages:\n backend-database:\n children:\n server1:\n server2:\n server3:\n front-end:\n networking:\n")),(0,i.kt)("p",null,"This example has a relatively straightforward structure with a maximum of 3 levels of nesting. You can continue to nest components to any depth."),(0,i.kt)("p",null,"Each component has some configuration, some input data, and a plugin pipeline."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"pipeline"),": a list of plugins that should be executed for a specific component"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"config"),": contains configuration for each plugin that applies just inside this specific component."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"defaults"),": fallback values that IF defaults to if they are not present in an input observation."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"inputs"),": an array of ",(0,i.kt)("inlineCode",{parentName:"li"},"observation")," data, with each ",(0,i.kt)("inlineCode",{parentName:"li"},"observation")," containing usage data for a given timestep.")),(0,i.kt)("p",null,"If a component ",(0,i.kt)("em",{parentName:"p"},"does not")," include its own ",(0,i.kt)("inlineCode",{parentName:"p"},"pipeline"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"config"),", or ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," values, they are inherited from the closest parent."),(0,i.kt)("p",null,"Here's an example of a moderately complex tree:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child-0:\n pipeline:\n - sci-e\n children:\n child-0-1:\n pipeline:\n - sci-e\n config: null\n defaults: null\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n energy: 0.000811\n child-0-2:\n children:\n child-0-2-1:\n pipeline:\n - sci-e\n config: null\n defaults: null\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n energy: 0.000811\n")),(0,i.kt)("h3",{id:"inputs"},"Inputs"),(0,i.kt)("p",null,"Every component includes an ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," field that gets read into plugins as an array. ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," are divided into ",(0,i.kt)("inlineCode",{parentName:"p"},"observations"),", each having a ",(0,i.kt)("inlineCode",{parentName:"p"},"timestamp")," and a ",(0,i.kt)("inlineCode",{parentName:"p"},"duration"),". Every ",(0,i.kt)("inlineCode",{parentName:"p"},"observation")," refers to an element in ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," representing some snapshot in time."),(0,i.kt)("p",null,"Each plugin takes the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array and applies some calculation or transformation to each ",(0,i.kt)("inlineCode",{parentName:"p"},"observation")," in the array."),(0,i.kt)("p",null,"Observations can include any type of data, including human judgment, assumptions, other plugins, APIs, survey data or telemetry."),(0,i.kt)("p",null,"The separation of timestamps in the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array determines the temporal granularity of your impact calculations. The more frequent your observations, the more accurate your impact assessment."),(0,i.kt)("h2",{id:"computing-a-manifest-file"},"Computing a manifest file"),(0,i.kt)("p",null,"Impact Framework computes manifest files. For each component in the tree, the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array is passed to each plugin in the pipeline in sequence. "),(0,i.kt)("p",null,"Each plugin ",(0,i.kt)("em",{parentName:"p"},"enriches")," the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array in some specific way, typically by adding a new ",(0,i.kt)("inlineCode",{parentName:"p"},"key-value")," pair to each observation in the array. For example, the ",(0,i.kt)("inlineCode",{parentName:"p"},"teads-curve")," plugin takes in CPU utilization expressed as a percentage as an input and appends ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," expressed in kWh. ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," is then available to be passed as an input to, for example, the ",(0,i.kt)("inlineCode",{parentName:"p"},"sci-e")," plugin."),(0,i.kt)("p",null,"This implies a sequence of plugins where the inputs for some plugins must either be present in the original manifest file or be outputs of the preceding plugins in the pipeline."),(0,i.kt)("p",null,"There are also plugins and built-in features that can synchronize time series of ",(0,i.kt)("inlineCode",{parentName:"p"},"observations")," across an entire tree and aggregate data across time or across components."),(0,i.kt)("h2",{id:"outputs"},"Outputs"),(0,i.kt)("p",null,"When Impact Framework computes a manifest file, it appends new data to the manifest file and the final result is an enriched manifest that includes all the configuration and contextual data, the input data, and the results of executing each plugin. This means the output file is completely auditable - the manifest file can be recovered simply by deleting the ",(0,i.kt)("inlineCode",{parentName:"p"},"outputs")," section of the output file."),(0,i.kt)("p",null,"Here's an example output file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},'name: e-mem\ndescription: null\ntags: null\ninitialize:\n plugins:\n e-mem:\n path: "@grnsft/if-plugins"\n method: EMem\ntree:\n children:\n child:\n pipeline:\n - e-mem\n config: null\n defaults:\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n memory/utilization: 40\n memory/capacity: 1\n outputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n mem-util: 40\n memory/capacity: 1\n memory/energy: 0.15200000000000002\n')))}m.isMDXComponent=!0},403:(e,n,t)=>{t.d(n,{Z:()=>a});const a=t.p+"assets/images/3f18767c1a55cee416e3de70314609e3-c7fa9feaf0993c3ed2b5a34b8b82432c.png"}}]); \ No newline at end of file diff --git a/assets/js/63925da8.78d94b7c.js b/assets/js/63925da8.8499b17f.js similarity index 63% rename from assets/js/63925da8.78d94b7c.js rename to assets/js/63925da8.8499b17f.js index f06e738c..23fcf3ea 100644 --- a/assets/js/63925da8.78d94b7c.js +++ b/assets/js/63925da8.8499b17f.js @@ -1 +1 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[822],{4137:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),s=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},u=function(e){var t=s(e.components);return n.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,p=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=s(r),f=o,m=d["".concat(p,".").concat(f)]||d[f]||c[f]||a;return r?n.createElement(m,i(i({ref:t},u),{},{components:r})):n.createElement(m,i({ref:t},u))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=d;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:o,i[1]=l;for(var s=2;s{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>c,frontMatter:()=>a,metadata:()=>l,toc:()=>s});var n=r(7462),o=(r(7294),r(4137));const a={sidebar_position:4},i="Developers",l={unversionedId:"developers/index",id:"developers/index",title:"Developers",description:"This section contains information for Impact Framework developers. You are a developer if you want to change or update the Impact Framework by adding new features, fixing bugs or building new plugins.",source:"@site/docs/developers/index.md",sourceDirName:"developers",slug:"/developers/",permalink:"/developers/",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/developers/index.md",tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"How to export to CSV",permalink:"/users/how-to-export-to-csv"},next:{title:"How to build plugins",permalink:"/developers/how-to-build-plugins"}},p={},s=[],u={toc:s};function c(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"developers"},"Developers"),(0,o.kt)("p",null,"This section contains information for Impact Framework developers. You are a developer if you want to ",(0,o.kt)("em",{parentName:"p"},"change or update")," the Impact Framework by adding new features, fixing bugs or building new plugins. "),(0,o.kt)("p",null,"The developer documentation includes:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/developers/how-to-build-plugins"},"How to build plugins")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/developers/how-to-refine-plugins"},"How to make plugins production-ready")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"./how-to-test-if.md"},"How to write unit tests")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/developers/how-to-visualize-results"},"How to visualize results"))),(0,o.kt)("p",null,"If you are looking for guidance for how to use IF to measure the environmental impact of your apps, you should go to our ",(0,o.kt)("a",{parentName:"p",href:"../users/"},(0,o.kt)("inlineCode",{parentName:"a"},"user")," documentation")," instead."))}c.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[822],{4137:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),s=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},u=function(e){var t=s(e.components);return n.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,p=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=s(r),f=o,m=d["".concat(p,".").concat(f)]||d[f]||c[f]||a;return r?n.createElement(m,i(i({ref:t},u),{},{components:r})):n.createElement(m,i({ref:t},u))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=d;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:o,i[1]=l;for(var s=2;s{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>c,frontMatter:()=>a,metadata:()=>l,toc:()=>s});var n=r(7462),o=(r(7294),r(4137));const a={sidebar_position:4},i="Developers",l={unversionedId:"developers/index",id:"developers/index",title:"Developers",description:"This section contains information for Impact Framework developers. You are a developer if you want to change or update the Impact Framework by adding new features, fixing bugs or building new plugins.",source:"@site/docs/developers/index.md",sourceDirName:"developers",slug:"/developers/",permalink:"/developers/",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/developers/index.md",tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"How to export to CSV",permalink:"/users/how-to-export-to-csv"},next:{title:"How to build plugins",permalink:"/developers/how-to-build-plugins"}},p={},s=[],u={toc:s};function c(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"developers"},"Developers"),(0,o.kt)("p",null,"This section contains information for Impact Framework developers. You are a developer if you want to ",(0,o.kt)("em",{parentName:"p"},"change or update")," the Impact Framework by adding new features, fixing bugs or building new plugins. "),(0,o.kt)("p",null,"The developer documentation includes:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/developers/how-to-build-plugins"},"How to build plugins")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/developers/how-to-refine-plugins"},"How to make plugins production-ready")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"./how-to-test-if.md"},"How to write unit tests")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/developers/how-to-visualize-results"},"How to visualize results"))),(0,o.kt)("p",null,"If you are looking for guidance for how to use IF to measure the environmental impact of your apps, you should go to our ",(0,o.kt)("a",{parentName:"p",href:"../users/"},(0,o.kt)("inlineCode",{parentName:"a"},"user")," documentation")," instead."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/68ffc9f1.28002050.js b/assets/js/68ffc9f1.28002050.js deleted file mode 100644 index 54888bf6..00000000 --- a/assets/js/68ffc9f1.28002050.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[644],{4137:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>d});var i=t(7294);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function r(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var s=i.createContext({}),p=function(e){var n=i.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},u=function(e){var n=p(e.components);return i.createElement(s.Provider,{value:n},e.children)},c={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},m=i.forwardRef((function(e,n){var t=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=p(t),d=a,h=m["".concat(s,".").concat(d)]||m[d]||c[d]||o;return t?i.createElement(h,r(r({ref:n},u),{},{components:t})):i.createElement(h,r({ref:n},u))}));function d(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var o=t.length,r=new Array(o);r[0]=m;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var i=t(7462),a=(t(7294),t(4137));const o={sidebar_position:4},r="How to write a manifest file",l={unversionedId:"users/how-to-write-manifests",id:"users/how-to-write-manifests",title:"How to write a manifest file",description:"The Impact Framework receives all its configuration and input data in the form of a manifest file known as an manifest. To use the framework, you will need to write a manifest file and pass its path to the command line tool. This guide will help you to understand how to construct one of these files and use it to measure the energy and carbon usage of your app.",source:"@site/docs/users/how-to-write-manifests.md",sourceDirName:"users",slug:"/users/how-to-write-manifests",permalink:"/users/how-to-write-manifests",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/users/how-to-write-manifests.md",tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"How to load plugins",permalink:"/users/how-to-import-plugins"},next:{title:"How to export to CSV",permalink:"/users/how-to-export-to-csv"}},s={},p=[{value:"Structure of a manifest",id:"structure-of-a-manifest",level:2},{value:"Project metadata",id:"project-metadata",level:3},{value:"Initialize",id:"initialize",level:3},{value:"Tree",id:"tree",level:3},{value:"Inputs",id:"inputs",level:3},{value:"More complex manifests",id:"more-complex-manifests",level:2},{value:"Complex pipelines",id:"complex-pipelines",level:3},{value:"Complex applications",id:"complex-applications",level:3},{value:"Choosing which plugins to run",id:"choosing-which-plugins-to-run",level:2},{value:"Running a manifest",id:"running-a-manifest",level:2}],u={toc:p};function c(e){let{components:n,...t}=e;return(0,a.kt)("wrapper",(0,i.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"how-to-write-a-manifest-file"},"How to write a manifest file"),(0,a.kt)("p",null,"The Impact Framework receives all its configuration and input data in the form of a manifest file known as an manifest. To use the framework, you will need to write a manifest file and pass its path to the command line tool. This guide will help you to understand how to construct one of these files and use it to measure the energy and carbon usage of your app."),(0,a.kt)("h2",{id:"structure-of-a-manifest"},"Structure of a manifest"),(0,a.kt)("p",null,"The basic structure of a manifest is as follows: "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name: \ndescription: \ntags: \ninitialize:\n plugins:\n : \n method:\n path: \ntree:\n children:\n child:\n pipeline:\n - \n config:\n defaults:\n inputs:\n\n")),(0,a.kt)("h3",{id:"project-metadata"},"Project metadata"),(0,a.kt)("p",null,"The file starts with some metadata about the project. There are no strict specfications for what to put in these fields, they are for you to keep track of your manifest files and to help other users to understand your use case."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name:\ndescription:\ntags:\n")),(0,a.kt)("h3",{id:"initialize"},"Initialize"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"initialize")," fields are where you specify each individual plugin that will be initialized in your pipeline. The plugins can be initialized in any order, but can only be invoked elsewhere in the manifest if they have been initialized first here. In each case, you will need to provide the ",(0,a.kt)("inlineCode",{parentName:"p"},"name"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"path")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"method")," (and ",(0,a.kt)("inlineCode",{parentName:"p"},"global-config")," if your plugin requires it):"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n sci-m:\n path: ''\n method:\n")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"The ",(0,a.kt)("inlineCode",{parentName:"li"},"name")," is the name you want this plugin instance to be recognized as by Impact Framework."),(0,a.kt)("li",{parentName:"ul"},"The ",(0,a.kt)("inlineCode",{parentName:"li"},"path")," defines where IF should look for the installed plugin. For example, for our standard library of plugins you would specify ",(0,a.kt)("inlineCode",{parentName:"li"},'"@grnsft/if-plugins"'),", as this is the name of the directory they are installed into in ",(0,a.kt)("inlineCode",{parentName:"li"},"node_modules"),"."),(0,a.kt)("li",{parentName:"ul"},"For the ",(0,a.kt)("inlineCode",{parentName:"li"},"method")," field, you should provide the name of the function exported by your plugin. For example, for the ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-e")," plugin, the correct value is ",(0,a.kt)("inlineCode",{parentName:"li"},"SciE"),".")),(0,a.kt)("h3",{id:"tree"},"Tree"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"tree")," fields are where you define the various components of your application. Each component is defined as ",(0,a.kt)("inlineCode",{parentName:"p"},"children"),", where each ",(0,a.kt)("inlineCode",{parentName:"p"},"child"),"'s output is summed to give the overall impact. Each ",(0,a.kt)("inlineCode",{parentName:"p"},"child")," can have its own plugin pipeline and its own configuration, but when none is provided, it is inherited from the tree-level configuration."),(0,a.kt)("p",null,"In the following example, there is only one component but the plugin pipeline contains two plugins; ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"sci-m"),". Neither requires any ",(0,a.kt)("inlineCode",{parentName:"p"},"config")," data, but certain information is required in ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child:\n pipeline:\n - teads-curve\n - sci-m\n config:\n defaults:\n inputs:\n - timestamp: '2023-11-02T10:35:31.820Z'\n duration: 3600\n total-embodied-emissions: 1533.12\n time-reserved: 1\n expected-lifespan: 3\n resources-reserved: 1\n total-resources: 8\n\n")),(0,a.kt)("h3",{id:"inputs"},"Inputs"),(0,a.kt)("p",null,"The most granular level of the manifest file are the ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs"),". This is where you can add specific data for each ",(0,a.kt)("inlineCode",{parentName:"p"},"child"),". Inputs must always include a ",(0,a.kt)("inlineCode",{parentName:"p"},"timestamp")," and a ",(0,a.kt)("inlineCode",{parentName:"p"},"duration"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n cpu-util: 45\n")),(0,a.kt)("p",null,"You now have a simple manifest file that will use the plugin config and input data to run the ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"sci-m")," plugins. The output data will be appended to the manifest under a new ",(0,a.kt)("inlineCode",{parentName:"p"},"outputs")," field and saved as an output file."),(0,a.kt)("h2",{id:"more-complex-manifests"},"More complex manifests"),(0,a.kt)("h3",{id:"complex-pipelines"},"Complex pipelines"),(0,a.kt)("p",null,"Whilst the manifest file we looked at above works perfectly well, it will only return the most basic output data. Most users will want to calculate an SCI score, which implies a number of additional steps:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"operational-carbon")," and ",(0,a.kt)("inlineCode",{parentName:"li"},"embodied-carbon")," must appear as inputs."),(0,a.kt)("li",{parentName:"ul"},"This means that ",(0,a.kt)("inlineCode",{parentName:"li"},"sci")," will need to be preceded by ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-m")," and ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-o")," in the plugin pipeline."),(0,a.kt)("li",{parentName:"ul"},"In most cases, ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-o")," will have to be preceded by ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-e")," to ensure ",(0,a.kt)("inlineCode",{parentName:"li"},"energy")," is available to be piped to ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-o"),"."),(0,a.kt)("li",{parentName:"ul"},"The inputs to ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-e")," will most likely be coming from a plugin such as ",(0,a.kt)("inlineCode",{parentName:"li"},"teads-curve")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"boavizta"),"."),(0,a.kt)("li",{parentName:"ul"},"The ",(0,a.kt)("inlineCode",{parentName:"li"},"sci")," plugin also requires ",(0,a.kt)("inlineCode",{parentName:"li"},"functional-unit")," information so it can convert the estimated ",(0,a.kt)("inlineCode",{parentName:"li"},"carbon")," into a useful unit."),(0,a.kt)("li",{parentName:"ul"},"You may also wish to grab your ",(0,a.kt)("inlineCode",{parentName:"li"},"input")," data by querying a metrics API on a virtual machine. ")),(0,a.kt)("p",null,"The example below gives you the full pipeline implemented in an manifest.There are also several other executable example manifests in ",(0,a.kt)("inlineCode",{parentName:"p"},"if/examples/manifests")," that you can run for yourself."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},'name: pipeline-demo\ndescription:\ntags:\naggregation:\n metrics:\n - \'carbon\'\n type: \'both\'\ninitialize:\n plugins:\n "teads-curve":\n path: "@grnsft/if-unofficial-plugins"\n method: TeadsCurve\n global-config:\n interpolation: spline\n "sci-e":\n path: "@grnsft/if-plugins"\n method: SciE\n "sci-m":\n path: "@grnsft/if-plugins"\n method: SciM\n "sci-o":\n path: "@grnsft/if-plugins"\n method: SciO\n "sci":\n path: "@grnsft/if-plugins"\n method: Sci\n global-config:\n functional-unit: "requests"\n functional-unit-time: "1 minute"\n "time-sync":\n method: TimeSync\n path: "builtin"\n global-config:\n start-time: "2023-12-12T00:00:00.000Z"\n end-time: "2023-12-12T00:01:00.000Z"\n interval: 5\n allow-padding: true\n \'group-by\':\n path: builtin\n method: GroupBy\ntree:\n children:\n child-1:\n pipeline:\n - teads-curve\n - sci-e\n - sci-m\n - sci-o\n - time-sync\n - sci\n config:\n group-by:\n group:\n - region\n - instance-type\n defaults:\n cpu/thermal-design-power: 100\n grid/carbon-intensity: 800\n device/emissions-embodied: 1533.120 # gCO2eq\n time-reserved: 3600 # 1hr in seconds\n device/expected-lifespan: 94608000 # 3 years in seconds\n resources-reserved: 1\n resources-total: 8\n functional-unit-time: "1 min"\n inputs:\n - timestamp: "2023-12-12T00:00:00.000Z"\n instance-type: A1 \n region: uk-west\n duration: 1\n cpu/utilization: 10\n - timestamp: "2023-12-12T00:00:01.000Z"\n duration: 5\n cpu/utilization: 20\n instance-type: A1 \n region: uk-west\n - timestamp: "2023-12-12T00:00:06.000Z"\n duration: 7\n cpu/utilization: 15\n instance-type: A1 \n region: uk-west\n - timestamp: "2023-12-12T00:00:13.000Z"\n duration: 30\n instance-type: A1 \n region: uk-west\n cpu/utilization: 15\n child-2:\n pipeline:\n - teads-curve\n - sci-e\n - sci-m\n - sci-o\n - time-sync\n - sci\n config:\n group-by:\n group:\n - region\n - instance-type\n defaults:\n cpu/thermal-design-power: 100\n grid/carbon-intensity: 800\n device/emissions-embodied: 1533.120 # gCO2eq\n time-reserved: 3600 # 1hr in seconds\n device/expected-lifespan: 94608000 # 3 years in seconds\n resources-reserved: 1\n resources-total: 8\n functional-unit-time: "1 min"\n inputs:\n - timestamp: "2023-12-12T00:00:00.000Z"\n duration: 1\n cpu/utilization: 30\n instance-type: A1 \n region: uk-west\n - timestamp: "2023-12-12T00:00:01.000Z"\n duration: 5\n cpu/utilization: 28\n instance-type: A1 \n region: uk-west\n - timestamp: "2023-12-12T00:00:06.000Z"\n duration: 7\n cpu/utilization: 40\n instance-type: A1 \n region: uk-west\n - timestamp: "2023-12-12T00:00:13.000Z"\n duration: 30\n cpu/utilization: 33\n instance-type: A1 \n region: uk-west\n')),(0,a.kt)("h3",{id:"complex-applications"},"Complex applications"),(0,a.kt)("p",null,"The manifest examples provided so far have only had a single component. However, Impact Framework can handle any number of nested ",(0,a.kt)("inlineCode",{parentName:"p"},"children"),"."),(0,a.kt)("p",null,"In this way, you can combine complex plugin pipelines and application architectures to calculate the energy and carbon outputs of complicated systems."),(0,a.kt)("h2",{id:"choosing-which-plugins-to-run"},"Choosing which plugins to run"),(0,a.kt)("p",null,"The plugins are designed to be composable, but they each have specific input requirements that must be met in order for the plugins to run correctly. For example, the ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," plugin requires ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/thermal-design-power")," to be available in the manifest. If it is not there, the plugin cannot use it to calculate ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/energy"),"."),(0,a.kt)("p",null,"It is also possible to leapfrog some plugins if you have access to high-level data. For example, perhaps you already know the energy being used by your CPU. In this case, there is no need to run ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve"),", you can simply provide ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/energy")," as an ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," and omit ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," from the plugin pipeline."),(0,a.kt)("p",null,"We have deliberately made the plugins modular and composable so that you can be creative in developing new plugins to replace those provided as part of IF."),(0,a.kt)("h2",{id:"running-a-manifest"},"Running a manifest"),(0,a.kt)("p",null,"You run a manifest by providing its path to our command line tool and a path to save the results file to. You can run a manifest named ",(0,a.kt)("inlineCode",{parentName:"p"},"my-manifest.yml")," using the following command:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest my-manifest.yml\n")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/68ffc9f1.df4945b5.js b/assets/js/68ffc9f1.df4945b5.js new file mode 100644 index 00000000..cc8a64d3 --- /dev/null +++ b/assets/js/68ffc9f1.df4945b5.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[644],{4137:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>d});var i=t(7294);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function r(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var s=i.createContext({}),p=function(e){var n=i.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},u=function(e){var n=p(e.components);return i.createElement(s.Provider,{value:n},e.children)},c={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},m=i.forwardRef((function(e,n){var t=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=p(t),d=a,h=m["".concat(s,".").concat(d)]||m[d]||c[d]||o;return t?i.createElement(h,r(r({ref:n},u),{},{components:t})):i.createElement(h,r({ref:n},u))}));function d(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var o=t.length,r=new Array(o);r[0]=m;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var i=t(7462),a=(t(7294),t(4137));const o={sidebar_position:4},r="How to write a manifest file",l={unversionedId:"users/how-to-write-manifests",id:"users/how-to-write-manifests",title:"How to write a manifest file",description:"The Impact Framework receives all its configuration and input data in the form of a manifest file known as an manifest. To use the framework, you will need to write a manifest file and pass its path to the command line tool. This guide will help you to understand how to construct one of these files and use it to measure the energy and carbon usage of your app.",source:"@site/docs/users/how-to-write-manifests.md",sourceDirName:"users",slug:"/users/how-to-write-manifests",permalink:"/users/how-to-write-manifests",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-write-manifests.md",tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"How to load plugins",permalink:"/users/how-to-import-plugins"},next:{title:"How to export to CSV",permalink:"/users/how-to-export-to-csv"}},s={},p=[{value:"Structure of a manifest",id:"structure-of-a-manifest",level:2},{value:"Project metadata",id:"project-metadata",level:3},{value:"Initialize",id:"initialize",level:3},{value:"Tree",id:"tree",level:3},{value:"Inputs",id:"inputs",level:3},{value:"More complex manifests",id:"more-complex-manifests",level:2},{value:"Complex pipelines",id:"complex-pipelines",level:3},{value:"Complex applications",id:"complex-applications",level:3},{value:"Choosing which plugins to run",id:"choosing-which-plugins-to-run",level:2},{value:"Running a manifest",id:"running-a-manifest",level:2}],u={toc:p};function c(e){let{components:n,...t}=e;return(0,a.kt)("wrapper",(0,i.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"how-to-write-a-manifest-file"},"How to write a manifest file"),(0,a.kt)("p",null,"The Impact Framework receives all its configuration and input data in the form of a manifest file known as an manifest. To use the framework, you will need to write a manifest file and pass its path to the command line tool. This guide will help you to understand how to construct one of these files and use it to measure the energy and carbon usage of your app."),(0,a.kt)("h2",{id:"structure-of-a-manifest"},"Structure of a manifest"),(0,a.kt)("p",null,"The basic structure of a manifest is as follows: "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name: \ndescription: \ntags: \ninitialize:\n plugins:\n : \n method:\n path: \ntree:\n children:\n child:\n pipeline:\n - \n config:\n defaults:\n inputs:\n\n")),(0,a.kt)("h3",{id:"project-metadata"},"Project metadata"),(0,a.kt)("p",null,"The file starts with some metadata about the project. There are no strict specfications for what to put in these fields, they are for you to keep track of your manifest files and to help other users to understand your use case."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name:\ndescription:\ntags:\n")),(0,a.kt)("h3",{id:"initialize"},"Initialize"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"initialize")," fields are where you specify each individual plugin that will be initialized in your pipeline. The plugins can be initialized in any order, but can only be invoked elsewhere in the manifest if they have been initialized first here. In each case, you will need to provide the ",(0,a.kt)("inlineCode",{parentName:"p"},"name"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"path")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"method")," (and ",(0,a.kt)("inlineCode",{parentName:"p"},"global-config")," if your plugin requires it):"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n sci-m:\n path: ''\n method:\n")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"The ",(0,a.kt)("inlineCode",{parentName:"li"},"name")," is the name you want this plugin instance to be recognized as by Impact Framework."),(0,a.kt)("li",{parentName:"ul"},"The ",(0,a.kt)("inlineCode",{parentName:"li"},"path")," defines where IF should look for the installed plugin. For example, for our standard library of plugins you would specify ",(0,a.kt)("inlineCode",{parentName:"li"},'"@grnsft/if-plugins"'),", as this is the name of the directory they are installed into in ",(0,a.kt)("inlineCode",{parentName:"li"},"node_modules"),"."),(0,a.kt)("li",{parentName:"ul"},"For the ",(0,a.kt)("inlineCode",{parentName:"li"},"method")," field, you should provide the name of the function exported by your plugin. For example, for the ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-e")," plugin, the correct value is ",(0,a.kt)("inlineCode",{parentName:"li"},"SciE"),".")),(0,a.kt)("h3",{id:"tree"},"Tree"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"tree")," fields are where you define the various components of your application. Each component is defined as ",(0,a.kt)("inlineCode",{parentName:"p"},"children"),", where each ",(0,a.kt)("inlineCode",{parentName:"p"},"child"),"'s output is summed to give the overall impact. Each ",(0,a.kt)("inlineCode",{parentName:"p"},"child")," can have its own plugin pipeline and its own configuration, but when none is provided, it is inherited from the tree-level configuration."),(0,a.kt)("p",null,"In the following example, there is only one component but the plugin pipeline contains two plugins; ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"sci-m"),". Neither requires any ",(0,a.kt)("inlineCode",{parentName:"p"},"config")," data, but certain information is required in ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child:\n pipeline:\n - teads-curve\n - sci-m\n config:\n defaults:\n inputs:\n - timestamp: '2023-11-02T10:35:31.820Z'\n duration: 3600\n total-embodied-emissions: 1533.12\n time-reserved: 1\n expected-lifespan: 3\n resources-reserved: 1\n total-resources: 8\n\n")),(0,a.kt)("h3",{id:"inputs"},"Inputs"),(0,a.kt)("p",null,"The most granular level of the manifest file are the ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs"),". This is where you can add specific data for each ",(0,a.kt)("inlineCode",{parentName:"p"},"child"),". Inputs must always include a ",(0,a.kt)("inlineCode",{parentName:"p"},"timestamp")," and a ",(0,a.kt)("inlineCode",{parentName:"p"},"duration"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n cpu-util: 45\n")),(0,a.kt)("p",null,"You now have a simple manifest file that will use the plugin config and input data to run the ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"sci-m")," plugins. The output data will be appended to the manifest under a new ",(0,a.kt)("inlineCode",{parentName:"p"},"outputs")," field and saved as an output file."),(0,a.kt)("h2",{id:"more-complex-manifests"},"More complex manifests"),(0,a.kt)("h3",{id:"complex-pipelines"},"Complex pipelines"),(0,a.kt)("p",null,"Whilst the manifest file we looked at above works perfectly well, it will only return the most basic output data. Most users will want to calculate an SCI score, which implies a number of additional steps:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"operational-carbon")," and ",(0,a.kt)("inlineCode",{parentName:"li"},"embodied-carbon")," must appear as inputs."),(0,a.kt)("li",{parentName:"ul"},"This means that ",(0,a.kt)("inlineCode",{parentName:"li"},"sci")," will need to be preceded by ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-m")," and ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-o")," in the plugin pipeline."),(0,a.kt)("li",{parentName:"ul"},"In most cases, ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-o")," will have to be preceded by ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-e")," to ensure ",(0,a.kt)("inlineCode",{parentName:"li"},"energy")," is available to be piped to ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-o"),"."),(0,a.kt)("li",{parentName:"ul"},"The inputs to ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-e")," will most likely be coming from a plugin such as ",(0,a.kt)("inlineCode",{parentName:"li"},"teads-curve")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"boavizta"),"."),(0,a.kt)("li",{parentName:"ul"},"The ",(0,a.kt)("inlineCode",{parentName:"li"},"sci")," plugin also requires ",(0,a.kt)("inlineCode",{parentName:"li"},"functional-unit")," information so it can convert the estimated ",(0,a.kt)("inlineCode",{parentName:"li"},"carbon")," into a useful unit."),(0,a.kt)("li",{parentName:"ul"},"You may also wish to grab your ",(0,a.kt)("inlineCode",{parentName:"li"},"input")," data by querying a metrics API on a virtual machine. ")),(0,a.kt)("p",null,"The example below gives you the full pipeline implemented in an manifest.There are also several other executable example manifests in ",(0,a.kt)("inlineCode",{parentName:"p"},"if/examples/manifests")," that you can run for yourself."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},'name: pipeline-demo\ndescription:\ntags:\naggregation:\n metrics:\n - \'carbon\'\n type: \'both\'\ninitialize:\n plugins:\n "teads-curve":\n path: "@grnsft/if-unofficial-plugins"\n method: TeadsCurve\n global-config:\n interpolation: spline\n "sci-e":\n path: "@grnsft/if-plugins"\n method: SciE\n "sci-m":\n path: "@grnsft/if-plugins"\n method: SciM\n "sci-o":\n path: "@grnsft/if-plugins"\n method: SciO\n "sci":\n path: "@grnsft/if-plugins"\n method: Sci\n global-config:\n functional-unit: "requests"\n functional-unit-time: "1 minute"\n "time-sync":\n method: TimeSync\n path: "builtin"\n global-config:\n start-time: "2023-12-12T00:00:00.000Z"\n end-time: "2023-12-12T00:01:00.000Z"\n interval: 5\n allow-padding: true\n \'group-by\':\n path: builtin\n method: GroupBy\ntree:\n children:\n child-1:\n pipeline:\n - teads-curve\n - sci-e\n - sci-m\n - sci-o\n - time-sync\n - sci\n config:\n group-by:\n group:\n - region\n - instance-type\n defaults:\n cpu/thermal-design-power: 100\n grid/carbon-intensity: 800\n device/emissions-embodied: 1533.120 # gCO2eq\n time-reserved: 3600 # 1hr in seconds\n device/expected-lifespan: 94608000 # 3 years in seconds\n resources-reserved: 1\n resources-total: 8\n functional-unit-time: "1 min"\n inputs:\n - timestamp: "2023-12-12T00:00:00.000Z"\n instance-type: A1 \n region: uk-west\n duration: 1\n cpu/utilization: 10\n - timestamp: "2023-12-12T00:00:01.000Z"\n duration: 5\n cpu/utilization: 20\n instance-type: A1 \n region: uk-west\n - timestamp: "2023-12-12T00:00:06.000Z"\n duration: 7\n cpu/utilization: 15\n instance-type: A1 \n region: uk-west\n - timestamp: "2023-12-12T00:00:13.000Z"\n duration: 30\n instance-type: A1 \n region: uk-west\n cpu/utilization: 15\n child-2:\n pipeline:\n - teads-curve\n - sci-e\n - sci-m\n - sci-o\n - time-sync\n - sci\n config:\n group-by:\n group:\n - region\n - instance-type\n defaults:\n cpu/thermal-design-power: 100\n grid/carbon-intensity: 800\n device/emissions-embodied: 1533.120 # gCO2eq\n time-reserved: 3600 # 1hr in seconds\n device/expected-lifespan: 94608000 # 3 years in seconds\n resources-reserved: 1\n resources-total: 8\n functional-unit-time: "1 min"\n inputs:\n - timestamp: "2023-12-12T00:00:00.000Z"\n duration: 1\n cpu/utilization: 30\n instance-type: A1 \n region: uk-west\n - timestamp: "2023-12-12T00:00:01.000Z"\n duration: 5\n cpu/utilization: 28\n instance-type: A1 \n region: uk-west\n - timestamp: "2023-12-12T00:00:06.000Z"\n duration: 7\n cpu/utilization: 40\n instance-type: A1 \n region: uk-west\n - timestamp: "2023-12-12T00:00:13.000Z"\n duration: 30\n cpu/utilization: 33\n instance-type: A1 \n region: uk-west\n')),(0,a.kt)("h3",{id:"complex-applications"},"Complex applications"),(0,a.kt)("p",null,"The manifest examples provided so far have only had a single component. However, Impact Framework can handle any number of nested ",(0,a.kt)("inlineCode",{parentName:"p"},"children"),"."),(0,a.kt)("p",null,"In this way, you can combine complex plugin pipelines and application architectures to calculate the energy and carbon outputs of complicated systems."),(0,a.kt)("h2",{id:"choosing-which-plugins-to-run"},"Choosing which plugins to run"),(0,a.kt)("p",null,"The plugins are designed to be composable, but they each have specific input requirements that must be met in order for the plugins to run correctly. For example, the ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," plugin requires ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/thermal-design-power")," to be available in the manifest. If it is not there, the plugin cannot use it to calculate ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/energy"),"."),(0,a.kt)("p",null,"It is also possible to leapfrog some plugins if you have access to high-level data. For example, perhaps you already know the energy being used by your CPU. In this case, there is no need to run ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve"),", you can simply provide ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/energy")," as an ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," and omit ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," from the plugin pipeline."),(0,a.kt)("p",null,"We have deliberately made the plugins modular and composable so that you can be creative in developing new plugins to replace those provided as part of IF."),(0,a.kt)("h2",{id:"running-a-manifest"},"Running a manifest"),(0,a.kt)("p",null,"You run a manifest by providing its path to our command line tool and a path to save the results file to. You can run a manifest named ",(0,a.kt)("inlineCode",{parentName:"p"},"my-manifest.yml")," using the following command:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest my-manifest.yml\n")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/75e434b4.03a9174b.js b/assets/js/75e434b4.03a9174b.js new file mode 100644 index 00000000..1ac8f05c --- /dev/null +++ b/assets/js/75e434b4.03a9174b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[229],{4137:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>f});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var u=i.createContext({}),s=function(e){var t=i.useContext(u),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=s(e.components);return i.createElement(u.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,u=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),m=s(n),f=a,d=m["".concat(u,".").concat(f)]||m[f]||c[f]||r;return n?i.createElement(d,o(o({ref:t},p),{},{components:n})):i.createElement(d,o({ref:t},p))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var u in t)hasOwnProperty.call(t,u)&&(l[u]=t[u]);l.originalType=e,l.mdxType="string"==typeof e?e:a,o[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>l,toc:()=>s});var i=n(7462),a=(n(7294),n(4137));const r={},o="Plugins",l={unversionedId:"reference/plugins",id:"reference/plugins",title:"Plugins",description:"The Impact Framework includes several builtin plugins (e.g. time-sync, groupby and csv-exporter). All other plugins are external plugins that have to be installed before they can be run in an IF pipeline. Anyone can create and install a new plugin. As long as the plugin conforms to the expected interface, IF can run it.",source:"@site/docs/reference/plugins.md",sourceDirName:"reference",slug:"/reference/plugins",permalink:"/reference/plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/reference/plugins.md",tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Command line tool",permalink:"/reference/cli"},next:{title:"FAQs",permalink:"/FAQ"}},u={},s=[{value:"built-in",id:"built-in",level:3},{value:"if-plugins",id:"if-plugins",level:3},{value:"if-unofficial-plugins",id:"if-unofficial-plugins",level:3},{value:"Exhaust plugins (outputs)",id:"exhaust-plugins-outputs",level:2}],p={toc:s};function c(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,i.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"plugins"},"Plugins"),(0,a.kt)("p",null,"The Impact Framework includes several builtin plugins (e.g. ",(0,a.kt)("inlineCode",{parentName:"p"},"time-sync"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"groupby")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"csv-exporter"),"). All other plugins are external plugins that have to be installed before they can be run in an IF pipeline. Anyone can create and install a new plugin. As long as the plugin conforms to the expected interface, IF can run it."),(0,a.kt)("p",null,"There are two repositories that were created by the IF core team. The ",(0,a.kt)("inlineCode",{parentName:"p"},"if-plugins"),' repository contains the "core" set of plugins that IF developers will maintain and support. We also provide a second repository of ',(0,a.kt)("inlineCode",{parentName:"p"},"if-unofficial")," plugins that are mainly re-implementations of existing third party plugins or code that we expect community members to maintain independently of the IF core team."),(0,a.kt)("p",null,"Documentation for the specific individual plugins are available in the plugin READMEs. Below you will find a brief description of each plugin and a link to its documentation:"),(0,a.kt)("h3",{id:"built-in"},(0,a.kt)("inlineCode",{parentName:"h3"},"built-in")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins#readme"},"Time Sync"),": Takes a heterogeneous set of time series data that might be offset, discontinuous or irregularly spaces and returns time series conforming to a user defined time grid. E.g. a user can define that all sets of observations should start at sopme global start time, end at some global end time and have a specific temporal resolution. ")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins#readme"},"CSV Exporter"),": Exports data for a given metric to a CSV file.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins#readme"},"Groupby"),": Allows a user to regroup their output data according to given keys."))),(0,a.kt)("h3",{id:"if-plugins"},(0,a.kt)("inlineCode",{parentName:"h3"},"if-plugins")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/cloud-metadata/README.md"},"Cloud metadata"),": Looks up detailed metadata about a given cloud instance type and region, including the physical processor being used."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/e-mem/README.md"},"E-MEM"),": Calculate the energy expended due to memroy usage, by multiplying the energy used in GB by a coefficient."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/sci-e/README.md"},"SCI-E"),": Calculates the sum of all energy components."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/sci-m/README.md"},"SCI-M")," - Calculates the embodied carbon for a component."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/sci-o/index.ts"},"SCI-O")," - Calculates the operational carbon from the total energy and grid carbon intensity."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/sci/README.md"},"SCI"),": Calculates the software carbon intensity."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/shell/README.md"},"SHELL")," - A plugin that enables external code in any language to be run in a child process"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/tdp-finder"},"TDP-FINDER"),": Looks up the thermnal desig power for a given processor in a local database."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/sum"},"Sum"),": a generic arithmetic plugin that allows you to sum any set of input parameters."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/multiply"},"Multiply"),": a generic arithmetic plugin that allows you to multiply any set of input parameters."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/coefficient"},"Coefficient"),": a generic arithmetic plugin that allows you to multiply any input value by a coefficient."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/e-net"},"E-NET"),": simply multiplies the amount of data transferred (GB) by a coefficient (kWh/GB) to yield network/energy."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/mock-observations"},"Mock Observations"),": A plugin for mocking observations (inputs) for testing and demo purposes."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/csv-export"},"CSV-Export"),": a generic CSV exporter plugin."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/divide"},"Divide"),": A generic plugin for doing arithmetic division of two values."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/regex"},"Regex"),": A generic plugin to match part of one string and extract it into another.")),(0,a.kt)("h3",{id:"if-unofficial-plugins"},(0,a.kt)("inlineCode",{parentName:"h3"},"if-unofficial-plugins")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/azure-importer/README.md"},"Azure importer"),": Grabs usage metrics from an Azure virtual machine, given user credentials and virtual machine details."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/ccf/README.md"},"Cloud Carbon Footprint"),": Calculates usage metrics using the Cloud Carbon Footprint APIs."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/watt-time/README.md"},"WattTime"),": WattTime is an external service for looking up grid emissions based on location."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/teads-curve/README.md"},"TEADS-CURVE"),": Calculates the energy in kWh used by the CPU"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/teads-aws/README.md"},"TEADS-AWS"),": Calculates the energy in kWh used by the CPU using a model specific to AWS instances."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/boavizta/README.md"},"Boavizta"),": Calculates energy and embodied carbon using the Boavizta APIs."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/co2js/README.md"},"co2js"),": Calculates the carbon emissions of a website.")),(0,a.kt)("h2",{id:"exhaust-plugins-outputs"},"Exhaust plugins (outputs)"),(0,a.kt)("p",null,"Export plugins designed to implement custom ways of exporting output file. Currenlty supported ones are ",(0,a.kt)("inlineCode",{parentName:"p"},"csv"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"yaml")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"log")," plugins.\nThese are currently built in to the IF, but migrating to dynamically loading export functions as plugins is part of our near-term roadmap."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/75e434b4.6ba4fb88.js b/assets/js/75e434b4.6ba4fb88.js deleted file mode 100644 index d8f13422..00000000 --- a/assets/js/75e434b4.6ba4fb88.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[229],{4137:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>f});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var u=i.createContext({}),s=function(e){var t=i.useContext(u),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=s(e.components);return i.createElement(u.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,u=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),m=s(n),f=a,d=m["".concat(u,".").concat(f)]||m[f]||c[f]||r;return n?i.createElement(d,o(o({ref:t},p),{},{components:n})):i.createElement(d,o({ref:t},p))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var u in t)hasOwnProperty.call(t,u)&&(l[u]=t[u]);l.originalType=e,l.mdxType="string"==typeof e?e:a,o[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>l,toc:()=>s});var i=n(7462),a=(n(7294),n(4137));const r={},o="Plugins",l={unversionedId:"reference/plugins",id:"reference/plugins",title:"Plugins",description:"The Impact Framework includes several builtin plugins (e.g. time-sync, groupby and csv-exporter). All other plugins are external plugins that have to be installed before they can be run in an IF pipeline. Anyone can create and install a new plugin. As long as the plugin conforms to the expected interface, IF can run it.",source:"@site/docs/reference/plugins.md",sourceDirName:"reference",slug:"/reference/plugins",permalink:"/reference/plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/reference/plugins.md",tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Command line tool",permalink:"/reference/cli"},next:{title:"FAQs",permalink:"/FAQ"}},u={},s=[{value:"built-in",id:"built-in",level:3},{value:"if-plugins",id:"if-plugins",level:3},{value:"if-unofficial-plugins",id:"if-unofficial-plugins",level:3},{value:"Exhaust plugins (outputs)",id:"exhaust-plugins-outputs",level:2}],p={toc:s};function c(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,i.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"plugins"},"Plugins"),(0,a.kt)("p",null,"The Impact Framework includes several builtin plugins (e.g. ",(0,a.kt)("inlineCode",{parentName:"p"},"time-sync"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"groupby")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"csv-exporter"),"). All other plugins are external plugins that have to be installed before they can be run in an IF pipeline. Anyone can create and install a new plugin. As long as the plugin conforms to the expected interface, IF can run it."),(0,a.kt)("p",null,"There are two repositories that were created by the IF core team. The ",(0,a.kt)("inlineCode",{parentName:"p"},"if-plugins"),' repository contains the "core" set of plugins that IF developers will maintain and support. We also provide a second repository of ',(0,a.kt)("inlineCode",{parentName:"p"},"if-unofficial")," plugins that are mainly re-implementations of existing third party plugins or code that we expect community members to maintain independently of the IF core team."),(0,a.kt)("p",null,"Documentation for the specific individual plugins are available in the plugin READMEs. Below you will find a brief description of each plugin and a link to its documentation:"),(0,a.kt)("h3",{id:"built-in"},(0,a.kt)("inlineCode",{parentName:"h3"},"built-in")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins#readme"},"Time Sync"),": Takes a heterogeneous set of time series data that might be offset, discontinuous or irregularly spaces and returns time series conforming to a user defined time grid. E.g. a user can define that all sets of observations should start at sopme global start time, end at some global end time and have a specific temporal resolution. ")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins#readme"},"CSV Exporter"),": Exports data for a given metric to a CSV file.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins#readme"},"Groupby"),": Allows a user to regroup their output data according to given keys."))),(0,a.kt)("h3",{id:"if-plugins"},(0,a.kt)("inlineCode",{parentName:"h3"},"if-plugins")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/cloud-metadata/README.md"},"Cloud metadata"),": Looks up detailed metadata about a given cloud instance type and region, including the physical processor being used."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/e-mem/README.md"},"E-MEM"),": Calculate the energy expended due to memroy usage, by multiplying the energy used in GB by a coefficient."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/sci-e/README.md"},"SCI-E"),": Calculates the sum of all energy components."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/sci-m/README.md"},"SCI-M")," - Calculates the embodied carbon for a component."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/sci-o/index.ts"},"SCI-O")," - Calculates the operational carbon from the total energy and grid carbon intensity."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/sci/README.md"},"SCI"),": Calculates the software carbon intensity."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/shell/README.md"},"SHELL")," - A plugin that enables external code in any language to be run in a child process"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/tdp-finder"},"TDP-FINDER"),": Looks up the thermnal desig power for a given processor in a local database."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/sum"},"Sum"),": a generic arithmetic plugin that allows you to sum any set of input parameters."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/multiply"},"Multiply"),": a generic arithmetic plugin that allows you to multiply any set of input parameters."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/coefficient"},"Coefficient"),": a generic arithmetic plugin that allows you to multiply any input value by a coefficient."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/e-net"},"E-NET"),": simply multiplies the amount of data transferred (GB) by a coefficient (kWh/GB) to yield network/energy."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/mock-observations"},"Mock Observations"),": A plugin for mocking observations (inputs) for testing and demo purposes."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/csv-export"},"CSV-Export"),": a generic CSV exporter plugin."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/divide"},"Divide"),": A generic plugin for doing arithmetic division of two values."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-plugins/tree/main/src/lib/regex"},"Regex"),": A generic plugin to match part of one string and extract it into another.")),(0,a.kt)("h3",{id:"if-unofficial-plugins"},(0,a.kt)("inlineCode",{parentName:"h3"},"if-unofficial-plugins")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/azure-importer/README.md"},"Azure importer"),": Grabs usage metrics from an Azure virtual machine, given user credentials and virtual machine details."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/ccf/README.md"},"Cloud Carbon Footprint"),": Calculates usage metrics using the Cloud Carbon Footprint APIs."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/watt-time/README.md"},"WattTime"),": WattTime is an external service for looking up grid emissions based on location."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/teads-curve/README.md"},"TEADS-CURVE"),": Calculates the energy in kWh used by the CPU"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/teads-aws/README.md"},"TEADS-AWS"),": Calculates the energy in kWh used by the CPU using a model specific to AWS instances."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/boavizta/README.md"},"Boavizta"),": Calculates energy and embodied carbon using the Boavizta APIs."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/co2js/README.md"},"co2js"),": Calculates the carbon emissions of a website.")),(0,a.kt)("h2",{id:"exhaust-plugins-outputs"},"Exhaust plugins (outputs)"),(0,a.kt)("p",null,"Export plugins designed to implement custom ways of exporting output file. Currenlty supported ones are ",(0,a.kt)("inlineCode",{parentName:"p"},"csv"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"yaml")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"log")," plugins.\nThese are currently built in to the IF, but migrating to dynamically loading export functions as plugins is part of our near-term roadmap."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/91c76d4c.136aee68.js b/assets/js/91c76d4c.136aee68.js new file mode 100644 index 00000000..d4a117be --- /dev/null +++ b/assets/js/91c76d4c.136aee68.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[960],{4137:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>c});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},m=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,s=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),u=p(n),c=r,f=u["".concat(s,".").concat(c)]||u[c]||d[c]||o;return n?a.createElement(f,i(i({ref:t},m),{},{components:n})):a.createElement(f,i({ref:t},m))}));function c(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:r,i[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var a=n(7462),r=(n(7294),n(4137));const o={},i="Command line tool",l={unversionedId:"reference/cli",id:"reference/cli",title:"Command line tool",description:"A core feature of the Impact Framework is the ie command line tool (CLI). This is how you trigger Impact Framework to execute a certain manifest file.",source:"@site/docs/reference/cli.md",sourceDirName:"reference",slug:"/reference/cli",permalink:"/reference/cli",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/reference/cli.md",tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Reference",permalink:"/reference/"},next:{title:"Plugins",permalink:"/reference/plugins"}},s={},p=[{value:"ie",id:"ie",level:2},{value:"--manifest",id:"--manifest",level:2},{value:"--output",id:"--output",level:2},{value:"CSV export identifiers",id:"csv-export-identifiers",level:3},{value:"--override-params",id:"--override-params",level:2}],m={toc:p};function d(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"command-line-tool"},"Command line tool"),(0,r.kt)("p",null,"A core feature of the Impact Framework is the ",(0,r.kt)("inlineCode",{parentName:"p"},"ie")," command line tool (CLI). This is how you trigger Impact Framework to execute a certain manifest file. "),(0,r.kt)("p",null,"Let's take a look at the various commands exposed by ",(0,r.kt)("inlineCode",{parentName:"p"},"ie"),"."),(0,r.kt)("h2",{id:"ie"},(0,r.kt)("inlineCode",{parentName:"h2"},"ie")),(0,r.kt)("p",null,"If you have globally installed our ",(0,r.kt)("inlineCode",{parentName:"p"},"if")," npm package, you can invoke the CLI using the ",(0,r.kt)("inlineCode",{parentName:"p"},"ie")," command directly in your terminal. The ",(0,r.kt)("inlineCode",{parentName:"p"},"ie")," command is an alias to ",(0,r.kt)("inlineCode",{parentName:"p"},"npx ts-node src/index.ts"),", which executes the Impact Framework's ",(0,r.kt)("inlineCode",{parentName:"p"},"src/index.ts")," script and acts as the entry point for Impact Framework."),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"ie ")),(0,r.kt)("h2",{id:"--manifest"},(0,r.kt)("inlineCode",{parentName:"h2"},"--manifest")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"--manifest")," flag is the only required flag and tells ",(0,r.kt)("inlineCode",{parentName:"p"},"ie")," where to find the manifest file that you want to execute. This command expects to receive the path where your manifest file is saved, as shown in the following example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest examples/manifests/my-manifest.yml\n")),(0,r.kt)("h2",{id:"--output"},(0,r.kt)("inlineCode",{parentName:"h2"},"--output")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"--output")," flag is optional and is used for defining a path to save your output data. If you provide the ",(0,r.kt)("inlineCode",{parentName:"p"},"--output")," command with a path, your output data will be saved as a ",(0,r.kt)("inlineCode",{parentName:"p"},".yml")," file to disk. If you omit this command, your output data will be displayed in the terminal."),(0,r.kt)("p",null,"Here is an example of ",(0,r.kt)("inlineCode",{parentName:"p"},"--output")," being used to define a path:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest examples/manifests/my-manifest.yml --output examples/outputs/my-outdata.yml\n")),(0,r.kt)("h3",{id:"csv-export-identifiers"},"CSV export identifiers"),(0,r.kt)("p",null,"If you want to save data to CSV, you have to select a specific metric to export. You do this by adding a hashtag and the metric name after the savepath provided to the output command. For example, you could save the ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," data to a CSV file called ",(0,r.kt)("inlineCode",{parentName:"p"},"demo.csv")," as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest demo.yml --output demo#carbon\n")),(0,r.kt)("h2",{id:"--override-params"},(0,r.kt)("inlineCode",{parentName:"h2"},"--override-params")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"override-params")," command is used when you want to discard our recommended set of parameters and associated units and aggregation methods and instead provide your own. We do not recommend this, and if you use this feature you take full responsibility for any errors you introduce downstream, including unit or aggregation errors. This is why we hide the ability to override the parameters behind a CLI command - it is an advanced feature that you should only use if you really know what you are doing. "),(0,r.kt)("p",null,"You pass the path to your new parameter file as an argument. The file is expected to conform to the same structure as our ",(0,r.kt)("inlineCode",{parentName:"p"},"src/config/params.ts")," file."),(0,r.kt)("p",null,"For example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest --override-params \n")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/91c76d4c.9525df5d.js b/assets/js/91c76d4c.9525df5d.js deleted file mode 100644 index 0b679aaf..00000000 --- a/assets/js/91c76d4c.9525df5d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[960],{4137:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>c});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var p=a.createContext({}),s=function(e){var t=a.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},m=function(e){var t=s(e.components);return a.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,p=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),d=s(n),c=r,f=d["".concat(p,".").concat(c)]||d[c]||u[c]||o;return n?a.createElement(f,i(i({ref:t},m),{},{components:n})):a.createElement(f,i({ref:t},m))}));function c(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=d;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:r,i[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var a=n(7462),r=(n(7294),n(4137));const o={},i="Command line tool",l={unversionedId:"reference/cli",id:"reference/cli",title:"Command line tool",description:"A core feature of the Impact Framework is the ie command line tool (CLI). This is how you trigger Impact Framework to execute a certain manifest file.",source:"@site/docs/reference/cli.md",sourceDirName:"reference",slug:"/reference/cli",permalink:"/reference/cli",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/reference/cli.md",tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Reference",permalink:"/reference/"},next:{title:"Plugins",permalink:"/reference/plugins"}},p={},s=[{value:"ie",id:"ie",level:2},{value:"--manifest",id:"--manifest",level:2},{value:"--output",id:"--output",level:2},{value:"CSV export identifiers",id:"csv-export-identifiers",level:3},{value:"--override-params",id:"--override-params",level:2}],m={toc:s};function u(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"command-line-tool"},"Command line tool"),(0,r.kt)("p",null,"A core feature of the Impact Framework is the ",(0,r.kt)("inlineCode",{parentName:"p"},"ie")," command line tool (CLI). This is how you trigger Impact Framework to execute a certain manifest file. "),(0,r.kt)("p",null,"Let's take a look at the various commands exposed by ",(0,r.kt)("inlineCode",{parentName:"p"},"ie"),"."),(0,r.kt)("h2",{id:"ie"},(0,r.kt)("inlineCode",{parentName:"h2"},"ie")),(0,r.kt)("p",null,"If you have globally installed our ",(0,r.kt)("inlineCode",{parentName:"p"},"if")," npm package, you can invoke the CLI using the ",(0,r.kt)("inlineCode",{parentName:"p"},"ie")," command directly in your terminal. The ",(0,r.kt)("inlineCode",{parentName:"p"},"ie")," command is an alias to ",(0,r.kt)("inlineCode",{parentName:"p"},"npx ts-node src/index.ts"),", which executes the Impact Framework's ",(0,r.kt)("inlineCode",{parentName:"p"},"src/index.ts")," script and acts as the entry point for Impact Framework."),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"ie ")),(0,r.kt)("h2",{id:"--manifest"},(0,r.kt)("inlineCode",{parentName:"h2"},"--manifest")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"--manifest")," flag is the only required flag and tells ",(0,r.kt)("inlineCode",{parentName:"p"},"ie")," where to find the manifest file that you want to execute. This command expects to receive the path where your manifest file is saved, as shown in the following example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest examples/manifests/my-manifest.yml\n")),(0,r.kt)("h2",{id:"--output"},(0,r.kt)("inlineCode",{parentName:"h2"},"--output")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"--output")," flag is optional and is used for defining a path to save your output data. If you provide the ",(0,r.kt)("inlineCode",{parentName:"p"},"--output")," command with a path, your output data will be saved as a ",(0,r.kt)("inlineCode",{parentName:"p"},".yml")," file to disk. If you omit this command, your output data will be displayed in the terminal."),(0,r.kt)("p",null,"Here is an example of ",(0,r.kt)("inlineCode",{parentName:"p"},"--output")," being used to define a path:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest examples/manifests/my-manifest.yml --output examples/outputs/my-outdata.yml\n")),(0,r.kt)("h3",{id:"csv-export-identifiers"},"CSV export identifiers"),(0,r.kt)("p",null,"If you want to save data to CSV, you have to select a specific metric to export. You do this by adding a hashtag and the metric name after the savepath provided to the output command. For example, you could save the ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," data to a CSV file called ",(0,r.kt)("inlineCode",{parentName:"p"},"demo.csv")," as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest demo.yml --output demo#carbon\n")),(0,r.kt)("h2",{id:"--override-params"},(0,r.kt)("inlineCode",{parentName:"h2"},"--override-params")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"override-params")," command is used when you want to discard our recommended set of parameters and associated units and aggregation methods and instead provide your own. We do not recommend this, and if you use this feature you take full responsibility for any errors you introduce downstream, including unit or aggregation errors. This is why we hide the ability to override the parameters behind a CLI command - it is an advanced feature that you should only use if you really know what you are doing. "),(0,r.kt)("p",null,"You pass the path to your new parameter file as an argument. The file is expected to conform to the same structure as our ",(0,r.kt)("inlineCode",{parentName:"p"},"src/config/params.ts")," file."),(0,r.kt)("p",null,"For example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest --override-params \n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/93f138be.5f45353c.js b/assets/js/93f138be.5f45353c.js new file mode 100644 index 00000000..7dae7dff --- /dev/null +++ b/assets/js/93f138be.5f45353c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[713],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=r.createContext({}),p=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=p(e.components);return r.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=p(n),m=i,f=d["".concat(l,".").concat(m)]||d[m]||c[m]||o;return n?r.createElement(f,a(a({ref:t},u),{},{components:n})):r.createElement(f,a({ref:t},u))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,a=new Array(o);a[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,a[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>c,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var r=n(7462),i=(n(7294),n(4137));const o={"sidebar-position":3},a="How to write unit tests",s={unversionedId:"developers/how-to-write-unit-tests",id:"developers/how-to-write-unit-tests",title:"How to write unit tests",description:"Impact Framework unit tests follow a standard format. We use the jest testing library. You can run all our existing tests by opening the project directory and running npm test. This page explains how you can add new unit tests for your plugins (or add some for our plugins if you notice a gap).",source:"@site/docs/developers/how-to-write-unit-tests.md",sourceDirName:"developers",slug:"/developers/how-to-write-unit-tests",permalink:"/developers/how-to-write-unit-tests",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/developers/how-to-write-unit-tests.md",tags:[],version:"current",frontMatter:{"sidebar-position":3},sidebar:"tutorialSidebar",previous:{title:"How to visualize results",permalink:"/developers/how-to-visualize-results"},next:{title:"Reference",permalink:"/reference/"}},l={},p=[{value:"Test files",id:"test-files",level:2},{value:"Setting up your test file",id:"setting-up-your-test-file",level:2},{value:"Describe",id:"describe",level:2},{value:"It",id:"it",level:2},{value:"Errors",id:"errors",level:2},{value:"Mocks",id:"mocks",level:2},{value:"Coverage",id:"coverage",level:2}],u={toc:p};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"how-to-write-unit-tests"},"How to write unit tests"),(0,i.kt)("p",null,"Impact Framework unit tests follow a standard format. We use the ",(0,i.kt)("inlineCode",{parentName:"p"},"jest")," testing library. You can run all our existing tests by opening the project directory and running ",(0,i.kt)("inlineCode",{parentName:"p"},"npm test"),". This page explains how you can add new unit tests for your plugins (or add some for our plugins if you notice a gap)."),(0,i.kt)("h2",{id:"test-files"},"Test files"),(0,i.kt)("p",null,"Both the IF and the project repositories include a ",(0,i.kt)("inlineCode",{parentName:"p"},"__test__")," directory. Inside, you will find subdirectory ",(0,i.kt)("inlineCode",{parentName:"p"},"unit/lib")," containing directories for each plugin. Your plugin repository should also follow this structure. Inside the plugin directory you can add ",(0,i.kt)("inlineCode",{parentName:"p"},"index.test.ts"),". This is where you write your unit tests. For example, here's the directory tree for our ",(0,i.kt)("inlineCode",{parentName:"p"},"teads-curve")," test file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"\nif-unofficial-plugins\n |\n |- src\n |\n |-__tests__\n |\n |-unit\n |\n |-lib\n |\n teads-curve\n |\n |- index.test.ts\n")),(0,i.kt)("h2",{id:"setting-up-your-test-file"},"Setting up your test file"),(0,i.kt)("p",null,"You will need to import your plugin so that it can be instantiated and tested. You will also need some elements from ",(0,i.kt)("inlineCode",{parentName:"p"},"jest/globals"),":\nFor example, these are the imports for our ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"import {Sum} from '../../../../lib';\nimport {ERRORS} from '../../../../util/errors';\nconst {InputValidationError} = ERRORS;\n")),(0,i.kt)("p",null,"You may require other imports for your specific set of tests."),(0,i.kt)("h2",{id:"describe"},"Describe"),(0,i.kt)("p",null,"Each method should have its own dedicated ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block. "),(0,i.kt)("p",null,"Your unit tests should have ",(0,i.kt)("em",{parentName:"p"},"at least")," two ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," blocks, one to test the plugin initialization and one for ",(0,i.kt)("inlineCode",{parentName:"p"},"execute"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'describe("init", ()=> {})\ndescribe("execute", ()=> {})\n')),(0,i.kt)("p",null,"For example, here is a describe block checking that the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin initializes correctly:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"describe('lib/sum: ', () => {\n describe('Sum: ', () => {\n const globalConfig = {\n 'input-parameters': ['cpu/energy', 'network/energy', 'memory/energy'],\n 'output-parameter': 'energy',\n };\n const sum = Sum(globalConfig);\n\n describe('init: ', () => {\n it('successfully initalized.', () => {\n expect(sum).toHaveProperty('metadata');\n expect(sum).toHaveProperty('execute');\n });\n });\n })\n})\n")),(0,i.kt)("h2",{id:"it"},"It"),(0,i.kt)("p",null,"Within each ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block, each effect to be tested should have a dedicated ",(0,i.kt)("inlineCode",{parentName:"p"},"it")," block."),(0,i.kt)("p",null,"Here's an example of a new ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block for the ",(0,i.kt)("inlineCode",{parentName:"p"},"execute()")," method on the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin. The ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block indicates that we are testing effects of the ",(0,i.kt)("inlineCode",{parentName:"p"},"execute()")," method. ",(0,i.kt)("inlineCode",{parentName:"p"},"it")," is specific to a single outcome - in this case there are two ",(0,i.kt)("inlineCode",{parentName:"p"},"it")," blocks that test that the plugin returns a specific result in the happy path and throws an exception if the user has provided invalid config data, specifically that the user-provided ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," parameter is missing:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"}," describe('execute(): ', () => {\n it('successfully applies Sum strategy to given input.', async () => {\n expect.assertions(1);\n\n const expectedResult = [\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n energy: 3,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ];\n\n const result = await sum.execute([\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ]);\n\n expect(result).toStrictEqual(expectedResult);\n });\n\n it('throws an error on missing params in input.', async () => {\n const expectedMessage =\n 'Sum: cpu/energy is missing from the input array.';\n\n expect.assertions(1);\n\n try {\n await sum.execute([\n {\n duration: 3600,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ]);\n } catch (error) {\n expect(error).toStrictEqual(\n new InputValidationError(expectedMessage)\n );\n }\n });\n })\n")),(0,i.kt)("h2",{id:"errors"},"Errors"),(0,i.kt)("p",null,"We prefer to use ",(0,i.kt)("inlineCode",{parentName:"p"},"expect")," to check the errors returned from a test. We do this by writing ",(0,i.kt)("inlineCode",{parentName:"p"},"expect")," in a ",(0,i.kt)("inlineCode",{parentName:"p"},"catch")," block. Here's an example from our ",(0,i.kt)("inlineCode",{parentName:"p"},"sci")," plugin tests:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"it('throws an exception on missing functional unit data.', async () => {\n const inputs = [\n {\n timestamp: '2021-01-01T00:00:00Z',\n 'operational-carbon': 0.002,\n 'embodied-carbon': 0.0005,\n 'functional-unit': 'requests',\n duration: 1,\n },\n ];\n expect.assertions(1);\n\n try {\n await sciModel.execute(inputs);\n } catch (error) {\n expect(error).toBeInstanceOf(InputValidationError);\n }\n});\n")),(0,i.kt)("p",null,"It is also necessary to include ",(0,i.kt)("inlineCode",{parentName:"p"},"expect.assertions(n)")," for testing asynchronous code, where ",(0,i.kt)("inlineCode",{parentName:"p"},"n")," is the number of assertiosn that should be tested before the test completes. "),(0,i.kt)("h2",{id:"mocks"},"Mocks"),(0,i.kt)("p",null,"Please try to avoid mocking data if possible. However, if it is necessary to mock (e.g. if your plugin relies on a third party credentialed API) then please make your mock data as realistic as possible (no ",(0,i.kt)("inlineCode",{parentName:"p"},"foo"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"bar"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"baz")," style mock data, please)."),(0,i.kt)("p",null,"We do have mock backends in several of our tests, and we also have a mock data generator plugin that can create realistic dummy data to your specific requirements."),(0,i.kt)("h2",{id:"coverage"},"Coverage"),(0,i.kt)("p",null,"Please use ",(0,i.kt)("inlineCode",{parentName:"p"},"jest --coverage")," to see a coverage report for your plugin - your unit tests should yield 100% coverage. The snippet below shows what to expect from the coverage report:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"-------------------------------|---------|----------|---------|---------|-------------------\n| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |\n| --------------------------- | ------- | -------- | ------- | ------- | ----------------- |\n| All files | 100 | 100 | 100 | 100 |\n| lib | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n| lib/cloud-metadata | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n| lib/e-mem | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n| lib/e-net | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/93f138be.dcabecf0.js b/assets/js/93f138be.dcabecf0.js deleted file mode 100644 index 97b3a1a7..00000000 --- a/assets/js/93f138be.dcabecf0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[713],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=r.createContext({}),p=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=p(e.components);return r.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=p(n),m=i,f=d["".concat(l,".").concat(m)]||d[m]||c[m]||o;return n?r.createElement(f,a(a({ref:t},u),{},{components:n})):r.createElement(f,a({ref:t},u))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,a=new Array(o);a[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,a[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>c,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var r=n(7462),i=(n(7294),n(4137));const o={"sidebar-position":3},a="How to write unit tests",s={unversionedId:"developers/how-to-write-unit-tests",id:"developers/how-to-write-unit-tests",title:"How to write unit tests",description:"Impact Framework unit tests follow a standard format. We use the jest testing library. You can run all our existing tests by opening the project directory and running npm test. This page explains how you can add new unit tests for your plugins (or add some for our plugins if you notice a gap).",source:"@site/docs/developers/how-to-write-unit-tests.md",sourceDirName:"developers",slug:"/developers/how-to-write-unit-tests",permalink:"/developers/how-to-write-unit-tests",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/developers/how-to-write-unit-tests.md",tags:[],version:"current",frontMatter:{"sidebar-position":3},sidebar:"tutorialSidebar",previous:{title:"How to visualize results",permalink:"/developers/how-to-visualize-results"},next:{title:"Reference",permalink:"/reference/"}},l={},p=[{value:"Test files",id:"test-files",level:2},{value:"Setting up your test file",id:"setting-up-your-test-file",level:2},{value:"Describe",id:"describe",level:2},{value:"It",id:"it",level:2},{value:"Errors",id:"errors",level:2},{value:"Mocks",id:"mocks",level:2},{value:"Coverage",id:"coverage",level:2}],u={toc:p};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"how-to-write-unit-tests"},"How to write unit tests"),(0,i.kt)("p",null,"Impact Framework unit tests follow a standard format. We use the ",(0,i.kt)("inlineCode",{parentName:"p"},"jest")," testing library. You can run all our existing tests by opening the project directory and running ",(0,i.kt)("inlineCode",{parentName:"p"},"npm test"),". This page explains how you can add new unit tests for your plugins (or add some for our plugins if you notice a gap)."),(0,i.kt)("h2",{id:"test-files"},"Test files"),(0,i.kt)("p",null,"Both the IF and the project repositories include a ",(0,i.kt)("inlineCode",{parentName:"p"},"__test__")," directory. Inside, you will find subdirectory ",(0,i.kt)("inlineCode",{parentName:"p"},"unit/lib")," containing directories for each plugin. Your plugin repository should also follow this structure. Inside the plugin directory you can add ",(0,i.kt)("inlineCode",{parentName:"p"},"index.test.ts"),". This is where you write your unit tests. For example, here's the directory tree for our ",(0,i.kt)("inlineCode",{parentName:"p"},"teads-curve")," test file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"\nif-unofficial-plugins\n |\n |- src\n |\n |-__tests__\n |\n |-unit\n |\n |-lib\n |\n teads-curve\n |\n |- index.test.ts\n")),(0,i.kt)("h2",{id:"setting-up-your-test-file"},"Setting up your test file"),(0,i.kt)("p",null,"You will need to import your plugin so that it can be instantiated and tested. You will also need some elements from ",(0,i.kt)("inlineCode",{parentName:"p"},"jest/globals"),":\nFor example, these are the imports for our ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"import {Sum} from '../../../../lib';\nimport {ERRORS} from '../../../../util/errors';\nconst {InputValidationError} = ERRORS;\n")),(0,i.kt)("p",null,"You may require other imports for your specific set of tests."),(0,i.kt)("h2",{id:"describe"},"Describe"),(0,i.kt)("p",null,"Each method should have its own dedicated ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block. "),(0,i.kt)("p",null,"Your unit tests should have ",(0,i.kt)("em",{parentName:"p"},"at least")," two ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," blocks, one to test the plugin initialization and one for ",(0,i.kt)("inlineCode",{parentName:"p"},"execute"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'describe("init", ()=> {})\ndescribe("execute", ()=> {})\n')),(0,i.kt)("p",null,"For example, here is a describe block checking that the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin initializes correctly:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"describe('lib/sum: ', () => {\n describe('Sum: ', () => {\n const globalConfig = {\n 'input-parameters': ['cpu/energy', 'network/energy', 'memory/energy'],\n 'output-parameter': 'energy',\n };\n const sum = Sum(globalConfig);\n\n describe('init: ', () => {\n it('successfully initalized.', () => {\n expect(sum).toHaveProperty('metadata');\n expect(sum).toHaveProperty('execute');\n });\n });\n })\n})\n")),(0,i.kt)("h2",{id:"it"},"It"),(0,i.kt)("p",null,"Within each ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block, each effect to be tested should have a dedicated ",(0,i.kt)("inlineCode",{parentName:"p"},"it")," block."),(0,i.kt)("p",null,"Here's an example of a new ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block for the ",(0,i.kt)("inlineCode",{parentName:"p"},"execute()")," method on the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin. The ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block indicates that we are testing effects of the ",(0,i.kt)("inlineCode",{parentName:"p"},"execute()")," method. ",(0,i.kt)("inlineCode",{parentName:"p"},"it")," is specific to a single outcome - in this case there are two ",(0,i.kt)("inlineCode",{parentName:"p"},"it")," blocks that test that the plugin returns a specific result in the happy path and throws an exception if the user has provided invalid config data, specifically that the user-provided ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," parameter is missing:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"}," describe('execute(): ', () => {\n it('successfully applies Sum strategy to given input.', async () => {\n expect.assertions(1);\n\n const expectedResult = [\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n energy: 3,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ];\n\n const result = await sum.execute([\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ]);\n\n expect(result).toStrictEqual(expectedResult);\n });\n\n it('throws an error on missing params in input.', async () => {\n const expectedMessage =\n 'Sum: cpu/energy is missing from the input array.';\n\n expect.assertions(1);\n\n try {\n await sum.execute([\n {\n duration: 3600,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ]);\n } catch (error) {\n expect(error).toStrictEqual(\n new InputValidationError(expectedMessage)\n );\n }\n });\n })\n")),(0,i.kt)("h2",{id:"errors"},"Errors"),(0,i.kt)("p",null,"We prefer to use ",(0,i.kt)("inlineCode",{parentName:"p"},"expect")," to check the errors returned from a test. We do this by writing ",(0,i.kt)("inlineCode",{parentName:"p"},"expect")," in a ",(0,i.kt)("inlineCode",{parentName:"p"},"catch")," block. Here's an example from our ",(0,i.kt)("inlineCode",{parentName:"p"},"sci")," plugin tests:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"it('throws an exception on missing functional unit data.', async () => {\n const inputs = [\n {\n timestamp: '2021-01-01T00:00:00Z',\n 'operational-carbon': 0.002,\n 'embodied-carbon': 0.0005,\n 'functional-unit': 'requests',\n duration: 1,\n },\n ];\n expect.assertions(1);\n\n try {\n await sciModel.execute(inputs);\n } catch (error) {\n expect(error).toBeInstanceOf(InputValidationError);\n }\n});\n")),(0,i.kt)("p",null,"It is also necessary to include ",(0,i.kt)("inlineCode",{parentName:"p"},"expect.assertions(n)")," for testing asynchronous code, where ",(0,i.kt)("inlineCode",{parentName:"p"},"n")," is the number of assertiosn that should be tested before the test completes. "),(0,i.kt)("h2",{id:"mocks"},"Mocks"),(0,i.kt)("p",null,"Please try to avoid mocking data if possible. However, if it is necessary to mock (e.g. if your plugin relies on a third party credentialed API) then please make your mock data as realistic as possible (no ",(0,i.kt)("inlineCode",{parentName:"p"},"foo"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"bar"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"baz")," style mock data, please)."),(0,i.kt)("p",null,"We do have mock backends in several of our tests, and we also have a mock data generator plugin that can create realistic dummy data to your specific requirements."),(0,i.kt)("h2",{id:"coverage"},"Coverage"),(0,i.kt)("p",null,"Please use ",(0,i.kt)("inlineCode",{parentName:"p"},"jest --coverage")," to see a coverage report for your plugin - your unit tests should yield 100% coverage. The snippet below shows what to expect from the coverage report:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"-------------------------------|---------|----------|---------|---------|-------------------\n| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |\n| --------------------------- | ------- | -------- | ------- | ------- | ----------------- |\n| All files | 100 | 100 | 100 | 100 |\n| lib | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n| lib/cloud-metadata | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n| lib/e-mem | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n| lib/e-net | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/a4954f21.34a05832.js b/assets/js/a4954f21.34a05832.js deleted file mode 100644 index dd085799..00000000 --- a/assets/js/a4954f21.34a05832.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[672],{4137:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>d});var n=a(7294);function o(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function i(e){for(var t=1;t=0||(o[a]=e[a]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(o[a]=e[a])}return o}var l=n.createContext({}),p=function(e){var t=n.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},c=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var a=e.components,o=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),u=p(a),d=o,h=u["".concat(l,".").concat(d)]||u[d]||m[d]||r;return a?n.createElement(h,i(i({ref:t},c),{},{components:a})):n.createElement(h,i({ref:t},c))}));function d(e,t){var a=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=a.length,i=new Array(r);i[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:o,i[1]=s;for(var p=2;p{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>m,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var n=a(7462),o=(a(7294),a(4137));const r={sidebar_position:1},i="Design philosophy",s={unversionedId:"major-concepts/design-philosophy",id:"major-concepts/design-philosophy",title:"Design philosophy",description:"Transparency",source:"@site/docs/major-concepts/design-philosophy.md",sourceDirName:"major-concepts",slug:"/major-concepts/design-philosophy",permalink:"/major-concepts/design-philosophy",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/major-concepts/design-philosophy.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Major Concepts",permalink:"/major-concepts/"},next:{title:"Aggregation",permalink:"/major-concepts/aggregation"}},l={},p=[{value:"Transparency",id:"transparency",level:2},{value:"Verifiability",id:"verifiability",level:2},{value:"Modularity",id:"modularity",level:2},{value:"Neutrality",id:"neutrality",level:2},{value:"Where to go next",id:"where-to-go-next",level:2}],c={toc:p};function m(e){let{components:t,...a}=e;return(0,o.kt)("wrapper",(0,n.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"design-philosophy"},"Design philosophy"),(0,o.kt)("h2",{id:"transparency"},"Transparency"),(0,o.kt)("p",null,"The ",(0,o.kt)("em",{parentName:"p"},"manifest file")," is the lifeblood of Impact Framework. It defines all the context for an environmental impact calculation, defining the architecture of an application, the observation period, the pipeline of calculations and transformations to execute, and the environmental impacts to track. This document can then be ",(0,o.kt)("em",{parentName:"p"},"executed")," to generate impact values. This gives unparalleled transparency to an environmental audit, all in a standard, easy to read format that anyone can read or re-execute."),(0,o.kt)("h2",{id:"verifiability"},"Verifiability"),(0,o.kt)("p",null,"An Impact Framework manifest file is powerful because anyone can re-execute it and verify an organization's impact calculation. You can even experiment by swapping out different plugins. The critical concept is that ",(0,o.kt)("em",{parentName:"p"},"everything")," you need to calculate an impact is provided in the manifest file and ",(0,o.kt)("em",{parentName:"p"},"anyone")," can re-run a calculation with the manifest file and the lightweight Impact Framework command line tool."),(0,o.kt)("h1",{id:"flexibility"},"Flexibility"),(0,o.kt)("p",null,"We aim to bake the minimum of constraints into the Imapct Framework, balancing the helpers and standards that make plugins interoperable and consistent against freedom of expression and creativity. The real power of Impact Framework comes from the community. This includes the community of experts contributing to the design decisions and standards baked into the protocol and the community of plugin developers experimenting at the margins, and the organizations using Impact Framework to measure, report and mitigate their environmental imapct. "),(0,o.kt)("p",null,"Impact Framework can be a tool for transparent, verifiable environmental impact audits, but it can also be a platform for experimentation. Your manifest file is a foundation for forecasting into the future or exploring where you can tweak your stack to most effectively minimize your impact. Impact Framework can be a tool for research, hypothesis testing, R&D and business decision making as well as environemntal reporting. To realize this vision, we know we have to make Impact Framework as flexible as possible, imposing the absolute minimum of constraints in the underlying protocol, while also providing the necessary functionality and safeguards our users require."),(0,o.kt)("h2",{id:"modularity"},"Modularity"),(0,o.kt)("p",null,"Impact Framework is the minimal set of features that enable a manifest file to be processed according to some agreed principles. We provide a tool for processing manifest files and a set of standards and norms. This allows builders to create plugins that do some specific task, such as grabbing data from a particular cloud provider, or applying some calculation over some particular data."),(0,o.kt)("p",null,"Anyone can build a plugin and share them with the world, meaning Impact Framework development can be bottom-up and community driven. It also means that if you are not satisfied with how some calculation was done, you can easily fork it and replace it with your own."),(0,o.kt)("p",null,"What we provide is a minimal set of rules and guardrails for plugin builders to conform to to ensure compatibility with Impact Framework."),(0,o.kt)("h2",{id:"neutrality"},"Neutrality"),(0,o.kt)("p",null,"Impact Framework aims to support maximally decentralized plugin development. We want anyone to be able to build plugins and use them to calculate their environmental impacts. We do not want to gatekeep what people can measure and monitor - we want to encourage people to build freely and experiment on Impact Framework rails! "),(0,o.kt)("p",null,"At the same time, we want to provide the helpers and guardrails that make impact calculations as friction free as possible. This means we focus on providing the minimal ",(0,o.kt)("em",{parentName:"p"},"protocol")," required to support community plugin development and make it as safe as possible from unit errors and other footguns. "),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},(0,o.kt)("em",{parentName:"strong"},"We want to see the universe of Impact Framework plugins grow organically and permissionlessly in ways we can't even imagine today!"))),(0,o.kt)("p",null,"To this end, what we are really building is a protocol. Impact Framework is just a Typescript implementation of the protocol. The protocol itself is a set of fundamental principles that define how a manifest file should be processed, such that any implementation in any language will yield the same result from a given manifest file. "),(0,o.kt)("p",null,"The Impact Protocol is the result of countless discussions, experiments, conversations with industry partners, academics, researchers and developers, and represents a community consensus for how certain actions should be executed, for example, how should a series of observations be aggregated, what standard units should be used, how should a manifest file be structured, etc."),(0,o.kt)("p",null,"This means we can be neutral about what can be built with IF while also providing a set of canonical processes and standards."),(0,o.kt)("h2",{id:"where-to-go-next"},"Where to go next"),(0,o.kt)("p",null,"This page has outlined the design philosophies that guide Impact Framework development. "),(0,o.kt)("p",null,"Explore the other pages in this section to see how these principles have been applied to specific Impact Framework features, or head to our ",(0,o.kt)("a",{parentName:"p",href:"../users/"},"user documentation")," to get started running Impact Framework for yourself."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/a4954f21.97c940d4.js b/assets/js/a4954f21.97c940d4.js new file mode 100644 index 00000000..99f75aa7 --- /dev/null +++ b/assets/js/a4954f21.97c940d4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[672],{4137:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>d});var n=a(7294);function o(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function i(e){for(var t=1;t=0||(o[a]=e[a]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(o[a]=e[a])}return o}var l=n.createContext({}),p=function(e){var t=n.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},c=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var a=e.components,o=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),u=p(a),d=o,h=u["".concat(l,".").concat(d)]||u[d]||m[d]||r;return a?n.createElement(h,i(i({ref:t},c),{},{components:a})):n.createElement(h,i({ref:t},c))}));function d(e,t){var a=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=a.length,i=new Array(r);i[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:o,i[1]=s;for(var p=2;p{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>m,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var n=a(7462),o=(a(7294),a(4137));const r={sidebar_position:1},i="Design philosophy",s={unversionedId:"major-concepts/design-philosophy",id:"major-concepts/design-philosophy",title:"Design philosophy",description:"Transparency",source:"@site/docs/major-concepts/design-philosophy.md",sourceDirName:"major-concepts",slug:"/major-concepts/design-philosophy",permalink:"/major-concepts/design-philosophy",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/design-philosophy.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Major Concepts",permalink:"/major-concepts/"},next:{title:"Aggregation",permalink:"/major-concepts/aggregation"}},l={},p=[{value:"Transparency",id:"transparency",level:2},{value:"Verifiability",id:"verifiability",level:2},{value:"Modularity",id:"modularity",level:2},{value:"Neutrality",id:"neutrality",level:2},{value:"Where to go next",id:"where-to-go-next",level:2}],c={toc:p};function m(e){let{components:t,...a}=e;return(0,o.kt)("wrapper",(0,n.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"design-philosophy"},"Design philosophy"),(0,o.kt)("h2",{id:"transparency"},"Transparency"),(0,o.kt)("p",null,"The ",(0,o.kt)("em",{parentName:"p"},"manifest file")," is the lifeblood of Impact Framework. It defines all the context for an environmental impact calculation, defining the architecture of an application, the observation period, the pipeline of calculations and transformations to execute, and the environmental impacts to track. This document can then be ",(0,o.kt)("em",{parentName:"p"},"executed")," to generate impact values. This gives unparalleled transparency to an environmental audit, all in a standard, easy to read format that anyone can read or re-execute."),(0,o.kt)("h2",{id:"verifiability"},"Verifiability"),(0,o.kt)("p",null,"An Impact Framework manifest file is powerful because anyone can re-execute it and verify an organization's impact calculation. You can even experiment by swapping out different plugins. The critical concept is that ",(0,o.kt)("em",{parentName:"p"},"everything")," you need to calculate an impact is provided in the manifest file and ",(0,o.kt)("em",{parentName:"p"},"anyone")," can re-run a calculation with the manifest file and the lightweight Impact Framework command line tool."),(0,o.kt)("h1",{id:"flexibility"},"Flexibility"),(0,o.kt)("p",null,"We aim to bake the minimum of constraints into the Imapct Framework, balancing the helpers and standards that make plugins interoperable and consistent against freedom of expression and creativity. The real power of Impact Framework comes from the community. This includes the community of experts contributing to the design decisions and standards baked into the protocol and the community of plugin developers experimenting at the margins, and the organizations using Impact Framework to measure, report and mitigate their environmental imapct. "),(0,o.kt)("p",null,"Impact Framework can be a tool for transparent, verifiable environmental impact audits, but it can also be a platform for experimentation. Your manifest file is a foundation for forecasting into the future or exploring where you can tweak your stack to most effectively minimize your impact. Impact Framework can be a tool for research, hypothesis testing, R&D and business decision making as well as environemntal reporting. To realize this vision, we know we have to make Impact Framework as flexible as possible, imposing the absolute minimum of constraints in the underlying protocol, while also providing the necessary functionality and safeguards our users require."),(0,o.kt)("h2",{id:"modularity"},"Modularity"),(0,o.kt)("p",null,"Impact Framework is the minimal set of features that enable a manifest file to be processed according to some agreed principles. We provide a tool for processing manifest files and a set of standards and norms. This allows builders to create plugins that do some specific task, such as grabbing data from a particular cloud provider, or applying some calculation over some particular data."),(0,o.kt)("p",null,"Anyone can build a plugin and share them with the world, meaning Impact Framework development can be bottom-up and community driven. It also means that if you are not satisfied with how some calculation was done, you can easily fork it and replace it with your own."),(0,o.kt)("p",null,"What we provide is a minimal set of rules and guardrails for plugin builders to conform to to ensure compatibility with Impact Framework."),(0,o.kt)("h2",{id:"neutrality"},"Neutrality"),(0,o.kt)("p",null,"Impact Framework aims to support maximally decentralized plugin development. We want anyone to be able to build plugins and use them to calculate their environmental impacts. We do not want to gatekeep what people can measure and monitor - we want to encourage people to build freely and experiment on Impact Framework rails! "),(0,o.kt)("p",null,"At the same time, we want to provide the helpers and guardrails that make impact calculations as friction free as possible. This means we focus on providing the minimal ",(0,o.kt)("em",{parentName:"p"},"protocol")," required to support community plugin development and make it as safe as possible from unit errors and other footguns. "),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},(0,o.kt)("em",{parentName:"strong"},"We want to see the universe of Impact Framework plugins grow organically and permissionlessly in ways we can't even imagine today!"))),(0,o.kt)("p",null,"To this end, what we are really building is a protocol. Impact Framework is just a Typescript implementation of the protocol. The protocol itself is a set of fundamental principles that define how a manifest file should be processed, such that any implementation in any language will yield the same result from a given manifest file. "),(0,o.kt)("p",null,"The Impact Protocol is the result of countless discussions, experiments, conversations with industry partners, academics, researchers and developers, and represents a community consensus for how certain actions should be executed, for example, how should a series of observations be aggregated, what standard units should be used, how should a manifest file be structured, etc."),(0,o.kt)("p",null,"This means we can be neutral about what can be built with IF while also providing a set of canonical processes and standards."),(0,o.kt)("h2",{id:"where-to-go-next"},"Where to go next"),(0,o.kt)("p",null,"This page has outlined the design philosophies that guide Impact Framework development. "),(0,o.kt)("p",null,"Explore the other pages in this section to see how these principles have been applied to specific Impact Framework features, or head to our ",(0,o.kt)("a",{parentName:"p",href:"../users/"},"user documentation")," to get started running Impact Framework for yourself."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ad895e75.6ddd513d.js b/assets/js/ad895e75.6ddd513d.js deleted file mode 100644 index 5624796c..00000000 --- a/assets/js/ad895e75.6ddd513d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[288],{4137:(e,t,o)=>{o.d(t,{Zo:()=>d,kt:()=>h});var a=o(7294);function r(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function n(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,a)}return o}function i(e){for(var t=1;t=0||(r[o]=e[o]);return r}(e,t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(r[o]=e[o])}return r}var l=a.createContext({}),u=function(e){var t=a.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):i(i({},t),e)),o},d=function(e){var t=u(e.components);return a.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var o=e.components,r=e.mdxType,n=e.originalType,l=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),c=u(o),h=r,f=c["".concat(l,".").concat(h)]||c[h]||p[h]||n;return o?a.createElement(f,i(i({ref:t},d),{},{components:o})):a.createElement(f,i({ref:t},d))}));function h(e,t){var o=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var n=o.length,i=new Array(n);i[0]=c;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,i[1]=s;for(var u=2;u{o.r(t),o.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>p,frontMatter:()=>n,metadata:()=>s,toc:()=>u});var a=o(7462),r=(o(7294),o(4137));const n={"sidebar-position":7},i="FAQs",s={unversionedId:"FAQ",id:"FAQ",title:"FAQs",description:"Is there any way to auto-generate manifest files?",source:"@site/docs/FAQ.md",sourceDirName:".",slug:"/FAQ",permalink:"/FAQ",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/FAQ.md",tags:[],version:"current",frontMatter:{"sidebar-position":7},sidebar:"tutorialSidebar",previous:{title:"Plugins",permalink:"/reference/plugins"},next:{title:"Contribution Guidelines",permalink:"/contributions"}},l={},u=[{value:"Is there any way to auto-generate manifest files?",id:"is-there-any-way-to-auto-generate-manifest-files",level:2},{value:"What is your vision for the IF?",id:"what-is-your-vision-for-the-if",level:2},{value:"Is there a way to generate an audit/report along with a csv/yaml output?",id:"is-there-a-way-to-generate-an-auditreport-along-with-a-csvyaml-output",level:2},{value:"The manifest approach makes things transparent, but you can still manipulate the data you put as inputs in the manifest. How to fight again this?",id:"the-manifest-approach-makes-things-transparent-but-you-can-still-manipulate-the-data-you-put-as-inputs-in-the-manifest-how-to-fight-again-this",level:2},{value:"Is it planned to make the IF more user friendly or also more usable for non it people?",id:"is-it-planned-to-make-the-if-more-user-friendly-or-also-more-usable-for-non-it-people",level:2},{value:"Does it only calculate emissions for CPU usage or does it also work with other meters like storage/bandwith?",id:"does-it-only-calculate-emissions-for-cpu-usage-or-does-it-also-work-with-other-meters-like-storagebandwith",level:2},{value:"Is the cloud-metadata only for public cloud providers?",id:"is-the-cloud-metadata-only-for-public-cloud-providers",level:2},{value:"Have you compared the results to the carbon reports that cloud providers are producing for their customers?",id:"have-you-compared-the-results-to-the-carbon-reports-that-cloud-providers-are-producing-for-their-customers",level:2},{value:"Are there plans to include other pieces of cloud metadata beyond servers e.g. for PaaS services?",id:"are-there-plans-to-include-other-pieces-of-cloud-metadata-beyond-servers-eg-for-paas-services",level:2},{value:"Is there a cloud-metadata for VMware and/or Openstack ?",id:"is-there-a-cloud-metadata-for-vmware-andor-openstack-",level:2},{value:"Will IF be developed beyond the upcoming hackathon?",id:"will-if-be-developed-beyond-the-upcoming-hackathon",level:2}],d={toc:u};function p(e){let{components:t,...o}=e;return(0,r.kt)("wrapper",(0,a.Z)({},d,o,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"faqs"},"FAQs"),(0,r.kt)("h2",{id:"is-there-any-way-to-auto-generate-manifest-files"},"Is there any way to auto-generate manifest files?"),(0,r.kt)("p",null,"You have to create your basic structure but it's expected that your input data will be fille dusing an importer plugin (we have one for Azure VMs and there will be mroe after the hackathon). For testing and experimenting we also have a MockObservations plugin that can autofill manifests with dummy data"),(0,r.kt)("h2",{id:"what-is-your-vision-for-the-if"},"What is your vision for the IF?"),(0,r.kt)("p",null,"The vision is to be a protocol that enables you to use IF for any and all of those use-cases, and more. "),(0,r.kt)("p",null,"Right now we focus on software applications running in the cloud, but we have people using it on-premise and even for supply chain modeling. The idea is that we provide the minimal infrastructure required for you to build up different use cases using plugins."),(0,r.kt)("h2",{id:"is-there-a-way-to-generate-an-auditreport-along-with-a-csvyaml-output"},"Is there a way to generate an audit/report along with a csv/yaml output?"),(0,r.kt)("p",null,'No - we see the manifest file and associated manifest as a new type of "executable audit". The IF itself does not generate any other form of report, although there is nothing stopping others from building out this functionality on top of IF.'),(0,r.kt)("h2",{id:"the-manifest-approach-makes-things-transparent-but-you-can-still-manipulate-the-data-you-put-as-inputs-in-the-manifest-how-to-fight-again-this"},"The manifest approach makes things transparent, but you can still manipulate the data you put as inputs in the manifest. How to fight again this?"),(0,r.kt)("p",null,"We can't really stop people inputting fake data into a manifest file. But we're very interested in ways we can verify that the computation was done correctly and provide public proofs. Don't expect anything imminently, but we are thinking along these lines for the future."),(0,r.kt)("h2",{id:"is-it-planned-to-make-the-if-more-user-friendly-or-also-more-usable-for-non-it-people"},"Is it planned to make the IF more user friendly or also more usable for non it people?"),(0,r.kt)("p",null,"IF is a low level infrastructure project. the core team focuses on building out solid foundations for others to build UIs, apps etc on top. There is no official IF UX tooling."),(0,r.kt)("h2",{id:"does-it-only-calculate-emissions-for-cpu-usage-or-does-it-also-work-with-other-meters-like-storagebandwith"},"Does it only calculate emissions for CPU usage or does it also work with other meters like storage/bandwith?"),(0,r.kt)("p",null,"We do have basic models for memory and network usage in addition to CPU. You can create plugins for anything you can observe and model."),(0,r.kt)("h2",{id:"is-the-cloud-metadata-only-for-public-cloud-providers"},"Is the cloud-metadata only for public cloud providers?"),(0,r.kt)("p",null,"Yes it is - it's based on a database we maintain as part of the IF repository. You could extend it by adding the same data for your on premise resource."),(0,r.kt)("h2",{id:"have-you-compared-the-results-to-the-carbon-reports-that-cloud-providers-are-producing-for-their-customers"},"Have you compared the results to the carbon reports that cloud providers are producing for their customers?"),(0,r.kt)("p",null,"No we haven't - we've been focussing on capturing impacts of individual applications over relatively short time periods so far. It's difficult to find the underlying data to build the comparisons."),(0,r.kt)("h2",{id:"are-there-plans-to-include-other-pieces-of-cloud-metadata-beyond-servers-eg-for-paas-services"},"Are there plans to include other pieces of cloud metadata beyond servers e.g. for PaaS services?"),(0,r.kt)("p",null,"We don't have immediate specific plans to build this as the IF core team, but we're enthusiastic about teams building plugins for this to support more use-cases"),(0,r.kt)("h2",{id:"is-there-a-cloud-metadata-for-vmware-andor-openstack-"},"Is there a cloud-metadata for VMware and/or Openstack ?"),(0,r.kt)("p",null,"No there isn't! It would be a nice addition."),(0,r.kt)("h2",{id:"will-if-be-developed-beyond-the-upcoming-hackathon"},"Will IF be developed beyond the upcoming hackathon?"),(0,r.kt)("p",null,"yes! we are very passionate about this project and it is only going to grow from here, far beyond the hackathon!"))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ad895e75.8c6cb83b.js b/assets/js/ad895e75.8c6cb83b.js new file mode 100644 index 00000000..4cceee92 --- /dev/null +++ b/assets/js/ad895e75.8c6cb83b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[288],{4137:(e,t,o)=>{o.d(t,{Zo:()=>d,kt:()=>h});var a=o(7294);function r(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function n(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,a)}return o}function i(e){for(var t=1;t=0||(r[o]=e[o]);return r}(e,t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(r[o]=e[o])}return r}var l=a.createContext({}),u=function(e){var t=a.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):i(i({},t),e)),o},d=function(e){var t=u(e.components);return a.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var o=e.components,r=e.mdxType,n=e.originalType,l=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),c=u(o),h=r,f=c["".concat(l,".").concat(h)]||c[h]||p[h]||n;return o?a.createElement(f,i(i({ref:t},d),{},{components:o})):a.createElement(f,i({ref:t},d))}));function h(e,t){var o=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var n=o.length,i=new Array(n);i[0]=c;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,i[1]=s;for(var u=2;u{o.r(t),o.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>p,frontMatter:()=>n,metadata:()=>s,toc:()=>u});var a=o(7462),r=(o(7294),o(4137));const n={"sidebar-position":7},i="FAQs",s={unversionedId:"FAQ",id:"FAQ",title:"FAQs",description:"Is there any way to auto-generate manifest files?",source:"@site/docs/FAQ.md",sourceDirName:".",slug:"/FAQ",permalink:"/FAQ",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/FAQ.md",tags:[],version:"current",frontMatter:{"sidebar-position":7},sidebar:"tutorialSidebar",previous:{title:"Plugins",permalink:"/reference/plugins"},next:{title:"Contribution Guidelines",permalink:"/contributions"}},l={},u=[{value:"Is there any way to auto-generate manifest files?",id:"is-there-any-way-to-auto-generate-manifest-files",level:2},{value:"What is your vision for the IF?",id:"what-is-your-vision-for-the-if",level:2},{value:"Is there a way to generate an audit/report along with a csv/yaml output?",id:"is-there-a-way-to-generate-an-auditreport-along-with-a-csvyaml-output",level:2},{value:"The manifest approach makes things transparent, but you can still manipulate the data you put as inputs in the manifest. How to fight again this?",id:"the-manifest-approach-makes-things-transparent-but-you-can-still-manipulate-the-data-you-put-as-inputs-in-the-manifest-how-to-fight-again-this",level:2},{value:"Is it planned to make the IF more user friendly or also more usable for non it people?",id:"is-it-planned-to-make-the-if-more-user-friendly-or-also-more-usable-for-non-it-people",level:2},{value:"Does it only calculate emissions for CPU usage or does it also work with other meters like storage/bandwith?",id:"does-it-only-calculate-emissions-for-cpu-usage-or-does-it-also-work-with-other-meters-like-storagebandwith",level:2},{value:"Is the cloud-metadata only for public cloud providers?",id:"is-the-cloud-metadata-only-for-public-cloud-providers",level:2},{value:"Have you compared the results to the carbon reports that cloud providers are producing for their customers?",id:"have-you-compared-the-results-to-the-carbon-reports-that-cloud-providers-are-producing-for-their-customers",level:2},{value:"Are there plans to include other pieces of cloud metadata beyond servers e.g. for PaaS services?",id:"are-there-plans-to-include-other-pieces-of-cloud-metadata-beyond-servers-eg-for-paas-services",level:2},{value:"Is there a cloud-metadata for VMware and/or Openstack ?",id:"is-there-a-cloud-metadata-for-vmware-andor-openstack-",level:2},{value:"Will IF be developed beyond the upcoming hackathon?",id:"will-if-be-developed-beyond-the-upcoming-hackathon",level:2}],d={toc:u};function p(e){let{components:t,...o}=e;return(0,r.kt)("wrapper",(0,a.Z)({},d,o,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"faqs"},"FAQs"),(0,r.kt)("h2",{id:"is-there-any-way-to-auto-generate-manifest-files"},"Is there any way to auto-generate manifest files?"),(0,r.kt)("p",null,"You have to create your basic structure but it's expected that your input data will be fille dusing an importer plugin (we have one for Azure VMs and there will be mroe after the hackathon). For testing and experimenting we also have a MockObservations plugin that can autofill manifests with dummy data"),(0,r.kt)("h2",{id:"what-is-your-vision-for-the-if"},"What is your vision for the IF?"),(0,r.kt)("p",null,"The vision is to be a protocol that enables you to use IF for any and all of those use-cases, and more. "),(0,r.kt)("p",null,"Right now we focus on software applications running in the cloud, but we have people using it on-premise and even for supply chain modeling. The idea is that we provide the minimal infrastructure required for you to build up different use cases using plugins."),(0,r.kt)("h2",{id:"is-there-a-way-to-generate-an-auditreport-along-with-a-csvyaml-output"},"Is there a way to generate an audit/report along with a csv/yaml output?"),(0,r.kt)("p",null,'No - we see the manifest file and associated manifest as a new type of "executable audit". The IF itself does not generate any other form of report, although there is nothing stopping others from building out this functionality on top of IF.'),(0,r.kt)("h2",{id:"the-manifest-approach-makes-things-transparent-but-you-can-still-manipulate-the-data-you-put-as-inputs-in-the-manifest-how-to-fight-again-this"},"The manifest approach makes things transparent, but you can still manipulate the data you put as inputs in the manifest. How to fight again this?"),(0,r.kt)("p",null,"We can't really stop people inputting fake data into a manifest file. But we're very interested in ways we can verify that the computation was done correctly and provide public proofs. Don't expect anything imminently, but we are thinking along these lines for the future."),(0,r.kt)("h2",{id:"is-it-planned-to-make-the-if-more-user-friendly-or-also-more-usable-for-non-it-people"},"Is it planned to make the IF more user friendly or also more usable for non it people?"),(0,r.kt)("p",null,"IF is a low level infrastructure project. the core team focuses on building out solid foundations for others to build UIs, apps etc on top. There is no official IF UX tooling."),(0,r.kt)("h2",{id:"does-it-only-calculate-emissions-for-cpu-usage-or-does-it-also-work-with-other-meters-like-storagebandwith"},"Does it only calculate emissions for CPU usage or does it also work with other meters like storage/bandwith?"),(0,r.kt)("p",null,"We do have basic models for memory and network usage in addition to CPU. You can create plugins for anything you can observe and model."),(0,r.kt)("h2",{id:"is-the-cloud-metadata-only-for-public-cloud-providers"},"Is the cloud-metadata only for public cloud providers?"),(0,r.kt)("p",null,"Yes it is - it's based on a database we maintain as part of the IF repository. You could extend it by adding the same data for your on premise resource."),(0,r.kt)("h2",{id:"have-you-compared-the-results-to-the-carbon-reports-that-cloud-providers-are-producing-for-their-customers"},"Have you compared the results to the carbon reports that cloud providers are producing for their customers?"),(0,r.kt)("p",null,"No we haven't - we've been focussing on capturing impacts of individual applications over relatively short time periods so far. It's difficult to find the underlying data to build the comparisons."),(0,r.kt)("h2",{id:"are-there-plans-to-include-other-pieces-of-cloud-metadata-beyond-servers-eg-for-paas-services"},"Are there plans to include other pieces of cloud metadata beyond servers e.g. for PaaS services?"),(0,r.kt)("p",null,"We don't have immediate specific plans to build this as the IF core team, but we're enthusiastic about teams building plugins for this to support more use-cases"),(0,r.kt)("h2",{id:"is-there-a-cloud-metadata-for-vmware-andor-openstack-"},"Is there a cloud-metadata for VMware and/or Openstack ?"),(0,r.kt)("p",null,"No there isn't! It would be a nice addition."),(0,r.kt)("h2",{id:"will-if-be-developed-beyond-the-upcoming-hackathon"},"Will IF be developed beyond the upcoming hackathon?"),(0,r.kt)("p",null,"yes! we are very passionate about this project and it is only going to grow from here, far beyond the hackathon!"))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/b5ada7f5.9a0a6900.js b/assets/js/b5ada7f5.9a0a6900.js new file mode 100644 index 00000000..2e2aa43d --- /dev/null +++ b/assets/js/b5ada7f5.9a0a6900.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[491],{4137:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>d});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var g=a.createContext({}),l=function(e){var t=a.useContext(g),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=l(e.components);return a.createElement(g.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,g=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),c=l(n),d=r,u=c["".concat(g,".").concat(d)]||c[d]||m[d]||o;return n?a.createElement(u,i(i({ref:t},p),{},{components:n})):a.createElement(u,i({ref:t},p))}));function d(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=c;var s={};for(var g in t)hasOwnProperty.call(t,g)&&(s[g]=t[g]);s.originalType=e,s.mdxType="string"==typeof e?e:r,i[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>g,contentTitle:()=>i,default:()=>m,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var a=n(7462),r=(n(7294),n(4137));const o={sidebar_position:6},i="Aggregation",s={unversionedId:"major-concepts/aggregation",id:"major-concepts/aggregation",title:"Aggregation",description:"Aggregation is the process of summarizing a set of metrics.",source:"@site/docs/major-concepts/aggregation.md",sourceDirName:"major-concepts",slug:"/major-concepts/aggregation",permalink:"/major-concepts/aggregation",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/aggregation.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{sidebar_position:6},sidebar:"tutorialSidebar",previous:{title:"Design philosophy",permalink:"/major-concepts/design-philosophy"},next:{title:"Group-by",permalink:"/major-concepts/groupby"}},g={},l=[{value:"Configuration",id:"configuration",level:2},{value:"Aggregation methods",id:"aggregation-methods",level:2},{value:"Aggregation outputs",id:"aggregation-outputs",level:2}],p={toc:l};function m(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"aggregation"},"Aggregation"),(0,r.kt)("p",null,"Aggregation is the process of summarizing a set of metrics. "),(0,r.kt)("p",null,"Two types of aggregation can be executed in IF. The first takes a time series and condenses it down into a single number representing an entire observation period, for example, if your time series contains three timesteps, the value of the metric being aggregated is ",(0,r.kt)("inlineCode",{parentName:"p"},"1")," in each timestep and the aggregate is being computed as a sum, the aggregated value for the whole observation period is ",(0,r.kt)("inlineCode",{parentName:"p"},"3"),'. We refer to this as "time series aggregation" or "horizontal aggregation".'),(0,r.kt)("p",null,"The second type of aggregation happens across components in a tree. Where time series exist for multiple child nodes under a parent, their time series are aggregated together into one summary time series that is pushed to the parent node. For example, in the following tree, each child has a time series with three timesteps. At each timestep, the metric value is ",(0,r.kt)("inlineCode",{parentName:"p"},"1")," for both children:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"}," parent\n / \\\n / \\\nchild-1 child-2\ndata: [1,1,1] data: [1,1,1]\n")),(0,r.kt)("p",null,"Assuming the aggregation method is ",(0,r.kt)("inlineCode",{parentName:"p"},"sum"),", the parent would receive an aggregated time series ","[2,2,2]",', representing the aggregated values from both children. We refer to this as "tree aggregation" or "vertical aggregation".'),(0,r.kt)("h2",{id:"configuration"},"Configuration"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"Aggregate")," is a built-in feature of the IF. This means you do not need to initialize it along with the plugins you are using in your pipeline. All you need to do is to add a small piece of config to your manifest file."),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregate")," config looks as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},'aggregation:\n metrics:\n - "carbon"\n - "energy"\n type: "both"\n')),(0,r.kt)("p",null,"There are two fields: ",(0,r.kt)("inlineCode",{parentName:"p"},"metrics")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"type"),"."),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"metrics")," is an array of metrics that you want to aggregate. You can provide any value here, but they must match a key that exists in your output data (i.e. if you tell IF to aggregate ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," but ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," is not in your outputs you will receive an error message and aggregation will fail). You can provide any number of metrics. In the example above, the aggregation feature will operate on the ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"energy")," values."),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"type")," determines which kind of aggregation you want to perform. The choices are ",(0,r.kt)("inlineCode",{parentName:"p"},"horizontal")," (time-series aggregation only), ",(0,r.kt)("inlineCode",{parentName:"p"},"vertical")," (tree aggregation only) or ",(0,r.kt)("inlineCode",{parentName:"p"},"both")," (both kinds of aggregation will be performed). In the example above, both types of aggregation will be performed over the two selected metrics."),(0,r.kt)("h2",{id:"aggregation-methods"},"Aggregation methods"),(0,r.kt)("p",null,"Aggregation can happen in several ways. In the example above, both of the selected metrics are absolute values measured in gCO2eq (carbon) and kWh (energy). To aggregate, it makes sense to add the values in each timestep for time-series aggregation and element-wise across components for tree aggregation. However, summing the values is not always the right way to aggregate. "),(0,r.kt)("p",null,"For example, some values are constants that apply to every timestep - these should simply be copied into the aggregated metric and not summed. For example, if you are using a hard-coded value for grid carbon intensity that applies globally, then you simply want to persist that value into the aggregate - adding them together would provide a misleading result. "),(0,r.kt)("p",null,"Similarly, some values are proportions or percentages. In these cases, the right way to aggregate is usually to take an average rather than summing over a set of values. "),(0,r.kt)("p",null,"The decisions about how to aggregate are made on a case-by-case basis for each individual parameter. We track the aggregation method for each parameter in our ",(0,r.kt)("inlineCode",{parentName:"p"},"params.ts")," file, which contains the canonical set of parameters, their units and aggregation methods. You can append to the parameters in this file by providing ",(0,r.kt)("inlineCode",{parentName:"p"},"params")," data in your manifest file, or you can override our recommended set of parameters entirely by providing a new file to the ",(0,r.kt)("inlineCode",{parentName:"p"},"--override-params")," command in the CLI. In either case, you need to provide an ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregation")," method with your parameters so that IF can look up the right way to aggregate the values."),(0,r.kt)("h2",{id:"aggregation-outputs"},"Aggregation outputs"),(0,r.kt)("p",null,"The aggregation process adds new output data to your manifest file. The two types of aggregation add different outputs. The horizontal (time-series) aggregation adds a new field called ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregated")," to each node whose time series has been aggregated. In the ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregated")," block, you will find the aggregated value for each of the aggregation metrics defined in the aggregation config."),(0,r.kt)("p",null,"The vertical aggregation adds a new array of output observations. These are simply named ",(0,r.kt)("inlineCode",{parentName:"p"},"outputs")," and they always contain a timestamp and duration along with the aggregated metrics for each timestep. "),(0,r.kt)("p",null,"The example below shows the result of running both kinds of aggregation for a single component:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},' "outputs": [\n {\n "carbon": 0.04846481793320214,\n "energy": 0.00030285447154471535,\n "timestamp": "2023-12-12T00:00:00.000Z"\n },\n {\n "carbon": 0.037777724630840566,\n "energy": 0.00023606013840495548,\n "timestamp": "2023-12-12T00:00:05.000Z"\n },\n {\n "carbon": 0.03630388278027921,\n "energy": 0.000226848626838947,\n "timestamp": "2023-12-12T00:00:10.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:15.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:20.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:25.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:30.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:35.000Z"\n },\n ],\n "aggregated": {\n "carbon": 0.3246705689138855,\n "energy": 0.0020287555470867216\n }\n')))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/b5ada7f5.cf500660.js b/assets/js/b5ada7f5.cf500660.js deleted file mode 100644 index 40e28a43..00000000 --- a/assets/js/b5ada7f5.cf500660.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[491],{4137:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>u});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var g=a.createContext({}),l=function(e){var t=a.useContext(g),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=l(e.components);return a.createElement(g.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,g=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),c=l(n),u=r,d=c["".concat(g,".").concat(u)]||c[u]||m[u]||o;return n?a.createElement(d,i(i({ref:t},p),{},{components:n})):a.createElement(d,i({ref:t},p))}));function u(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=c;var s={};for(var g in t)hasOwnProperty.call(t,g)&&(s[g]=t[g]);s.originalType=e,s.mdxType="string"==typeof e?e:r,i[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>g,contentTitle:()=>i,default:()=>m,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var a=n(7462),r=(n(7294),n(4137));const o={sidebar_position:6},i="Aggregation",s={unversionedId:"major-concepts/aggregation",id:"major-concepts/aggregation",title:"Aggregation",description:"Aggregation is the process of summarizing a set of metrics.",source:"@site/docs/major-concepts/aggregation.md",sourceDirName:"major-concepts",slug:"/major-concepts/aggregation",permalink:"/major-concepts/aggregation",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/major-concepts/aggregation.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{sidebar_position:6},sidebar:"tutorialSidebar",previous:{title:"Design philosophy",permalink:"/major-concepts/design-philosophy"},next:{title:"Group-by",permalink:"/major-concepts/groupby"}},g={},l=[{value:"Configuration",id:"configuration",level:2},{value:"Aggregation methods",id:"aggregation-methods",level:2},{value:"Aggregation outputs",id:"aggregation-outputs",level:2}],p={toc:l};function m(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"aggregation"},"Aggregation"),(0,r.kt)("p",null,"Aggregation is the process of summarizing a set of metrics. "),(0,r.kt)("p",null,"Two types of aggregation can be executed in IF. The first takes a time series and condenses it down into a single number representing an entire observation period, for example, if your time series contains three timesteps, the value of the metric being aggregated is ",(0,r.kt)("inlineCode",{parentName:"p"},"1")," in each timestep and the aggregate is being computed as a sum, the aggregated value for the whole observation period is ",(0,r.kt)("inlineCode",{parentName:"p"},"3"),'. We refer to this as "time series aggregation" or "horizontal aggregation".'),(0,r.kt)("p",null,"The second type of aggregation happens across components in a tree. Where time series exist for multiple child nodes under a parent, their time series are aggregated together into one summary time series that is pushed to the parent node. For example, in the following tree, each child has a time series with three timesteps. At each timestep, the metric value is ",(0,r.kt)("inlineCode",{parentName:"p"},"1")," for both children:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"}," parent\n / \\\n / \\\nchild-1 child-2\ndata: [1,1,1] data: [1,1,1]\n")),(0,r.kt)("p",null,"Assuming the aggregation method is ",(0,r.kt)("inlineCode",{parentName:"p"},"sum"),", the parent would receive an aggregated time series ","[2,2,2]",', representing the aggregated values from both children. We refer to this as "tree aggregation" or "vertical aggregation".'),(0,r.kt)("h2",{id:"configuration"},"Configuration"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"Aggregate")," is a built-in feature of the IF. This means you do not need to initialize it along with the plugins you are using in your pipeline. All you need to do is to add a small piece of config to your manifest file."),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregate")," config looks as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},'aggregation:\n metrics:\n - "carbon"\n - "energy"\n type: "both"\n')),(0,r.kt)("p",null,"There are two fields: ",(0,r.kt)("inlineCode",{parentName:"p"},"metrics")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"type"),"."),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"metrics")," is an array of metrics that you want to aggregate. You can provide any value here, but they must match a key that exists in your output data (i.e. if you tell IF to aggregate ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," but ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," is not in your outputs you will receive an error message and aggregation will fail). You can provide any number of metrics. In the example above, the aggregation feature will operate on the ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"energy")," values."),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"type")," determines which kind of aggregation you want to perform. The choices are ",(0,r.kt)("inlineCode",{parentName:"p"},"horizontal")," (time-series aggregation only), ",(0,r.kt)("inlineCode",{parentName:"p"},"vertical")," (tree aggregation only) or ",(0,r.kt)("inlineCode",{parentName:"p"},"both")," (both kinds of aggregation will be performed). In the example above, both types of aggregation will be performed over the two selected metrics."),(0,r.kt)("h2",{id:"aggregation-methods"},"Aggregation methods"),(0,r.kt)("p",null,"Aggregation can happen in several ways. In the example above, both of the selected metrics are absolute values measured in gCO2eq (carbon) and kWh (energy). To aggregate, it makes sense to add the values in each timestep for time-series aggregation and element-wise across components for tree aggregation. However, summing the values is not always the right way to aggregate. "),(0,r.kt)("p",null,"For example, some values are constants that apply to every timestep - these should simply be copied into the aggregated metric and not summed. For example, if you are using a hard-coded value for grid carbon intensity that applies globally, then you simply want to persist that value into the aggregate - adding them together would provide a misleading result. "),(0,r.kt)("p",null,"Similarly, some values are proportions or percentages. In these cases, the right way to aggregate is usually to take an average rather than summing over a set of values. "),(0,r.kt)("p",null,"The decisions about how to aggregate are made on a case-by-case basis for each individual parameter. We track the aggregation method for each parameter in our ",(0,r.kt)("inlineCode",{parentName:"p"},"params.ts")," file, which contains the canonical set of parameters, their units and aggregation methods. You can append to the parameters in this file by providing ",(0,r.kt)("inlineCode",{parentName:"p"},"params")," data in your manifest file, or you can override our recommended set of parameters entirely by providing a new file to the ",(0,r.kt)("inlineCode",{parentName:"p"},"--override-params")," command in the CLI. In either case, you need to provide an ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregation")," method with your parameters so that IF can look up the right way to aggregate the values."),(0,r.kt)("h2",{id:"aggregation-outputs"},"Aggregation outputs"),(0,r.kt)("p",null,"The aggregation process adds new output data to your manifest file. The two types of aggregation add different outputs. The horizontal (time-series) aggregation adds a new field called ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregated")," to each node whose time series has been aggregated. In the ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregated")," block, you will find the aggregated value for each of the aggregation metrics defined in the aggregation config."),(0,r.kt)("p",null,"The vertical aggregation adds a new array of output observations. These are simply named ",(0,r.kt)("inlineCode",{parentName:"p"},"outputs")," and they always contain a timestamp and duration along with the aggregated metrics for each timestep. "),(0,r.kt)("p",null,"The example below shows the result of running both kinds of aggregation for a single component:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},' "outputs": [\n {\n "carbon": 0.04846481793320214,\n "energy": 0.00030285447154471535,\n "timestamp": "2023-12-12T00:00:00.000Z"\n },\n {\n "carbon": 0.037777724630840566,\n "energy": 0.00023606013840495548,\n "timestamp": "2023-12-12T00:00:05.000Z"\n },\n {\n "carbon": 0.03630388278027921,\n "energy": 0.000226848626838947,\n "timestamp": "2023-12-12T00:00:10.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:15.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:20.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:25.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:30.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:35.000Z"\n },\n ],\n "aggregated": {\n "carbon": 0.3246705689138855,\n "energy": 0.0020287555470867216\n }\n')))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/bdfbfcca.34dfc0db.js b/assets/js/bdfbfcca.34dfc0db.js new file mode 100644 index 00000000..c091a2aa --- /dev/null +++ b/assets/js/bdfbfcca.34dfc0db.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[533],{4137:(e,t,n)=>{n.d(t,{Zo:()=>l,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),c=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},l=function(e){var t=c(e.components);return r.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,l=s(e,["components","mdxType","originalType","parentName"]),d=c(n),f=a,m=d["".concat(p,".").concat(f)]||d[f]||u[f]||o;return n?r.createElement(m,i(i({ref:t},l),{},{components:n})):r.createElement(m,i({ref:t},l))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=d;var s={};for(var p in t)hasOwnProperty.call(t,p)&&(s[p]=t[p]);s.originalType=e,s.mdxType="string"==typeof e?e:a,i[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>s,toc:()=>c});var r=n(7462),a=(n(7294),n(4137));const o={"sidebar-position":4},i="Plugins",s={unversionedId:"major-concepts/plugins",id:"major-concepts/plugins",title:"Plugins",description:"Plugins are self-contained units of code that do one thing. They can be loaded into IF and chained together in a pipeline so that simple individual plugins can form a complicated procedure for computing a manifest file.",source:"@site/docs/major-concepts/plugins.md",sourceDirName:"major-concepts",slug:"/major-concepts/plugins",permalink:"/major-concepts/plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/plugins.md",tags:[],version:"current",frontMatter:{"sidebar-position":4},sidebar:"tutorialSidebar",previous:{title:"Parameters",permalink:"/major-concepts/parameters"},next:{title:"Time",permalink:"/major-concepts/time"}},p={},c=[{value:"What are plugins?",id:"what-are-plugins",level:2},{value:"Why do we need to standardize the interface to plugins?",id:"why-do-we-need-to-standardize-the-interface-to-plugins",level:2}],l={toc:c};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},l,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"plugins"},"Plugins"),(0,a.kt)("p",null,"Plugins are self-contained units of code that do one thing. They can be loaded into IF and chained together in a pipeline so that simple individual plugins can form a complicated procedure for computing a manifest file."),(0,a.kt)("h2",{id:"what-are-plugins"},"What are plugins?"),(0,a.kt)("p",null,"A plugin converts an input into some output, for example, some plugins convert an input of CPU utilization into an output of energy."),(0,a.kt)("p",null,"There are many different ",(0,a.kt)("strong",{parentName:"p"},"plugins"),": ",(0,a.kt)("a",{parentName:"p",href:"https://dataviz.boavizta.org/"},"Boavizta"),", ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/cloud-carbon-footprint/ccf-coefficients"},"Cloud Carbon Footprint"),", ",(0,a.kt)("a",{parentName:"p",href:"https://www.climatiq.io/data"},"Climatiq")," are some great examples of open-source IMs, there are ",(0,a.kt)("strong",{parentName:"p"},"many other")," closed source, commercial and private plugins being built in-house inside organizations."),(0,a.kt)("p",null,"The set of plugins is increasing; however, no single plugin can cover all impacts, scenarios, environments, contexts, and use cases. To calculate the end-to-end impact of a software application, you need to stitch together many different plugins. Plugins differ in fundamental ways in the inputs inputs they accept, their interface, their calculation methodology, their outputs, their granularity, and their coverage. "),(0,a.kt)("p",null,"We expect the choice of which plugin to use for which software component to come down to an expert decision by a green software professional."),(0,a.kt)("h2",{id:"why-do-we-need-to-standardize-the-interface-to-plugins"},"Why do we need to standardize the interface to plugins?"),(0,a.kt)("p",null,"Currently, suppose you want to consume a plugin in your measurement application. In that case, you must craft custom code to interact with a custom interface since every plugin has its unique interface. Swapping one plugin for another requires code changes, and comparing plugins or validating their accuracy/precision is challenging. "),(0,a.kt)("p",null,"If every plugin ",(0,a.kt)("strong",{parentName:"p"},"exposes the same interface"),", then those plugins can easily be plugged into different applications, swapped in and out, upgraded, and compared. "),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"Our thesis is simple: if we want a large, vibrant ecosystem of people and tooling around measurement, we need a standard, common interface for all plugins.")),(0,a.kt)("blockquote",null,(0,a.kt)("p",{parentName:"blockquote"},"Ecosystems grow and thrive through standards.")),(0,a.kt)("p",null,"You can explore more in the ",(0,a.kt)("a",{parentName:"p",href:"/reference/plugins"},"plugins reference docs")," or our ",(0,a.kt)("a",{parentName:"p",href:"/developers/how-to-build-plugins"},"plugin building tutorial"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/bdfbfcca.42b4dcbc.js b/assets/js/bdfbfcca.42b4dcbc.js deleted file mode 100644 index 314f7a90..00000000 --- a/assets/js/bdfbfcca.42b4dcbc.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[533],{4137:(e,t,n)=>{n.d(t,{Zo:()=>l,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),c=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},l=function(e){var t=c(e.components);return r.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,l=p(e,["components","mdxType","originalType","parentName"]),d=c(n),f=a,m=d["".concat(s,".").concat(f)]||d[f]||u[f]||o;return n?r.createElement(m,i(i({ref:t},l),{},{components:n})):r.createElement(m,i({ref:t},l))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=d;var p={};for(var s in t)hasOwnProperty.call(t,s)&&(p[s]=t[s]);p.originalType=e,p.mdxType="string"==typeof e?e:a,i[1]=p;for(var c=2;c{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>p,toc:()=>c});var r=n(7462),a=(n(7294),n(4137));const o={"sidebar-position":4},i="Plugins",p={unversionedId:"major-concepts/plugins",id:"major-concepts/plugins",title:"Plugins",description:"Plugins are self-contained units of code that do one thing. They can be loaded into IF and chained together in a pipeline so that simple individual plugins can form a complicated procedure for computing a manifest file.",source:"@site/docs/major-concepts/plugins.md",sourceDirName:"major-concepts",slug:"/major-concepts/plugins",permalink:"/major-concepts/plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/major-concepts/plugins.md",tags:[],version:"current",frontMatter:{"sidebar-position":4},sidebar:"tutorialSidebar",previous:{title:"Parameters",permalink:"/major-concepts/parameters"},next:{title:"Time",permalink:"/major-concepts/time"}},s={},c=[{value:"What are plugins?",id:"what-are-plugins",level:2},{value:"Why do we need to standardize the interface to plugins?",id:"why-do-we-need-to-standardize-the-interface-to-plugins",level:2}],l={toc:c};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},l,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"plugins"},"Plugins"),(0,a.kt)("p",null,"Plugins are self-contained units of code that do one thing. They can be loaded into IF and chained together in a pipeline so that simple individual plugins can form a complicated procedure for computing a manifest file."),(0,a.kt)("h2",{id:"what-are-plugins"},"What are plugins?"),(0,a.kt)("p",null,"A plugin converts an input into some output, for example, some plugins convert an input of CPU utilization into an output of energy."),(0,a.kt)("p",null,"There are many different ",(0,a.kt)("strong",{parentName:"p"},"plugins"),": ",(0,a.kt)("a",{parentName:"p",href:"https://dataviz.boavizta.org/"},"Boavizta"),", ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/cloud-carbon-footprint/ccf-coefficients"},"Cloud Carbon Footprint"),", ",(0,a.kt)("a",{parentName:"p",href:"https://www.climatiq.io/data"},"Climatiq")," are some great examples of open-source IMs, there are ",(0,a.kt)("strong",{parentName:"p"},"many other")," closed source, commercial and private plugins being built in-house inside organizations."),(0,a.kt)("p",null,"The set of plugins is increasing; however, no single plugin can cover all impacts, scenarios, environments, contexts, and use cases. To calculate the end-to-end impact of a software application, you need to stitch together many different plugins. Plugins differ in fundamental ways in the inputs inputs they accept, their interface, their calculation methodology, their outputs, their granularity, and their coverage. "),(0,a.kt)("p",null,"We expect the choice of which plugin to use for which software component to come down to an expert decision by a green software professional."),(0,a.kt)("h2",{id:"why-do-we-need-to-standardize-the-interface-to-plugins"},"Why do we need to standardize the interface to plugins?"),(0,a.kt)("p",null,"Currently, suppose you want to consume a plugin in your measurement application. In that case, you must craft custom code to interact with a custom interface since every plugin has its unique interface. Swapping one plugin for another requires code changes, and comparing plugins or validating their accuracy/precision is challenging. "),(0,a.kt)("p",null,"If every plugin ",(0,a.kt)("strong",{parentName:"p"},"exposes the same interface"),", then those plugins can easily be plugged into different applications, swapped in and out, upgraded, and compared. "),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"Our thesis is simple: if we want a large, vibrant ecosystem of people and tooling around measurement, we need a standard, common interface for all plugins.")),(0,a.kt)("blockquote",null,(0,a.kt)("p",{parentName:"blockquote"},"Ecosystems grow and thrive through standards.")),(0,a.kt)("p",null,"You can explore more in the ",(0,a.kt)("a",{parentName:"p",href:"/reference/plugins"},"plugins reference docs")," or our ",(0,a.kt)("a",{parentName:"p",href:"/developers/how-to-build-plugins"},"plugin building tutorial"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/cca8bc57.0af78829.js b/assets/js/cca8bc57.0af78829.js deleted file mode 100644 index fe8694bc..00000000 --- a/assets/js/cca8bc57.0af78829.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[112],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>d});var r=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var p=r.createContext({}),s=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},c=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,p=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),c=s(n),d=o,f=c["".concat(p,".").concat(d)]||c[d]||m[d]||i;return n?r.createElement(f,a(a({ref:t},u),{},{components:n})):r.createElement(f,a({ref:t},u))}));function d(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,a=new Array(i);a[0]=c;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:o,a[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>a,default:()=>m,frontMatter:()=>i,metadata:()=>l,toc:()=>s});var r=n(7462),o=(n(7294),n(4137));const i={sidebar_position:3},a="How to load plugins",l={unversionedId:"users/how-to-import-plugins",id:"users/how-to-import-plugins",title:"How to load plugins",description:"Plugins are developed separately to the Impact Framework core. However, the IF core developers maintain a standard library of plugins that can be found in this Github repository. We also provide some implementations of popular community plugins, although we rely on other members of the community to maintain them.",source:"@site/docs/users/how-to-import-plugins.md",sourceDirName:"users",slug:"/users/how-to-import-plugins",permalink:"/users/how-to-import-plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/users/how-to-import-plugins.md",tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"How to install Impact Framework",permalink:"/users/how-to-install-if"},next:{title:"How to write a manifest file",permalink:"/users/how-to-write-manifests"}},p={},s=[],u={toc:s};function m(e){let{components:t,...n}=e;return(0,o.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"how-to-load-plugins"},"How to load plugins"),(0,o.kt)("p",null,"Plugins are developed separately to the Impact Framework core. However, the IF core developers maintain a standard library of plugins that can be found in this ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-plugins"},"Github repository"),". We also provide some implementations of popular community plugins, although we rely on other members of the community to maintain them."),(0,o.kt)("p",null,"Use the following commands to install the ",(0,o.kt)("inlineCode",{parentName:"p"},"if-plugins")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"if-unofficial-plugins")," repositories:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"npm -i -g @grnsft/if-plugins\nnpm -i -g @grnsft/if-unofficial-plugins\n")),(0,o.kt)("p",null,"Plugins in these packages can then be invoked in an manifest by providing their path in the plugin initialization, as shown in the following example:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-yaml"},'name: if-demo\ndescription: demo pipeline\ntags:\ninitialize:\n plugins:\n azure-importer:\n method: AzureImporter\n path: "@grnsft/if-unofficial-plugins"\n cloud-metadata:\n method: CloudMetadata\n path: "@grnsft/if-plugins"\n')),(0,o.kt)("p",null,"Load your plugin directly from your Github repository, or from ",(0,o.kt)("inlineCode",{parentName:"p"},"npm")," if you have published your plugin there. First, you'll need to install it by providing the path to the repository to ",(0,o.kt)("inlineCode",{parentName:"p"},"npm install")," as follows:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-sh"},"npm install https://github.com/Green-Software-Foundation/if-plugins\n")),(0,o.kt)("p",null,"You'll need to provide the following fields:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"YOUR-PLUGIN-HERE"),": the same name has to be used to refer to this plugin everywhere across the manifest"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"method"),": the class name for your plugin, e.g. ",(0,o.kt)("inlineCode",{parentName:"li"},"AzureImporter")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"path"),": the path to the plugin")),(0,o.kt)("p",null,"And, if your plugin requires it, add ",(0,o.kt)("inlineCode",{parentName:"p"},"global-config")," too."),(0,o.kt)("p",null,"Then, in your manifest, initialize the plugin as follows:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-yaml"},"name: plugin-demo\ndescription: loads plugin\ntags: null\ninitialize:\n plugins:\n {n.d(t,{Zo:()=>u,kt:()=>d});var r=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var p=r.createContext({}),s=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},c=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,p=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),c=s(n),d=o,f=c["".concat(p,".").concat(d)]||c[d]||m[d]||i;return n?r.createElement(f,a(a({ref:t},u),{},{components:n})):r.createElement(f,a({ref:t},u))}));function d(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,a=new Array(i);a[0]=c;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:o,a[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>a,default:()=>m,frontMatter:()=>i,metadata:()=>l,toc:()=>s});var r=n(7462),o=(n(7294),n(4137));const i={sidebar_position:3},a="How to load plugins",l={unversionedId:"users/how-to-import-plugins",id:"users/how-to-import-plugins",title:"How to load plugins",description:"Plugins are developed separately to the Impact Framework core. However, the IF core developers maintain a standard library of plugins that can be found in this Github repository. We also provide some implementations of popular community plugins, although we rely on other members of the community to maintain them.",source:"@site/docs/users/how-to-import-plugins.md",sourceDirName:"users",slug:"/users/how-to-import-plugins",permalink:"/users/how-to-import-plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-import-plugins.md",tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"How to install Impact Framework",permalink:"/users/how-to-install-if"},next:{title:"How to write a manifest file",permalink:"/users/how-to-write-manifests"}},p={},s=[],u={toc:s};function m(e){let{components:t,...n}=e;return(0,o.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"how-to-load-plugins"},"How to load plugins"),(0,o.kt)("p",null,"Plugins are developed separately to the Impact Framework core. However, the IF core developers maintain a standard library of plugins that can be found in this ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-plugins"},"Github repository"),". We also provide some implementations of popular community plugins, although we rely on other members of the community to maintain them."),(0,o.kt)("p",null,"Use the following commands to install the ",(0,o.kt)("inlineCode",{parentName:"p"},"if-plugins")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"if-unofficial-plugins")," repositories:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"npm -i -g @grnsft/if-plugins\nnpm -i -g @grnsft/if-unofficial-plugins\n")),(0,o.kt)("p",null,"Plugins in these packages can then be invoked in an manifest by providing their path in the plugin initialization, as shown in the following example:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-yaml"},'name: if-demo\ndescription: demo pipeline\ntags:\ninitialize:\n plugins:\n azure-importer:\n method: AzureImporter\n path: "@grnsft/if-unofficial-plugins"\n cloud-metadata:\n method: CloudMetadata\n path: "@grnsft/if-plugins"\n')),(0,o.kt)("p",null,"Load your plugin directly from your Github repository, or from ",(0,o.kt)("inlineCode",{parentName:"p"},"npm")," if you have published your plugin there. First, you'll need to install it by providing the path to the repository to ",(0,o.kt)("inlineCode",{parentName:"p"},"npm install")," as follows:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-sh"},"npm install https://github.com/Green-Software-Foundation/if-plugins\n")),(0,o.kt)("p",null,"You'll need to provide the following fields:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"YOUR-PLUGIN-HERE"),": the same name has to be used to refer to this plugin everywhere across the manifest"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"method"),": the class name for your plugin, e.g. ",(0,o.kt)("inlineCode",{parentName:"li"},"AzureImporter")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"path"),": the path to the plugin")),(0,o.kt)("p",null,"And, if your plugin requires it, add ",(0,o.kt)("inlineCode",{parentName:"p"},"global-config")," too."),(0,o.kt)("p",null,"Then, in your manifest, initialize the plugin as follows:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-yaml"},"name: plugin-demo\ndescription: loads plugin\ntags: null\ninitialize:\n plugins:\n {n.d(t,{Zo:()=>c,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),s=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=s(e.components);return r.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),m=s(n),f=a,d=m["".concat(p,".").concat(f)]||m[f]||u[f]||o;return n?r.createElement(d,i(i({ref:t},c),{},{components:n})):r.createElement(d,i({ref:t},c))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var r=n(7462),a=(n(7294),n(4137));const o={"sidebar-position":3},i="Impact Engine (CLI)",l={unversionedId:"major-concepts/if",id:"major-concepts/if",title:"Impact Engine (CLI)",description:"Introduction",source:"@site/docs/major-concepts/if.md",sourceDirName:"major-concepts",slug:"/major-concepts/if",permalink:"/major-concepts/if",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/major-concepts/if.md",tags:[],version:"current",frontMatter:{"sidebar-position":3},sidebar:"tutorialSidebar",previous:{title:"Group-by",permalink:"/major-concepts/groupby"},next:{title:"Manifest File",permalink:"/major-concepts/manifest-file"}},p={},s=[{value:"Introduction",id:"introduction",level:2}],c={toc:s};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"impact-engine-cli"},"Impact Engine (CLI)"),(0,a.kt)("h2",{id:"introduction"},"Introduction"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"ie")," is a command line tool that computes ",(0,a.kt)("a",{parentName:"p",href:"/major-concepts/manifest-file"},"Manifest files"),".\nIt is the portal allowing users to interact with the Impact Framework."),(0,a.kt)("p",null,"The available options are:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--manifest"),": path to an input manifest file"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--output")," (optional): path to the output file where the results as saved, if none is provided it prints to stdout."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--override-params")," (optional): if you are an advanced user and you want to override our standard set of parameters and their definitions, you can provide the path to an alternative file as an argument to this command.")),(0,a.kt)("p",null,"The only required command is ",(0,a.kt)("inlineCode",{parentName:"p"},"--manifest"),". Without a valid path to a manifest file, ",(0,a.kt)("inlineCode",{parentName:"p"},"ie")," has nothing to execute."),(0,a.kt)("p",null,"To use ",(0,a.kt)("inlineCode",{parentName:"p"},"ie"),", you must first ",(0,a.kt)("a",{parentName:"p",href:"/users/how-to-write-manifests"},"write a manifest file"),". Then, you can simply pass the path to the manifest file to ",(0,a.kt)("inlineCode",{parentName:"p"},"ie")," on the command line. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest /my-manifest.yml\n")),(0,a.kt)("p",null,"You can also pass a path where you would like to save the output file to. For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest ./my-manifest.yml --output ./my-results.yml\n")),(0,a.kt)("blockquote",null,(0,a.kt)("p",{parentName:"blockquote"},"Note that you also need to add some config to your manifest file to enable exporting to a file. The config is as follows:"),(0,a.kt)("pre",{parentName:"blockquote"},(0,a.kt)("code",{parentName:"pre"},"initialize:\n outputs:\n - yaml\n"))),(0,a.kt)("p",null,"If you omit the ",(0,a.kt)("inlineCode",{parentName:"p"},"--output")," command, your results will be displayed in the console."),(0,a.kt)("p",null,"For more information on the ",(0,a.kt)("inlineCode",{parentName:"p"},"ie")," commands see the ",(0,a.kt)("a",{parentName:"p",href:"/reference/cli"},"CLI reference documentation"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/f744e480.cb13829f.js b/assets/js/f744e480.cb13829f.js new file mode 100644 index 00000000..592f12ad --- /dev/null +++ b/assets/js/f744e480.cb13829f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[352],{4137:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),s=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=s(e.components);return r.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),m=s(n),f=a,d=m["".concat(p,".").concat(f)]||m[f]||u[f]||o;return n?r.createElement(d,i(i({ref:t},c),{},{components:n})):r.createElement(d,i({ref:t},c))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var r=n(7462),a=(n(7294),n(4137));const o={"sidebar-position":3},i="Impact Engine (CLI)",l={unversionedId:"major-concepts/if",id:"major-concepts/if",title:"Impact Engine (CLI)",description:"Introduction",source:"@site/docs/major-concepts/if.md",sourceDirName:"major-concepts",slug:"/major-concepts/if",permalink:"/major-concepts/if",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/if.md",tags:[],version:"current",frontMatter:{"sidebar-position":3},sidebar:"tutorialSidebar",previous:{title:"Group-by",permalink:"/major-concepts/groupby"},next:{title:"Manifest File",permalink:"/major-concepts/manifest-file"}},p={},s=[{value:"Introduction",id:"introduction",level:2}],c={toc:s};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"impact-engine-cli"},"Impact Engine (CLI)"),(0,a.kt)("h2",{id:"introduction"},"Introduction"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"ie")," is a command line tool that computes ",(0,a.kt)("a",{parentName:"p",href:"/major-concepts/manifest-file"},"Manifest files"),".\nIt is the portal allowing users to interact with the Impact Framework."),(0,a.kt)("p",null,"The available options are:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--manifest"),": path to an input manifest file"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--output")," (optional): path to the output file where the results as saved, if none is provided it prints to stdout."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--override-params")," (optional): if you are an advanced user and you want to override our standard set of parameters and their definitions, you can provide the path to an alternative file as an argument to this command.")),(0,a.kt)("p",null,"The only required command is ",(0,a.kt)("inlineCode",{parentName:"p"},"--manifest"),". Without a valid path to a manifest file, ",(0,a.kt)("inlineCode",{parentName:"p"},"ie")," has nothing to execute."),(0,a.kt)("p",null,"To use ",(0,a.kt)("inlineCode",{parentName:"p"},"ie"),", you must first ",(0,a.kt)("a",{parentName:"p",href:"/users/how-to-write-manifests"},"write a manifest file"),". Then, you can simply pass the path to the manifest file to ",(0,a.kt)("inlineCode",{parentName:"p"},"ie")," on the command line. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest /my-manifest.yml\n")),(0,a.kt)("p",null,"You can also pass a path where you would like to save the output file to. For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"ie --manifest ./my-manifest.yml --output ./my-results.yml\n")),(0,a.kt)("blockquote",null,(0,a.kt)("p",{parentName:"blockquote"},"Note that you also need to add some config to your manifest file to enable exporting to a file. The config is as follows:"),(0,a.kt)("pre",{parentName:"blockquote"},(0,a.kt)("code",{parentName:"pre"},"initialize:\n outputs:\n - yaml\n"))),(0,a.kt)("p",null,"If you omit the ",(0,a.kt)("inlineCode",{parentName:"p"},"--output")," command, your results will be displayed in the console."),(0,a.kt)("p",null,"For more information on the ",(0,a.kt)("inlineCode",{parentName:"p"},"ie")," commands see the ",(0,a.kt)("a",{parentName:"p",href:"/reference/cli"},"CLI reference documentation"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/fd565be6.1a85a6a4.js b/assets/js/fd565be6.1a85a6a4.js new file mode 100644 index 00000000..ba8e1dff --- /dev/null +++ b/assets/js/fd565be6.1a85a6a4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[168],{4137:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>h});var i=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=i.createContext({}),c=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},m=function(e){var t=c(e.components);return i.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},u=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,m=s(e,["components","mdxType","originalType","parentName"]),u=c(n),h=r,d=u["".concat(l,".").concat(h)]||u[h]||p[h]||a;return n?i.createElement(d,o(o({ref:t},m),{},{components:n})):i.createElement(d,o({ref:t},m))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,o=new Array(a);o[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,o[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var i=n(7462),r=(n(7294),n(4137));const a={"sidebar-position":5},o="Time",s={unversionedId:"major-concepts/time",id:"major-concepts/time",title:"Time",description:"Every observation in an array of inputs represents a snapshot with a known start time and a known duration. For example, the following observation shows that the CPU utilization for a resource was 20% for the 10 second period starting at 1500 on the 22nd January 2024:",source:"@site/docs/major-concepts/time.md",sourceDirName:"major-concepts",slug:"/major-concepts/time",permalink:"/major-concepts/time",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/time.md",tags:[],version:"current",frontMatter:{"sidebar-position":5},sidebar:"tutorialSidebar",previous:{title:"Plugins",permalink:"/major-concepts/plugins"},next:{title:"Users",permalink:"/users/"}},l={},c=[{value:"Toggling off time sync",id:"toggling-off-time-sync",level:2}],m={toc:c};function p(e){let{components:t,...a}=e;return(0,r.kt)("wrapper",(0,i.Z)({},m,a,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"time"},"Time"),(0,r.kt)("p",null,"Every ",(0,r.kt)("inlineCode",{parentName:"p"},"observation")," in an array of ",(0,r.kt)("inlineCode",{parentName:"p"},"inputs")," represents a snapshot with a known start time and a known duration. For example, the following observation shows that the CPU utilization for a resource was 20% for the 10 second period starting at 1500 on the 22nd January 2024:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yml"},"inputs:\n - timestamp: 2024-01-15T00:00:00.000Z\n duration: 10\n cpu-util: 20\n")),(0,r.kt)("p",null,"The total time covered by an inputs array is determined by the timestamp of the first observation in the array and the timestamp and duration in the last observation in the array. Since every observation needs both a timestamp and a duration, an inputs array is always a time series."),(0,r.kt)("h1",{id:"synchronizing-time-series"},"Synchronizing time series'"),(0,r.kt)("p",null,"The time series for each component is defined by its inputs array. However, a manifest file can contain many separate components, each with their own time series. There is no guarantee that an individual time series is continuous, or that all the components in a manifest file have the same start time, end time and resolution. This makes it difficult to aggregate, visualize or do like-for-like comparisons between components."),(0,r.kt)("p",null,"To solve this problem, we provide a built-in ",(0,r.kt)("inlineCode",{parentName:"p"},"time-sync")," feature that synchronizes the time series' across all the components in a tree. The time-sync feature takes a global start time, end time and interval, then forces every individual time series to conform to this global configuration."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"This works by first upsampling each time series to a common base resolution (typically 1s)."),(0,r.kt)("li",{parentName:"ul"},'Any gaps in the time series are filled in with "zero objects", which have an identical structure to the real observations but with usage metrics set to zero (we assume that when there is no data, there is no usage).'),(0,r.kt)("li",{parentName:"ul"},"Next, we check to see whether the first timestamp in each time series is before, after or identical to the global start time."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre"},'If a component\'s time series starts after the global start time, we pad the start of the time series with "zero objects" so that the start times are identical.\n'))),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre"},"If the component's time series starts *before* the global start time, we trim the time series down, discarding observations from before the global start time. The same trimming logic is applied to the end times.\n"))),(0,r.kt)("li",{parentName:"ul"},"After synchronizing the start and end times and padding any discontinuities, we have a set of continuous time series' of identical length."),(0,r.kt)("li",{parentName:"ul"},"Next, we batch observations together into time bins whose size is define by the global ",(0,r.kt)("inlineCode",{parentName:"li"},"interval")," value. This means that the resolution of the final time series' are identical and equal to ",(0,r.kt)("inlineCode",{parentName:"li"},"interval"),". ")),(0,r.kt)("p",null,"This process yields synchronized time series for all components across a tree, enabling easy visualization and intercomparison. This synchronization is also a prerequisite for our aggregation function."),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(5966).Z,width:"1058",height:"595"})),(0,r.kt)("h2",{id:"toggling-off-time-sync"},"Toggling off time sync"),(0,r.kt)("p",null,"Some applications will not want to pad with zero values, and may be strict about continuous time series' being provided in the raw manifest file. In these cases, simply toggle the padding off in the manifest file."))}p.isMDXComponent=!0},5966:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/time-sync-schematic-f7b18c86827dc4b4f0551acc799ff6ab.png"}}]); \ No newline at end of file diff --git a/assets/js/fd565be6.1f7d4f62.js b/assets/js/fd565be6.1f7d4f62.js deleted file mode 100644 index 1b91f47a..00000000 --- a/assets/js/fd565be6.1f7d4f62.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[168],{4137:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>h});var i=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=i.createContext({}),c=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},m=function(e){var t=c(e.components);return i.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},u=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,m=s(e,["components","mdxType","originalType","parentName"]),u=c(n),h=r,d=u["".concat(l,".").concat(h)]||u[h]||p[h]||a;return n?i.createElement(d,o(o({ref:t},m),{},{components:n})):i.createElement(d,o({ref:t},m))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,o=new Array(a);o[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,o[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var i=n(7462),r=(n(7294),n(4137));const a={"sidebar-position":5},o="Time",s={unversionedId:"major-concepts/time",id:"major-concepts/time",title:"Time",description:"Every observation in an array of inputs represents a snapshot with a known start time and a known duration. For example, the following observation shows that the CPU utilization for a resource was 20% for the 10 second period starting at 1500 on the 22nd January 2024:",source:"@site/docs/major-concepts/time.md",sourceDirName:"major-concepts",slug:"/major-concepts/time",permalink:"/major-concepts/time",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/ief/docs/major-concepts/time.md",tags:[],version:"current",frontMatter:{"sidebar-position":5},sidebar:"tutorialSidebar",previous:{title:"Plugins",permalink:"/major-concepts/plugins"},next:{title:"Users",permalink:"/users/"}},l={},c=[{value:"Toggling off time sync",id:"toggling-off-time-sync",level:2}],m={toc:c};function p(e){let{components:t,...a}=e;return(0,r.kt)("wrapper",(0,i.Z)({},m,a,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"time"},"Time"),(0,r.kt)("p",null,"Every ",(0,r.kt)("inlineCode",{parentName:"p"},"observation")," in an array of ",(0,r.kt)("inlineCode",{parentName:"p"},"inputs")," represents a snapshot with a known start time and a known duration. For example, the following observation shows that the CPU utilization for a resource was 20% for the 10 second period starting at 1500 on the 22nd January 2024:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yml"},"inputs:\n - timestamp: 2024-01-15T00:00:00.000Z\n duration: 10\n cpu-util: 20\n")),(0,r.kt)("p",null,"The total time covered by an inputs array is determined by the timestamp of the first observation in the array and the timestamp and duration in the last observation in the array. Since every observation needs both a timestamp and a duration, an inputs array is always a time series."),(0,r.kt)("h1",{id:"synchronizing-time-series"},"Synchronizing time series'"),(0,r.kt)("p",null,"The time series for each component is defined by its inputs array. However, a manifest file can contain many separate components, each with their own time series. There is no guarantee that an individual time series is continuous, or that all the components in a manifest file have the same start time, end time and resolution. This makes it difficult to aggregate, visualize or do like-for-like comparisons between components."),(0,r.kt)("p",null,"To solve this problem, we provide a built-in ",(0,r.kt)("inlineCode",{parentName:"p"},"time-sync")," feature that synchronizes the time series' across all the components in a tree. The time-sync feature takes a global start time, end time and interval, then forces every individual time series to conform to this global configuration."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"This works by first upsampling each time series to a common base resolution (typically 1s)."),(0,r.kt)("li",{parentName:"ul"},'Any gaps in the time series are filled in with "zero objects", which have an identical structure to the real observations but with usage metrics set to zero (we assume that when there is no data, there is no usage).'),(0,r.kt)("li",{parentName:"ul"},"Next, we check to see whether the first timestamp in each time series is before, after or identical to the global start time."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre"},'If a component\'s time series starts after the global start time, we pad the start of the time series with "zero objects" so that the start times are identical.\n'))),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre"},"If the component's time series starts *before* the global start time, we trim the time series down, discarding observations from before the global start time. The same trimming logic is applied to the end times.\n"))),(0,r.kt)("li",{parentName:"ul"},"After synchronizing the start and end times and padding any discontinuities, we have a set of continuous time series' of identical length."),(0,r.kt)("li",{parentName:"ul"},"Next, we batch observations together into time bins whose size is define by the global ",(0,r.kt)("inlineCode",{parentName:"li"},"interval")," value. This means that the resolution of the final time series' are identical and equal to ",(0,r.kt)("inlineCode",{parentName:"li"},"interval"),". ")),(0,r.kt)("p",null,"This process yields synchronized time series for all components across a tree, enabling easy visualization and intercomparison. This synchronization is also a prerequisite for our aggregation function."),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(5966).Z,width:"1058",height:"595"})),(0,r.kt)("h2",{id:"toggling-off-time-sync"},"Toggling off time sync"),(0,r.kt)("p",null,"Some applications will not want to pad with zero values, and may be strict about continuous time series' being provided in the raw manifest file. In these cases, simply toggle the padding off in the manifest file."))}p.isMDXComponent=!0},5966:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/time-sync-schematic-f7b18c86827dc4b4f0551acc799ff6ab.png"}}]); \ No newline at end of file diff --git a/assets/js/main.c42b18fe.js b/assets/js/main.c7c3252d.js similarity index 98% rename from assets/js/main.c42b18fe.js rename to assets/js/main.c7c3252d.js index 6c3e6d13..a7cc1400 100644 --- a/assets/js/main.c42b18fe.js +++ b/assets/js/main.c7c3252d.js @@ -1,2 +1,2 @@ -/*! For license information please see main.c42b18fe.js.LICENSE.txt */ -(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[179],{997:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var r=n(7294),a=n(7462),o=n(8356),i=n.n(o),l=n(6887);const s={"069226b7":[()=>n.e(760).then(n.t.bind(n,5745,19)),"/home/runner/work/if-docs/if-docs/.docusaurus/docusaurus-plugin-content-pages/default/plugin-route-context-module-100.json",5745],"090462b1":[()=>n.e(941).then(n.bind(n,3277)),"@site/docs/users/how-to-export-to-csv.md",3277],"0a19367a":[()=>n.e(109).then(n.bind(n,1652)),"@site/docs/users/quick-start.md",1652],"0e384e19":[()=>n.e(671).then(n.bind(n,1039)),"@site/docs/intro.md",1039],17896441:[()=>Promise.all([n.e(532),n.e(918)]).then(n.bind(n,2358)),"@theme/DocItem",2358],"1a3c9b31":[()=>n.e(751).then(n.bind(n,2559)),"@site/docs/reference/index.md",2559],"1be78505":[()=>Promise.all([n.e(532),n.e(514)]).then(n.bind(n,1299)),"@theme/DocPage",1299],"1df93b7f":[()=>Promise.all([n.e(532),n.e(237)]).then(n.bind(n,8888)),"@site/src/pages/index.tsx",8888],"1ead5b6d":[()=>n.e(890).then(n.bind(n,2080)),"@site/docs/major-concepts/groupby.md",2080],"244c9605":[()=>n.e(426).then(n.bind(n,6869)),"@site/docs/developers/how-to-refine-plugins.md",6869],"28f080da":[()=>n.e(806).then(n.bind(n,8227)),"@site/docs/developers/how-to-build-plugins.md",8227],"29966d47":[()=>n.e(117).then(n.bind(n,1841)),"@site/docs/major-concepts/index.md",1841],"2d14298d":[()=>n.e(754).then(n.bind(n,9406)),"@site/docs/developers/how-to-visualize-results.md",9406],"3c29e2ef":[()=>n.e(702).then(n.bind(n,8630)),"@site/docs/contributions.md",8630],"425ff8ea":[()=>n.e(681).then(n.bind(n,5513)),"@site/docs/major-concepts/parameters.md",5513],"449bc0d9":[()=>n.e(99).then(n.bind(n,2350)),"@site/docs/users/index.md",2350],"486bc80e":[()=>n.e(162).then(n.bind(n,2843)),"@site/docs/users/how-to-install-if.md",2843],"5c5a410f":[()=>n.e(468).then(n.bind(n,7003)),"@site/docs/major-concepts/manifest-file.md",7003],"5e9f5e1a":[()=>Promise.resolve().then(n.bind(n,6809)),"@generated/docusaurus.config",6809],"63925da8":[()=>n.e(822).then(n.bind(n,7673)),"@site/docs/developers/index.md",7673],"68ffc9f1":[()=>n.e(644).then(n.bind(n,9365)),"@site/docs/users/how-to-write-manifests.md",9365],"75e434b4":[()=>n.e(229).then(n.bind(n,9059)),"@site/docs/reference/plugins.md",9059],"859a76f9":[()=>n.e(527).then(n.t.bind(n,3769,19)),"/home/runner/work/if-docs/if-docs/.docusaurus/docusaurus-plugin-content-docs/default/plugin-route-context-module-100.json",3769],"91c76d4c":[()=>n.e(960).then(n.bind(n,8699)),"@site/docs/reference/cli.md",8699],"935f2afb":[()=>n.e(53).then(n.t.bind(n,1109,19)),"~docs/default/version-current-metadata-prop-751.json",1109],"93f138be":[()=>n.e(713).then(n.bind(n,6769)),"@site/docs/developers/how-to-write-unit-tests.md",6769],a4954f21:[()=>n.e(672).then(n.bind(n,4296)),"@site/docs/major-concepts/design-philosophy.md",4296],ad895e75:[()=>n.e(288).then(n.bind(n,7701)),"@site/docs/FAQ.md",7701],b5ada7f5:[()=>n.e(491).then(n.bind(n,1802)),"@site/docs/major-concepts/aggregation.md",1802],bdfbfcca:[()=>n.e(533).then(n.bind(n,325)),"@site/docs/major-concepts/plugins.md",325],cca8bc57:[()=>n.e(112).then(n.bind(n,4851)),"@site/docs/users/how-to-import-plugins.md",4851],f744e480:[()=>n.e(352).then(n.bind(n,77)),"@site/docs/major-concepts/if.md",77],fd565be6:[()=>n.e(168).then(n.bind(n,9887)),"@site/docs/major-concepts/time.md",9887]};function u(e){let{error:t,retry:n,pastDelay:a}=e;return t?r.createElement("div",{style:{textAlign:"center",color:"#fff",backgroundColor:"#fa383e",borderColor:"#fa383e",borderStyle:"solid",borderRadius:"0.25rem",borderWidth:"1px",boxSizing:"border-box",display:"block",padding:"1rem",flex:"0 0 50%",marginLeft:"25%",marginRight:"25%",marginTop:"5rem",maxWidth:"50%",width:"100%"}},r.createElement("p",null,String(t)),r.createElement("div",null,r.createElement("button",{type:"button",onClick:n},"Retry"))):a?r.createElement("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",height:"100vh"}},r.createElement("svg",{id:"loader",style:{width:128,height:110,position:"absolute",top:"calc(100vh - 64%)"},viewBox:"0 0 45 45",xmlns:"http://www.w3.org/2000/svg",stroke:"#61dafb"},r.createElement("g",{fill:"none",fillRule:"evenodd",transform:"translate(1 1)",strokeWidth:"2"},r.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},r.createElement("animate",{attributeName:"r",begin:"1.5s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-opacity",begin:"1.5s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-width",begin:"1.5s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),r.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},r.createElement("animate",{attributeName:"r",begin:"3s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-opacity",begin:"3s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-width",begin:"3s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),r.createElement("circle",{cx:"22",cy:"22",r:"8"},r.createElement("animate",{attributeName:"r",begin:"0s",dur:"1.5s",values:"6;1;2;3;4;5;6",calcMode:"linear",repeatCount:"indefinite"}))))):null}var c=n(5304),d=n(9656);function f(e,t){if("*"===e)return i()({loading:u,loader:()=>n.e(248).then(n.bind(n,4248)),modules:["@theme/NotFound"],webpack:()=>[4248],render(e,t){const n=e.default;return r.createElement(d.z,{value:{plugin:{name:"native",id:"default"}}},r.createElement(n,t))}});const o=l[e+"-"+t],f={},p=[],m=[],h=(0,c.Z)(o);return Object.entries(h).forEach((e=>{let[t,n]=e;const r=s[n];r&&(f[t]=r[0],p.push(r[1]),m.push(r[2]))})),i().Map({loading:u,loader:f,modules:p,webpack:()=>m,render(t,n){const i=JSON.parse(JSON.stringify(o));Object.entries(t).forEach((t=>{let[n,r]=t;const a=r.default;if(!a)throw new Error("The page component at "+e+" doesn't have a default export. This makes it impossible to render anything. Consider default-exporting a React component.");"object"!=typeof a&&"function"!=typeof a||Object.keys(r).filter((e=>"default"!==e)).forEach((e=>{a[e]=r[e]}));let o=i;const l=n.split(".");l.slice(0,-1).forEach((e=>{o=o[e]})),o[l[l.length-1]]=a}));const l=i.__comp;delete i.__comp;const s=i.__context;return delete i.__context,r.createElement(d.z,{value:s},r.createElement(l,(0,a.Z)({},i,n)))}})}const p=[{path:"/",component:f("/","289"),exact:!0},{path:"/",component:f("/","429"),routes:[{path:"/contributions",component:f("/contributions","c46"),exact:!0,sidebar:"tutorialSidebar"},{path:"/developers/",component:f("/developers/","745"),exact:!0,sidebar:"tutorialSidebar"},{path:"/developers/how-to-build-plugins",component:f("/developers/how-to-build-plugins","e49"),exact:!0,sidebar:"tutorialSidebar"},{path:"/developers/how-to-refine-plugins",component:f("/developers/how-to-refine-plugins","017"),exact:!0,sidebar:"tutorialSidebar"},{path:"/developers/how-to-visualize-results",component:f("/developers/how-to-visualize-results","e9c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/developers/how-to-write-unit-tests",component:f("/developers/how-to-write-unit-tests","836"),exact:!0,sidebar:"tutorialSidebar"},{path:"/FAQ",component:f("/FAQ","9ff"),exact:!0,sidebar:"tutorialSidebar"},{path:"/intro",component:f("/intro","283"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/",component:f("/major-concepts/","d20"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/aggregation",component:f("/major-concepts/aggregation","aa7"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/design-philosophy",component:f("/major-concepts/design-philosophy","99c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/groupby",component:f("/major-concepts/groupby","b59"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/if",component:f("/major-concepts/if","00c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/manifest-file",component:f("/major-concepts/manifest-file","054"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/parameters",component:f("/major-concepts/parameters","2bc"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/plugins",component:f("/major-concepts/plugins","c56"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/time",component:f("/major-concepts/time","279"),exact:!0,sidebar:"tutorialSidebar"},{path:"/reference/",component:f("/reference/","183"),exact:!0,sidebar:"tutorialSidebar"},{path:"/reference/cli",component:f("/reference/cli","131"),exact:!0,sidebar:"tutorialSidebar"},{path:"/reference/plugins",component:f("/reference/plugins","c1f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/",component:f("/users/","669"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/how-to-export-to-csv",component:f("/users/how-to-export-to-csv","7ed"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/how-to-import-plugins",component:f("/users/how-to-import-plugins","c01"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/how-to-install-if",component:f("/users/how-to-install-if","a04"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/how-to-write-manifests",component:f("/users/how-to-write-manifests","5ed"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/quick-start",component:f("/users/quick-start","164"),exact:!0,sidebar:"tutorialSidebar"}]},{path:"*",component:f("*")}]},8121:(e,t,n)=>{"use strict";n.d(t,{_:()=>a,t:()=>o});var r=n(7294);const a=r.createContext(!1);function o(e){let{children:t}=e;const[n,o]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{o(!0)}),[]),r.createElement(a.Provider,{value:n},t)}},654:(e,t,n)=>{"use strict";var r=n(7294),a=n(3935),o=n(3727),i=n(405),l=n(6136);const s=[n(4313),n(984),n(2251),n(9957),n(6930)];var u=n(997),c=n(6775),d=n(8790);function f(e){let{children:t}=e;return r.createElement(r.Fragment,null,t)}var p=n(7462),m=n(1514),h=n(9962),g=n(9524),v=n(107),b=n(5463),y=n(626),w=n(8181),k=n(246),E=n(3647);function S(){const{i18n:{defaultLocale:e,localeConfigs:t}}=(0,h.Z)(),n=(0,y.l)();return r.createElement(m.Z,null,Object.entries(t).map((e=>{let[t,{htmlLang:a}]=e;return r.createElement("link",{key:t,rel:"alternate",href:n.createUrl({locale:t,fullyQualified:!0}),hrefLang:a})})),r.createElement("link",{rel:"alternate",href:n.createUrl({locale:e,fullyQualified:!0}),hrefLang:"x-default"}))}function x(e){let{permalink:t}=e;const{siteConfig:{url:n}}=(0,h.Z)(),a=function(){const{siteConfig:{url:e}}=(0,h.Z)(),{pathname:t}=(0,c.TH)();return e+(0,g.Z)(t)}(),o=t?""+n+t:a;return r.createElement(m.Z,null,r.createElement("meta",{property:"og:url",content:o}),r.createElement("link",{rel:"canonical",href:o}))}function _(){const{i18n:{currentLocale:e}}=(0,h.Z)(),{metadata:t,image:n}=(0,v.L)();return r.createElement(r.Fragment,null,r.createElement(m.Z,null,r.createElement("meta",{name:"twitter:card",content:"summary_large_image"}),r.createElement("body",{className:w.h})),n&&r.createElement(b.d,{image:n}),r.createElement(x,null),r.createElement(S,null),r.createElement(E.Z,{tag:k.HX,locale:e}),r.createElement(m.Z,null,t.map(((e,t)=>r.createElement("meta",(0,p.Z)({key:t},e))))))}const C=new Map;function T(e){if(C.has(e.pathname))return{...e,pathname:C.get(e.pathname)};if((0,d.f)(u.Z,e.pathname).some((e=>{let{route:t}=e;return!0===t.exact})))return C.set(e.pathname,e.pathname),e;const t=e.pathname.trim().replace(/(?:\/index)?\.html$/,"")||"/";return C.set(e.pathname,t),{...e,pathname:t}}var A=n(8121),L=n(694);function R(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r{var r,a;const o=null!=(r=null==(a=t.default)?void 0:a[e])?r:t[e];return null==o?void 0:o(...n)}));return()=>a.forEach((e=>null==e?void 0:e()))}const P=function(e){let{children:t,location:n,previousLocation:a}=e;return(0,r.useLayoutEffect)((()=>{a!==n&&(a&&function(e){const{hash:t}=e;if(t){const e=decodeURIComponent(t.substring(1)),n=document.getElementById(e);null==n||n.scrollIntoView()}else window.scrollTo(0,0)}(n),R("onRouteDidUpdate",{previousLocation:a,location:n}))}),[a,n]),t};function N(e){const t=(0,d.f)(u.Z,e);return Promise.all(t.map((e=>null==e.route.component.preload?void 0:e.route.component.preload())))}class O extends r.Component{constructor(e){super(e),this.previousLocation=void 0,this.routeUpdateCleanupCb=void 0,this.previousLocation=null,this.routeUpdateCleanupCb=l.Z.canUseDOM?R("onRouteUpdate",{previousLocation:null,location:this.props.location}):()=>{},this.state={nextRouteHasLoaded:!0}}shouldComponentUpdate(e,t){if(e.location===this.props.location)return t.nextRouteHasLoaded;const n=e.location;return this.previousLocation=this.props.location,this.setState({nextRouteHasLoaded:!1}),this.routeUpdateCleanupCb=R("onRouteUpdate",{previousLocation:this.previousLocation,location:n}),N(n.pathname).then((()=>{this.routeUpdateCleanupCb(),this.setState({nextRouteHasLoaded:!0})})).catch((e=>{console.warn(e),window.location.reload()})),!1}render(){const{children:e,location:t}=this.props;return r.createElement(P,{previousLocation:this.previousLocation,location:t},r.createElement(c.AW,{location:t,render:()=>e}))}}const D=O,I="docusaurus-base-url-issue-banner-container",M="docusaurus-base-url-issue-banner-suggestion-container",F="__DOCUSAURUS_INSERT_BASEURL_BANNER";function j(e){return"\nwindow['"+F+"'] = true;\n\ndocument.addEventListener('DOMContentLoaded', maybeInsertBanner);\n\nfunction maybeInsertBanner() {\n var shouldInsert = window['"+F+"'];\n shouldInsert && insertBanner();\n}\n\nfunction insertBanner() {\n var bannerContainer = document.getElementById('"+I+"');\n if (!bannerContainer) {\n return;\n }\n var bannerHtml = "+JSON.stringify(function(e){return'\n
\n

Your Docusaurus site did not load properly.

\n

A very common reason is a wrong site baseUrl configuration.

\n

Current configured baseUrl = '+e+" "+("/"===e?" (default value)":"")+'

\n

We suggest trying baseUrl =

\n
\n'}(e)).replace(/{window[F]=!1}),[]),r.createElement(r.Fragment,null,!l.Z.canUseDOM&&r.createElement(m.Z,null,r.createElement("script",null,j(e))),r.createElement("div",{id:I}))}function z(){const{siteConfig:{baseUrl:e,baseUrlIssueBanner:t}}=(0,h.Z)(),{pathname:n}=(0,c.TH)();return t&&n===e?r.createElement(B,null):null}function U(){const{siteConfig:{favicon:e,title:t},i18n:{currentLocale:n,localeConfigs:a}}=(0,h.Z)(),o=(0,g.Z)(e),{htmlLang:i,direction:l}=a[n];return r.createElement(m.Z,null,r.createElement("html",{lang:i,dir:l}),r.createElement("title",null,t),r.createElement("meta",{property:"og:title",content:t}),e&&r.createElement("link",{rel:"icon",href:o}))}var $=n(3256);function q(){const e=(0,d.H)(u.Z),t=(0,c.TH)();return r.createElement($.Z,null,r.createElement(L.M,null,r.createElement(A.t,null,r.createElement(f,null,r.createElement(U,null),r.createElement(_,null),r.createElement(z,null),r.createElement(D,{location:T(t)},e)))))}var G=n(6887);const H=function(e){try{return document.createElement("link").relList.supports(e)}catch{return!1}}("prefetch")?function(e){return new Promise(((t,n)=>{var r,a;if("undefined"==typeof document)return void n();const o=document.createElement("link");o.setAttribute("rel","prefetch"),o.setAttribute("href",e),o.onload=()=>t(),o.onerror=()=>n();const i=null!=(r=document.getElementsByTagName("head")[0])?r:null==(a=document.getElementsByName("script")[0])?void 0:a.parentNode;null==i||i.appendChild(o)}))}:function(e){return new Promise(((t,n)=>{const r=new XMLHttpRequest;r.open("GET",e,!0),r.withCredentials=!0,r.onload=()=>{200===r.status?t():n()},r.send(null)}))};var Z=n(5304);const V=new Set,W=new Set,Y=()=>{var e,t;return(null==(e=navigator.connection)?void 0:e.effectiveType.includes("2g"))||(null==(t=navigator.connection)?void 0:t.saveData)},K={prefetch(e){if(!(e=>!Y()&&!W.has(e)&&!V.has(e))(e))return!1;V.add(e);const t=(0,d.f)(u.Z,e).flatMap((e=>{return t=e.route.path,Object.entries(G).filter((e=>{let[n]=e;return n.replace(/-[^-]+$/,"")===t})).flatMap((e=>{let[,t]=e;return Object.values((0,Z.Z)(t))}));var t}));return Promise.all(t.map((e=>{const t=n.gca(e);return t&&!t.includes("undefined")?H(t).catch((()=>{})):Promise.resolve()})))},preload:e=>!!(e=>!Y()&&!W.has(e))(e)&&(W.add(e),N(e))},Q=Object.freeze(K);if(l.Z.canUseDOM){window.docusaurus=Q;const e=a.hydrate;N(window.location.pathname).then((()=>{e(r.createElement(i.B6,null,r.createElement(o.VK,null,r.createElement(q,null))),document.getElementById("__docusaurus"))}))}},694:(e,t,n)=>{"use strict";n.d(t,{_:()=>c,M:()=>d});var r=n(7294),a=n(6809);const o=JSON.parse('{"docusaurus-plugin-google-gtag":{"default":{"trackingID":"G-P59RXWWG7S","anonymizeIP":false,"id":"default"}},"docusaurus-plugin-content-docs":{"default":{"path":"/","versions":[{"name":"current","label":"Next","isLast":true,"path":"/","mainDocId":"intro","docs":[{"id":"contributions","path":"/contributions","sidebar":"tutorialSidebar"},{"id":"developers/how-to-build-plugins","path":"/developers/how-to-build-plugins","sidebar":"tutorialSidebar"},{"id":"developers/how-to-refine-plugins","path":"/developers/how-to-refine-plugins","sidebar":"tutorialSidebar"},{"id":"developers/how-to-visualize-results","path":"/developers/how-to-visualize-results","sidebar":"tutorialSidebar"},{"id":"developers/how-to-write-unit-tests","path":"/developers/how-to-write-unit-tests","sidebar":"tutorialSidebar"},{"id":"developers/index","path":"/developers/","sidebar":"tutorialSidebar"},{"id":"FAQ","path":"/FAQ","sidebar":"tutorialSidebar"},{"id":"intro","path":"/intro","sidebar":"tutorialSidebar"},{"id":"major-concepts/aggregation","path":"/major-concepts/aggregation","sidebar":"tutorialSidebar"},{"id":"major-concepts/design-philosophy","path":"/major-concepts/design-philosophy","sidebar":"tutorialSidebar"},{"id":"major-concepts/groupby","path":"/major-concepts/groupby","sidebar":"tutorialSidebar"},{"id":"major-concepts/if","path":"/major-concepts/if","sidebar":"tutorialSidebar"},{"id":"major-concepts/index","path":"/major-concepts/","sidebar":"tutorialSidebar"},{"id":"major-concepts/manifest-file","path":"/major-concepts/manifest-file","sidebar":"tutorialSidebar"},{"id":"major-concepts/parameters","path":"/major-concepts/parameters","sidebar":"tutorialSidebar"},{"id":"major-concepts/plugins","path":"/major-concepts/plugins","sidebar":"tutorialSidebar"},{"id":"major-concepts/time","path":"/major-concepts/time","sidebar":"tutorialSidebar"},{"id":"reference/cli","path":"/reference/cli","sidebar":"tutorialSidebar"},{"id":"reference/index","path":"/reference/","sidebar":"tutorialSidebar"},{"id":"reference/plugins","path":"/reference/plugins","sidebar":"tutorialSidebar"},{"id":"users/how-to-export-to-csv","path":"/users/how-to-export-to-csv","sidebar":"tutorialSidebar"},{"id":"users/how-to-import-plugins","path":"/users/how-to-import-plugins","sidebar":"tutorialSidebar"},{"id":"users/how-to-install-if","path":"/users/how-to-install-if","sidebar":"tutorialSidebar"},{"id":"users/how-to-write-manifests","path":"/users/how-to-write-manifests","sidebar":"tutorialSidebar"},{"id":"users/index","path":"/users/","sidebar":"tutorialSidebar"},{"id":"users/quick-start","path":"/users/quick-start","sidebar":"tutorialSidebar"}],"draftIds":[],"sidebars":{"tutorialSidebar":{"link":{"path":"/intro","label":"intro"}}}}],"breadcrumbs":true}}}'),i=JSON.parse('{"defaultLocale":"en","locales":["en"],"path":"i18n","currentLocale":"en","localeConfigs":{"en":{"label":"English","direction":"ltr","htmlLang":"en","calendar":"gregory","path":"en"}}}');var l=n(7529);const s=JSON.parse('{"docusaurusVersion":"2.0.1","siteVersion":"0.0.0","pluginVersions":{"docusaurus-plugin-content-docs":{"type":"package","name":"@docusaurus/plugin-content-docs","version":"2.0.1"},"docusaurus-plugin-content-pages":{"type":"package","name":"@docusaurus/plugin-content-pages","version":"2.0.1"},"docusaurus-plugin-google-gtag":{"type":"package","name":"@docusaurus/plugin-google-gtag","version":"2.0.1"},"docusaurus-plugin-sitemap":{"type":"package","name":"@docusaurus/plugin-sitemap","version":"2.0.1"},"docusaurus-theme-classic":{"type":"package","name":"@docusaurus/theme-classic","version":"2.0.1"},"docusaurus-plugin-ideal-image":{"type":"package","name":"@docusaurus/plugin-ideal-image","version":"2.0.1"}}}'),u={siteConfig:a.default,siteMetadata:s,globalData:o,i18n:i,codeTranslations:l},c=r.createContext(u);function d(e){let{children:t}=e;return r.createElement(c.Provider,{value:u},t)}},3256:(e,t,n)=>{"use strict";n.d(t,{Z:()=>c});var r=n(7294),a=n(6136),o=n(1514),i=n(1982);function l(e){let{error:t,tryAgain:n}=e;return r.createElement("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center",height:"50vh",width:"100%",fontSize:"20px"}},r.createElement("h1",null,"This page crashed."),r.createElement("p",null,t.message),r.createElement("button",{type:"button",onClick:n},"Try again"))}function s(e){let{error:t,tryAgain:n}=e;return r.createElement(c,{fallback:()=>r.createElement(l,{error:t,tryAgain:n})},r.createElement(o.Z,null,r.createElement("title",null,"Page Error")),r.createElement(i.Z,null,r.createElement(l,{error:t,tryAgain:n})))}const u=e=>r.createElement(s,e);class c extends r.Component{constructor(e){super(e),this.state={error:null}}componentDidCatch(e){a.Z.canUseDOM&&this.setState({error:e})}render(){const{children:e}=this.props,{error:t}=this.state;if(t){var n;const e={error:t,tryAgain:()=>this.setState({error:null})};return(null!=(n=this.props.fallback)?n:u)(e)}return null!=e?e:null}}},6136:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});const r="undefined"!=typeof window&&"document"in window&&"createElement"in window.document,a={canUseDOM:r,canUseEventListeners:r&&("addEventListener"in window||"attachEvent"in window),canUseIntersectionObserver:r&&"IntersectionObserver"in window,canUseViewport:r&&"screen"in window}},1514:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(405);function o(e){return r.createElement(a.ql,e)}},3699:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var r=n(7462),a=n(7294),o=n(3727),i=n(3905),l=n(9962),s=n(2735),u=n(6136);const c=a.createContext({collectLink:()=>{}});var d=n(9524);function f(e,t){var n,f;let{isNavLink:p,to:m,href:h,activeClassName:g,isActive:v,"data-noBrokenLinkCheck":b,autoAddBaseUrl:y=!0,...w}=e;const{siteConfig:{trailingSlash:k,baseUrl:E}}=(0,l.Z)(),{withBaseUrl:S}=(0,d.C)(),x=(0,a.useContext)(c),_=(0,a.useRef)(null);(0,a.useImperativeHandle)(t,(()=>_.current));const C=m||h;const T=(0,s.Z)(C),A=null==C?void 0:C.replace("pathname://","");let L=void 0!==A?(R=A,y&&(e=>e.startsWith("/"))(R)?S(R):R):void 0;var R;L&&T&&(L=(0,i.applyTrailingSlash)(L,{trailingSlash:k,baseUrl:E}));const P=(0,a.useRef)(!1),N=p?o.OL:o.rU,O=u.Z.canUseIntersectionObserver,D=(0,a.useRef)();(0,a.useEffect)((()=>(!O&&T&&null!=L&&window.docusaurus.prefetch(L),()=>{O&&D.current&&D.current.disconnect()})),[D,L,O,T]);const I=null!=(n=null==(f=L)?void 0:f.startsWith("#"))&&n,M=!L||!T||I;return M||b||x.collectLink(L),M?a.createElement("a",(0,r.Z)({ref:_,href:L},C&&!T&&{target:"_blank",rel:"noopener noreferrer"},w)):a.createElement(N,(0,r.Z)({},w,{onMouseEnter:()=>{P.current||null==L||(window.docusaurus.preload(L),P.current=!0)},innerRef:e=>{_.current=e,O&&e&&T&&(D.current=new window.IntersectionObserver((t=>{t.forEach((t=>{e===t.target&&(t.isIntersecting||t.intersectionRatio>0)&&(D.current.unobserve(e),D.current.disconnect(),null!=L&&window.docusaurus.prefetch(L))}))})),D.current.observe(e))},to:L},p&&{isActive:v,activeClassName:g}))}const p=a.forwardRef(f)},7325:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s,I:()=>l});var r=n(7294);function a(e,t){const n=e.split(/(\{\w+\})/).map(((e,n)=>{if(n%2==1){const n=null==t?void 0:t[e.slice(1,-1)];if(void 0!==n)return n}return e}));return n.some((e=>(0,r.isValidElement)(e)))?n.map(((e,t)=>(0,r.isValidElement)(e)?r.cloneElement(e,{key:t}):e)).filter((e=>""!==e)):n.join("")}var o=n(7529);function i(e){var t,n;let{id:r,message:a}=e;if(void 0===r&&void 0===a)throw new Error("Docusaurus translation declarations must have at least a translation id or a default translation message");return null!=(t=null!=(n=o[null!=r?r:a])?n:a)?t:r}function l(e,t){let{message:n,id:r}=e;return a(i({message:n,id:r}),t)}function s(e){let{children:t,id:n,values:o}=e;if(t&&"string"!=typeof t)throw console.warn("Illegal children",t),new Error("The Docusaurus component only accept simple string values");const l=i({message:t,id:n});return r.createElement(r.Fragment,null,a(l,o))}},6875:(e,t,n)=>{"use strict";n.d(t,{m:()=>r});const r="default"},2735:(e,t,n)=>{"use strict";function r(e){return/^(?:\w*:|\/\/)/.test(e)}function a(e){return void 0!==e&&!r(e)}n.d(t,{Z:()=>a,b:()=>r})},9524:(e,t,n)=>{"use strict";n.d(t,{C:()=>o,Z:()=>i});var r=n(9962),a=n(2735);function o(){const{siteConfig:{baseUrl:e,url:t}}=(0,r.Z)();return{withBaseUrl:(n,r)=>function(e,t,n,r){let{forcePrependBaseUrl:o=!1,absolute:i=!1}=void 0===r?{}:r;if(!n||n.startsWith("#")||(0,a.b)(n))return n;if(o)return t+n.replace(/^\//,"");if(n===t.replace(/\/$/,""))return t;const l=n.startsWith(t)?n:t+n.replace(/^\//,"");return i?e+l:l}(t,e,n,r)}}function i(e,t){void 0===t&&(t={});const{withBaseUrl:n}=o();return n(e,t)}},9962:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(694);function o(){return(0,r.useContext)(a._)}},1048:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(8121);function o(){return(0,r.useContext)(a._)}},5304:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});function r(e){const t={};return function e(n,r){Object.entries(n).forEach((n=>{let[a,o]=n;const i=r?r+"."+a:a;var l;"object"==typeof(l=o)&&l&&Object.keys(l).length>0?e(o,i):t[i]=o}))}(e),t}},9656:(e,t,n)=>{"use strict";n.d(t,{_:()=>a,z:()=>o});var r=n(7294);const a=r.createContext(null);function o(e){let{children:t,value:n}=e;const o=r.useContext(a),i=(0,r.useMemo)((()=>function(e){let{parent:t,value:n}=e;if(!t){if(!n)throw new Error("Unexpected: no Docusaurus route context found");if(!("plugin"in n))throw new Error("Unexpected: Docusaurus topmost route context has no `plugin` attribute");return n}const r={...t.data,...null==n?void 0:n.data};return{plugin:t.plugin,data:r}}({parent:o,value:n})),[o,n]);return r.createElement(a.Provider,{value:i},t)}},9871:(e,t,n)=>{"use strict";n.d(t,{Iw:()=>h,gA:()=>f,_r:()=>c,Jo:()=>g,zh:()=>d,yW:()=>m,gB:()=>p});var r=n(6775),a=n(9962),o=n(6875);function i(e,t){void 0===t&&(t={});const n=function(){const{globalData:e}=(0,a.Z)();return e}()[e];if(!n&&t.failfast)throw new Error('Docusaurus plugin global data not found for "'+e+'" plugin.');return n}const l=e=>e.versions.find((e=>e.isLast));function s(e,t){const n=function(e,t){const n=l(e);return[...e.versions.filter((e=>e!==n)),n].find((e=>!!(0,r.LX)(t,{path:e.path,exact:!1,strict:!1})))}(e,t),a=null==n?void 0:n.docs.find((e=>!!(0,r.LX)(t,{path:e.path,exact:!0,strict:!1})));return{activeVersion:n,activeDoc:a,alternateDocVersions:a?function(t){const n={};return e.versions.forEach((e=>{e.docs.forEach((r=>{r.id===t&&(n[e.name]=r)}))})),n}(a.id):{}}}const u={},c=()=>{var e;return null!=(e=i("docusaurus-plugin-content-docs"))?e:u},d=e=>function(e,t,n){void 0===t&&(t=o.m),void 0===n&&(n={});const r=i(e),a=null==r?void 0:r[t];if(!a&&n.failfast)throw new Error('Docusaurus plugin global data not found for "'+e+'" plugin with id "'+t+'".');return a}("docusaurus-plugin-content-docs",e,{failfast:!0});function f(e){void 0===e&&(e={});const t=c(),{pathname:n}=(0,r.TH)();return function(e,t,n){void 0===n&&(n={});const a=Object.entries(e).sort(((e,t)=>t[1].path.localeCompare(e[1].path))).find((e=>{let[,n]=e;return!!(0,r.LX)(t,{path:n.path,exact:!1,strict:!1})})),o=a?{pluginId:a[0],pluginData:a[1]}:void 0;if(!o&&n.failfast)throw new Error("Can't find active docs plugin for \""+t+'" pathname, while it was expected to be found. Maybe you tried to use a docs feature that can only be used on a docs-related page? Existing docs plugin paths are: '+Object.values(e).map((e=>e.path)).join(", "));return o}(t,n,e)}function p(e){return d(e).versions}function m(e){const t=d(e);return l(t)}function h(e){const t=d(e),{pathname:n}=(0,r.TH)();return s(t,n)}function g(e){const t=d(e),{pathname:n}=(0,r.TH)();return function(e,t){const n=l(e);return{latestDocSuggestion:s(e,t).alternateDocVersions[n.name],latestVersionSuggestion:n}}(t,n)}},4313:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r={onRouteDidUpdate(e){let{location:t,previousLocation:n}=e;!n||t.pathname===n.pathname&&t.search===n.search&&t.hash===n.hash||setTimeout((()=>{window.gtag("event","page_view",{page_title:document.title,page_location:window.location.href,page_path:t.pathname+t.search+t.hash})}))}}},9957:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(4865),a=n.n(r);a().configure({showSpinner:!1});const o={onRouteUpdate(e){let{location:t,previousLocation:n}=e;if(n&&t.pathname!==n.pathname){const e=window.setTimeout((()=>{a().start()}),200);return()=>window.clearTimeout(e)}},onRouteDidUpdate(){a().done()}}},2251:(e,t,n)=>{"use strict";n.r(t);var r=n(7410),a=n(6809);!function(e){const{themeConfig:{prism:t}}=a.default,{additionalLanguages:r}=t;globalThis.Prism=e,r.forEach((e=>{n(6726)("./prism-"+e)})),delete globalThis.Prism}(r.Z)},4082:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294);const a="iconExternalLink_nPIU";function o(e){let{width:t=13.5,height:n=13.5}=e;return r.createElement("svg",{width:t,height:n,"aria-hidden":"true",viewBox:"0 0 24 24",className:a},r.createElement("path",{fill:"currentColor",d:"M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"}))}},1982:(e,t,n)=>{"use strict";n.d(t,{Z:()=>at});var r=n(7294),a=n(6010),o=n(3256),i=n(5463),l=n(3702),s=n(8181),u=n(7325),c=n(6775),d=n(3266);function f(e){e.setAttribute("tabindex","-1"),e.focus(),e.removeAttribute("tabindex")}const p="skipToContent_fXgn";function m(){const{containerRef:e,handleSkip:t}=function(){const e=(0,r.useRef)(null),{action:t}=(0,c.k6)(),n=(0,r.useCallback)((e=>{var t;e.preventDefault();const n=null!=(t=document.querySelector("main:first-of-type"))?t:document.querySelector("."+l.k.wrapper.main);n&&f(n)}),[]);return(0,d.S)((n=>{let{location:r}=n;e.current&&!r.hash&&"PUSH"===t&&f(e.current)})),{containerRef:e,handleSkip:n}}();return r.createElement("div",{ref:e,role:"region"},r.createElement("a",{href:"#",className:p,onClick:t},r.createElement(u.Z,{id:"theme.common.skipToMainContent",description:"The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation"},"Skip to main content")))}var h=n(107),g=n(5830),v=n(7462);function b(e){let{width:t=21,height:n=21,color:a="currentColor",strokeWidth:o=1.2,className:i,...l}=e;return r.createElement("svg",(0,v.Z)({viewBox:"0 0 15 15",width:t,height:n},l),r.createElement("g",{stroke:a,strokeWidth:o},r.createElement("path",{d:"M.75.75l13.5 13.5M14.25.75L.75 14.25"})))}const y="announcementBar_mb4j",w="announcementBarPlaceholder_vyr4",k="announcementBarClose_gvF7",E="announcementBarContent_xLdY";function S(){const{isActive:e,close:t}=(0,g.nT)(),{announcementBar:n}=(0,h.L)();if(!e)return null;const{content:o,backgroundColor:i,textColor:l,isCloseable:s}=n;return r.createElement("div",{className:y,style:{backgroundColor:i,color:l},role:"banner"},s&&r.createElement("div",{className:w}),r.createElement("div",{className:E,dangerouslySetInnerHTML:{__html:o}}),s?r.createElement("button",{type:"button",className:(0,a.Z)("clean-btn close",k),onClick:t,"aria-label":(0,u.I)({id:"theme.AnnouncementBar.closeButtonAriaLabel",message:"Close",description:"The ARIA label for close button of announcement bar"})},r.createElement(b,{width:14,height:14,strokeWidth:3.1})):null)}var x=n(2600),_=n(2957);var C=n(3768),T=n(3086);const A=r.createContext(null);function L(e){let{children:t}=e;const n=function(){const e=(0,x.e)(),t=(0,T.HY)(),[n,a]=(0,r.useState)(!1),o=null!==t.component,i=(0,C.D9)(o);return(0,r.useEffect)((()=>{o&&!i&&a(!0)}),[o,i]),(0,r.useEffect)((()=>{o?e.shown||a(!0):a(!1)}),[e.shown,o]),(0,r.useMemo)((()=>[n,a]),[n])}();return r.createElement(A.Provider,{value:n},t)}function R(e){if(e.component){const t=e.component;return r.createElement(t,e.props)}}function P(){const e=(0,r.useContext)(A);if(!e)throw new C.i6("NavbarSecondaryMenuDisplayProvider");const[t,n]=e,a=(0,r.useCallback)((()=>n(!1)),[n]),o=(0,T.HY)();return(0,r.useMemo)((()=>({shown:t,hide:a,content:R(o)})),[a,o,t])}function N(e){let{header:t,primaryMenu:n,secondaryMenu:o}=e;const{shown:i}=P();return r.createElement("div",{className:"navbar-sidebar"},t,r.createElement("div",{className:(0,a.Z)("navbar-sidebar__items",{"navbar-sidebar__items--show-secondary":i})},r.createElement("div",{className:"navbar-sidebar__item menu"},n),r.createElement("div",{className:"navbar-sidebar__item menu"},o)))}var O=n(9200),D=n(1048);function I(e){return r.createElement("svg",(0,v.Z)({viewBox:"0 0 24 24",width:24,height:24},e),r.createElement("path",{fill:"currentColor",d:"M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"}))}function M(e){return r.createElement("svg",(0,v.Z)({viewBox:"0 0 24 24",width:24,height:24},e),r.createElement("path",{fill:"currentColor",d:"M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"}))}const F={toggle:"toggle_vylO",toggleButton:"toggleButton_gllP",darkToggleIcon:"darkToggleIcon_wfgR",lightToggleIcon:"lightToggleIcon_pyhR",toggleButtonDisabled:"toggleButtonDisabled_aARS"};function j(e){let{className:t,value:n,onChange:o}=e;const i=(0,D.Z)(),l=(0,u.I)({message:"Switch between dark and light mode (currently {mode})",id:"theme.colorToggle.ariaLabel",description:"The ARIA label for the navbar color mode toggle"},{mode:"dark"===n?(0,u.I)({message:"dark mode",id:"theme.colorToggle.ariaLabel.mode.dark",description:"The name for the dark color mode"}):(0,u.I)({message:"light mode",id:"theme.colorToggle.ariaLabel.mode.light",description:"The name for the light color mode"})});return r.createElement("div",{className:(0,a.Z)(F.toggle,t)},r.createElement("button",{className:(0,a.Z)("clean-btn",F.toggleButton,!i&&F.toggleButtonDisabled),type:"button",onClick:()=>o("dark"===n?"light":"dark"),disabled:!i,title:l,"aria-label":l},r.createElement(I,{className:(0,a.Z)(F.toggleIcon,F.lightToggleIcon)}),r.createElement(M,{className:(0,a.Z)(F.toggleIcon,F.darkToggleIcon)})))}const B=r.memo(j);function z(e){let{className:t}=e;const n=(0,h.L)().colorMode.disableSwitch,{colorMode:a,setColorMode:o}=(0,O.I)();return n?null:r.createElement(B,{className:t,value:a,onChange:o})}var U=n(6811);function $(){return r.createElement(U.Z,{className:"navbar__brand",imageClassName:"navbar__logo",titleClassName:"navbar__title text--truncate"})}function q(){const e=(0,x.e)();return r.createElement("button",{type:"button",className:"clean-btn navbar-sidebar__close",onClick:()=>e.toggle()},r.createElement(b,{color:"var(--ifm-color-emphasis-600)"}))}function G(){return r.createElement("div",{className:"navbar-sidebar__brand"},r.createElement($,null),r.createElement(z,{className:"margin-right--md"}),r.createElement(q,null))}var H=n(3699),Z=n(9524),V=n(2735);function W(e,t){return void 0!==e&&void 0!==t&&new RegExp(e,"gi").test(t)}var Y=n(4082);function K(e){let{activeBasePath:t,activeBaseRegex:n,to:a,href:o,label:i,html:l,isDropdownLink:s,prependBaseUrlToHref:u,...c}=e;const d=(0,Z.Z)(a),f=(0,Z.Z)(t),p=(0,Z.Z)(o,{forcePrependBaseUrl:!0}),m=i&&o&&!(0,V.Z)(o),h=l?{dangerouslySetInnerHTML:{__html:l}}:{children:r.createElement(r.Fragment,null,i,m&&r.createElement(Y.Z,s&&{width:12,height:12}))};return o?r.createElement(H.Z,(0,v.Z)({href:u?p:o},c,h)):r.createElement(H.Z,(0,v.Z)({to:d,isNavLink:!0},(t||n)&&{isActive:(e,t)=>n?W(n,t.pathname):t.pathname.startsWith(f)},c,h))}function Q(e){let{className:t,isDropdownItem:n=!1,...o}=e;const i=r.createElement(K,(0,v.Z)({className:(0,a.Z)(n?"dropdown__link":"navbar__item navbar__link",t),isDropdownLink:n},o));return n?r.createElement("li",null,i):i}function X(e){let{className:t,isDropdownItem:n,...o}=e;return r.createElement("li",{className:"menu__list-item"},r.createElement(K,(0,v.Z)({className:(0,a.Z)("menu__link",t)},o)))}function J(e){var t;let{mobile:n=!1,position:a,...o}=e;const i=n?X:Q;return r.createElement(i,(0,v.Z)({},o,{activeClassName:null!=(t=o.activeClassName)?t:n?"menu__link--active":"navbar__link--active"}))}var ee=n(4639),te=n(9003),ne=n(9962);function re(e,t){return e.some((e=>function(e,t){return!!(0,te.Mg)(e.to,t)||!!W(e.activeBaseRegex,t)||!(!e.activeBasePath||!t.startsWith(e.activeBasePath))}(e,t)))}function ae(e){var t;let{items:n,position:o,className:i,onClick:l,...s}=e;const u=(0,r.useRef)(null),[c,d]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{const e=e=>{u.current&&!u.current.contains(e.target)&&d(!1)};return document.addEventListener("mousedown",e),document.addEventListener("touchstart",e),()=>{document.removeEventListener("mousedown",e),document.removeEventListener("touchstart",e)}}),[u]),r.createElement("div",{ref:u,className:(0,a.Z)("navbar__item","dropdown","dropdown--hoverable",{"dropdown--right":"right"===o,"dropdown--show":c})},r.createElement(K,(0,v.Z)({"aria-haspopup":"true","aria-expanded":c,role:"button",href:s.to?void 0:"#",className:(0,a.Z)("navbar__link",i)},s,{onClick:s.to?void 0:e=>e.preventDefault(),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),d(!c))}}),null!=(t=s.children)?t:s.label),r.createElement("ul",{className:"dropdown__menu"},n.map(((e,t)=>r.createElement(be,(0,v.Z)({isDropdownItem:!0,onKeyDown:e=>{if(t===n.length-1&&"Tab"===e.key){e.preventDefault(),d(!1);const t=u.current.nextElementSibling;if(t){(t instanceof HTMLAnchorElement?t:t.querySelector("a")).focus()}}},activeClassName:"dropdown__link--active"},e,{key:t}))))))}function oe(e){var t;let{items:n,className:o,position:i,onClick:l,...s}=e;const u=function(){const{siteConfig:{baseUrl:e}}=(0,ne.Z)(),{pathname:t}=(0,c.TH)();return t.replace(e,"/")}(),d=re(n,u),{collapsed:f,toggleCollapsed:p,setCollapsed:m}=(0,ee.u)({initialState:()=>!d});return(0,r.useEffect)((()=>{d&&m(!d)}),[u,d,m]),r.createElement("li",{className:(0,a.Z)("menu__list-item",{"menu__list-item--collapsed":f})},r.createElement(K,(0,v.Z)({role:"button",className:(0,a.Z)("menu__link menu__link--sublist menu__link--sublist-caret",o)},s,{onClick:e=>{e.preventDefault(),p()}}),null!=(t=s.children)?t:s.label),r.createElement(ee.z,{lazy:!0,as:"ul",className:"menu__list",collapsed:f},n.map(((e,t)=>r.createElement(be,(0,v.Z)({mobile:!0,isDropdownItem:!0,onClick:l,activeClassName:"menu__link--active"},e,{key:t}))))))}function ie(e){let{mobile:t=!1,...n}=e;const a=t?oe:ae;return r.createElement(a,n)}var le=n(626);function se(e){let{width:t=20,height:n=20,...a}=e;return r.createElement("svg",(0,v.Z)({viewBox:"0 0 24 24",width:t,height:n,"aria-hidden":!0},a),r.createElement("path",{fill:"currentColor",d:"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"}))}const ue="iconLanguage_nlXk";const ce=()=>null,de="searchBox_ZlJk";function fe(e){let{children:t,className:n}=e;return r.createElement("div",{className:(0,a.Z)(n,de)},t)}var pe=n(9871),me=n(3734);var he=n(6409);const ge=e=>e.docs.find((t=>t.id===e.mainDocId));const ve={default:J,localeDropdown:function(e){let{mobile:t,dropdownItemsBefore:n,dropdownItemsAfter:a,...o}=e;const{i18n:{currentLocale:i,locales:l,localeConfigs:s}}=(0,ne.Z)(),c=(0,le.l)(),d=[...n,...l.map((e=>{const n="pathname://"+c.createUrl({locale:e,fullyQualified:!1});return{label:s[e].label,to:n,target:"_self",autoAddBaseUrl:!1,className:e===i?t?"menu__link--active":"dropdown__link--active":""}})),...a],f=t?(0,u.I)({message:"Languages",id:"theme.navbar.mobileLanguageDropdown.label",description:"The label for the mobile language switcher dropdown"}):s[i].label;return r.createElement(ie,(0,v.Z)({},o,{mobile:t,label:r.createElement(r.Fragment,null,r.createElement(se,{className:ue}),f),items:d}))},search:function(e){let{mobile:t,className:n}=e;return t?null:r.createElement(fe,{className:n},r.createElement(ce,null))},dropdown:ie,html:function(e){let{value:t,className:n,mobile:o=!1,isDropdownItem:i=!1}=e;const l=i?"li":"div";return r.createElement(l,{className:(0,a.Z)({navbar__item:!o&&!i,"menu__list-item":o},n),dangerouslySetInnerHTML:{__html:t}})},doc:function(e){let{docId:t,label:n,docsPluginId:a,...o}=e;const{activeDoc:i}=(0,pe.Iw)(a),l=(0,me.vY)(t,a);return null===l?null:r.createElement(J,(0,v.Z)({exact:!0},o,{isActive:()=>(null==i?void 0:i.path)===l.path||!(null==i||!i.sidebar)&&i.sidebar===l.sidebar,label:null!=n?n:l.id,to:l.path}))},docSidebar:function(e){let{sidebarId:t,label:n,docsPluginId:a,...o}=e;const{activeDoc:i}=(0,pe.Iw)(a),l=(0,me.oz)(t,a).link;if(!l)throw new Error('DocSidebarNavbarItem: Sidebar with ID "'+t+"\" doesn't have anything to be linked to.");return r.createElement(J,(0,v.Z)({exact:!0},o,{isActive:()=>(null==i?void 0:i.sidebar)===t,label:null!=n?n:l.label,to:l.path}))},docsVersion:function(e){let{label:t,to:n,docsPluginId:a,...o}=e;const i=(0,me.lO)(a)[0],l=null!=t?t:i.label,s=null!=n?n:(e=>e.docs.find((t=>t.id===e.mainDocId)))(i).path;return r.createElement(J,(0,v.Z)({},o,{label:l,to:s}))},docsVersionDropdown:function(e){let{mobile:t,docsPluginId:n,dropdownActiveClassDisabled:a,dropdownItemsBefore:o,dropdownItemsAfter:i,...l}=e;const s=(0,pe.Iw)(n),c=(0,pe.gB)(n),{savePreferredVersionName:d}=(0,he.J)(n),f=[...o,...c.map((e=>{var t;const n=null!=(t=s.alternateDocVersions[e.name])?t:ge(e);return{label:e.label,to:n.path,isActive:()=>e===s.activeVersion,onClick:()=>d(e.name)}})),...i],p=(0,me.lO)(n)[0],m=t&&f.length>1?(0,u.I)({id:"theme.navbar.mobileVersionsDropdown.label",message:"Versions",description:"The label for the navbar versions dropdown on mobile view"}):p.label,h=t&&f.length>1?void 0:ge(p).path;return f.length<=1?r.createElement(J,(0,v.Z)({},l,{mobile:t,label:m,to:h,isActive:a?()=>!1:void 0})):r.createElement(ie,(0,v.Z)({},l,{mobile:t,label:m,to:h,items:f,isActive:a?()=>!1:void 0}))}};function be(e){let{type:t,...n}=e;const a=function(e,t){return e&&"default"!==e?e:"items"in t?"dropdown":"default"}(t,n),o=ve[a];if(!o)throw new Error('No NavbarItem component found for type "'+t+'".');return r.createElement(o,n)}function ye(){const e=(0,x.e)(),t=(0,h.L)().navbar.items;return r.createElement("ul",{className:"menu__list"},t.map(((t,n)=>r.createElement(be,(0,v.Z)({mobile:!0},t,{onClick:()=>e.toggle(),key:n})))))}function we(e){return r.createElement("button",(0,v.Z)({},e,{type:"button",className:"clean-btn navbar-sidebar__back"}),r.createElement(u.Z,{id:"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel",description:"The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)"},"\u2190 Back to main menu"))}function ke(){const e=0===(0,h.L)().navbar.items.length,t=P();return r.createElement(r.Fragment,null,!e&&r.createElement(we,{onClick:()=>t.hide()}),t.content)}function Ee(){const e=(0,x.e)();var t;return void 0===(t=e.shown)&&(t=!0),(0,r.useEffect)((()=>(document.body.style.overflow=t?"hidden":"visible",()=>{document.body.style.overflow="visible"})),[t]),e.shouldRender?r.createElement(N,{header:r.createElement(G,null),primaryMenu:r.createElement(ye,null),secondaryMenu:r.createElement(ke,null)}):null}const Se="navbarHideable_m1mJ",xe="navbarHidden_jGov";function _e(e){return r.createElement("div",(0,v.Z)({role:"presentation"},e,{className:(0,a.Z)("navbar-sidebar__backdrop",e.className)}))}function Ce(e){let{children:t}=e;const{navbar:{hideOnScroll:n,style:o}}=(0,h.L)(),i=(0,x.e)(),{navbarRef:l,isNavbarVisible:s}=function(e){const[t,n]=(0,r.useState)(e),a=(0,r.useRef)(!1),o=(0,r.useRef)(0),i=(0,r.useCallback)((e=>{null!==e&&(o.current=e.getBoundingClientRect().height)}),[]);return(0,_.RF)(((t,r)=>{let{scrollY:i}=t;if(!e)return;if(i=l?n(!1):i+u{if(e)return t.location.hash?(a.current=!0,void n(!1)):void n(!0)})),{navbarRef:i,isNavbarVisible:t}}(n);return r.createElement("nav",{ref:l,className:(0,a.Z)("navbar","navbar--fixed-top",n&&[Se,!s&&xe],{"navbar--dark":"dark"===o,"navbar--primary":"primary"===o,"navbar-sidebar--show":i.shown})},t,r.createElement(_e,{onClick:i.toggle}),r.createElement(Ee,null))}function Te(e){let{width:t=30,height:n=30,className:a,...o}=e;return r.createElement("svg",(0,v.Z)({className:a,width:t,height:n,viewBox:"0 0 30 30","aria-hidden":"true"},o),r.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeMiterlimit:"10",strokeWidth:"2",d:"M4 7h22M4 15h22M4 23h22"}))}function Ae(){const e=(0,x.e)();return r.createElement("button",{onClick:e.toggle,onKeyDown:e.toggle,"aria-label":"Navigation bar toggle",className:"navbar__toggle clean-btn",type:"button",tabIndex:0},r.createElement(Te,null))}const Le="colorModeToggle_DEke";function Re(e){let{items:t}=e;return r.createElement(r.Fragment,null,t.map(((e,t)=>r.createElement(be,(0,v.Z)({},e,{key:t})))))}function Pe(e){let{left:t,right:n}=e;return r.createElement("div",{className:"navbar__inner"},r.createElement("div",{className:"navbar__items"},t),r.createElement("div",{className:"navbar__items navbar__items--right"},n))}function Ne(){const e=(0,x.e)(),t=(0,h.L)().navbar.items,[n,a]=function(e){function t(e){var t;return"left"===(null!=(t=e.position)?t:"right")}return[e.filter(t),e.filter((e=>!t(e)))]}(t),o=t.find((e=>"search"===e.type));return r.createElement(Pe,{left:r.createElement(r.Fragment,null,!e.disabled&&r.createElement(Ae,null),r.createElement($,null),r.createElement(Re,{items:n})),right:r.createElement(r.Fragment,null,r.createElement(Re,{items:a}),r.createElement(z,{className:Le}),!o&&r.createElement(fe,null,r.createElement(ce,null)))})}function Oe(){return r.createElement(Ce,null,r.createElement(Ne,null))}function De(e){return r.createElement(r.Fragment,null,r.createElement("div",{style:{background:"#f0ffc4",textAlign:"center",padding:"16px 32px",color:"var(--ifm-color-primary-dark)",fontWeight:"bold",fontSize:"0.875rem"}},"This project is an ",r.createElement("b",null,"incubation project")," being run inside the Green Software Foundation; as such, we DON\u2019T recommend using it in any critical use case. Incubation projects are experimental, offer no support guarantee, have minimal governance and process, and may be retired at any moment. This project may one day Graduate, in which case this disclaimer will be removed."),r.createElement(Oe,e))}function Ie(e){let{item:t}=e;const{to:n,href:a,label:o,prependBaseUrlToHref:i,...l}=t,s=(0,Z.Z)(n),u=(0,Z.Z)(a,{forcePrependBaseUrl:!0});return r.createElement(H.Z,(0,v.Z)({className:"footer__link-item"},a?{href:i?u:a}:{to:s},l),o,a&&!(0,V.Z)(a)&&r.createElement(Y.Z,null))}function Me(e){var t;let{item:n}=e;return n.html?r.createElement("li",{className:"footer__item",dangerouslySetInnerHTML:{__html:n.html}}):r.createElement("li",{key:null!=(t=n.href)?t:n.to,className:"footer__item"},r.createElement(Ie,{item:n}))}function Fe(e){let{column:t}=e;return r.createElement("div",{className:"col footer__col"},r.createElement("div",{className:"footer__title"},t.title),r.createElement("ul",{className:"footer__items clean-list"},t.items.map(((e,t)=>r.createElement(Me,{key:t,item:e})))))}function je(e){let{columns:t}=e;return r.createElement("div",{className:"row footer__links"},t.map(((e,t)=>r.createElement(Fe,{key:t,column:e}))))}function Be(){return r.createElement("span",{className:"footer__link-separator"},"\xb7")}function ze(e){let{item:t}=e;return t.html?r.createElement("span",{className:"footer__link-item",dangerouslySetInnerHTML:{__html:t.html}}):r.createElement(Ie,{item:t})}function Ue(e){let{links:t}=e;return r.createElement("div",{className:"footer__links text--center"},r.createElement("div",{className:"footer__links"},t.map(((e,n)=>r.createElement(r.Fragment,{key:n},r.createElement(ze,{item:e}),t.length!==n+1&&r.createElement(Be,null))))))}function $e(e){let{links:t}=e;return function(e){return"title"in e[0]}(t)?r.createElement(je,{columns:t}):r.createElement(Ue,{links:t})}var qe=n(7909);const Ge="footerLogoLink_BH7S";function He(e){var t;let{logo:n}=e;const{withBaseUrl:o}=(0,Z.C)(),i={light:o(n.src),dark:o(null!=(t=n.srcDark)?t:n.src)};return r.createElement(qe.Z,{className:(0,a.Z)("footer__logo",n.className),alt:n.alt,sources:i,width:n.width,height:n.height,style:n.style})}function Ze(e){let{logo:t}=e;return t.href?r.createElement(H.Z,{href:t.href,className:Ge,target:t.target},r.createElement(He,{logo:t})):r.createElement(He,{logo:t})}function Ve(e){let{copyright:t}=e;return r.createElement("div",{className:"footer__copyright",dangerouslySetInnerHTML:{__html:t}})}function We(e){let{style:t,links:n,logo:o,copyright:i}=e;return r.createElement("footer",{className:(0,a.Z)("footer",{"footer--dark":"dark"===t})},r.createElement("div",{className:"container container-fluid"},n,(o||i)&&r.createElement("div",{className:"footer__bottom text--center"},o&&r.createElement("div",{className:"margin-bottom--sm"},o),i)))}function Ye(){const{footer:e}=(0,h.L)();if(!e)return null;const{copyright:t,links:n,logo:a,style:o}=e;return r.createElement(We,{style:o,links:n&&n.length>0&&r.createElement($e,{links:n}),logo:a&&r.createElement(Ze,{logo:a}),copyright:t&&r.createElement(Ve,{copyright:t})})}const Ke=r.memo(Ye);var Qe=n(2560);const Xe="docusaurus.tab.",Je=r.createContext(void 0);const et=(0,C.Qc)([O.S,g.pl,function(e){let{children:t}=e;const n=function(){const[e,t]=(0,r.useState)({}),n=(0,r.useCallback)(((e,t)=>{(0,Qe.W)("docusaurus.tab."+e).set(t)}),[]);(0,r.useEffect)((()=>{try{const e={};(0,Qe._)().forEach((t=>{if(t.startsWith(Xe)){const n=t.substring(Xe.length);e[n]=(0,Qe.W)(t).get()}})),t(e)}catch(e){console.error(e)}}),[]);const a=(0,r.useCallback)(((e,r)=>{t((t=>({...t,[e]:r}))),n(e,r)}),[n]);return(0,r.useMemo)((()=>({tabGroupChoices:e,setTabGroupChoices:a})),[e,a])}();return r.createElement(Je.Provider,{value:n},t)},_.OC,he.L5,i.VC,function(e){let{children:t}=e;return r.createElement(T.n2,null,r.createElement(x.M,null,r.createElement(L,null,t)))}]);function tt(e){let{children:t}=e;return r.createElement(et,null,t)}function nt(e){let{error:t,tryAgain:n}=e;return r.createElement("main",{className:"container margin-vert--xl"},r.createElement("div",{className:"row"},r.createElement("div",{className:"col col--6 col--offset-3"},r.createElement("h1",{className:"hero__title"},r.createElement(u.Z,{id:"theme.ErrorPageContent.title",description:"The title of the fallback page when the page crashed"},"This page crashed.")),r.createElement("p",null,t.message),r.createElement("div",null,r.createElement("button",{type:"button",onClick:n},r.createElement(u.Z,{id:"theme.ErrorPageContent.tryAgain",description:"The label of the button to try again when the page crashed"},"Try again"))))))}const rt="mainWrapper_z2l0";function at(e){const{children:t,noFooter:n,wrapperClassName:u,title:c,description:d}=e;return(0,s.t)(),r.createElement(tt,null,r.createElement(i.d,{title:c,description:d}),r.createElement(m,null),r.createElement(S,null),r.createElement(De,null),r.createElement("div",{className:(0,a.Z)(l.k.wrapper.main,rt,u)},r.createElement(o.Z,{fallback:e=>r.createElement(nt,e)},t)),!n&&r.createElement(Ke,null))}},6811:(e,t,n)=>{"use strict";n.d(t,{Z:()=>d});var r=n(7462),a=n(7294),o=n(3699),i=n(9524),l=n(9962),s=n(107),u=n(7909);function c(e){let{logo:t,alt:n,imageClassName:r}=e;const o={light:(0,i.Z)(t.src),dark:(0,i.Z)(t.srcDark||t.src)},l=a.createElement(u.Z,{className:t.className,sources:o,height:t.height,width:t.width,alt:n,style:t.style});return r?a.createElement("div",{className:r},l):l}function d(e){var t;const{siteConfig:{title:n}}=(0,l.Z)(),{navbar:{title:u,logo:d}}=(0,s.L)(),{imageClassName:f,titleClassName:p,...m}=e,h=(0,i.Z)((null==d?void 0:d.href)||"/"),g=u?"":n,v=null!=(t=null==d?void 0:d.alt)?t:g;return a.createElement(o.Z,(0,r.Z)({to:h},m,(null==d?void 0:d.target)&&{target:d.target}),d&&a.createElement(c,{logo:d,alt:v,imageClassName:f}),null!=u&&a.createElement("b",{className:p},u))}},3647:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(1514);function o(e){let{locale:t,version:n,tag:o}=e;const i=t;return r.createElement(a.Z,null,t&&r.createElement("meta",{name:"docusaurus_locale",content:t}),n&&r.createElement("meta",{name:"docusaurus_version",content:n}),o&&r.createElement("meta",{name:"docusaurus_tag",content:o}),i&&r.createElement("meta",{name:"docsearch:language",content:i}),n&&r.createElement("meta",{name:"docsearch:version",content:n}),o&&r.createElement("meta",{name:"docsearch:docusaurus_tag",content:o}))}},7909:(e,t,n)=>{"use strict";n.d(t,{Z:()=>u});var r=n(7462),a=n(7294),o=n(6010),i=n(1048),l=n(9200);const s={themedImage:"themedImage_ToTc","themedImage--light":"themedImage--light_HNdA","themedImage--dark":"themedImage--dark_i4oU"};function u(e){const t=(0,i.Z)(),{colorMode:n}=(0,l.I)(),{sources:u,className:c,alt:d,...f}=e,p=t?"dark"===n?["dark"]:["light"]:["light","dark"];return a.createElement(a.Fragment,null,p.map((e=>a.createElement("img",(0,r.Z)({key:e,src:u[e],alt:d,className:(0,o.Z)(s.themedImage,s["themedImage--"+e],c)},f)))))}},4639:(e,t,n)=>{"use strict";n.d(t,{u:()=>i,z:()=>m});var r=n(7462),a=n(7294),o=n(6136);function i(e){let{initialState:t}=e;const[n,r]=(0,a.useState)(null!=t&&t),o=(0,a.useCallback)((()=>{r((e=>!e))}),[]);return{collapsed:n,setCollapsed:r,toggleCollapsed:o}}const l={display:"none",overflow:"hidden",height:"0px"},s={display:"block",overflow:"visible",height:"auto"};function u(e,t){const n=t?l:s;e.style.display=n.display,e.style.overflow=n.overflow,e.style.height=n.height}function c(e){let{collapsibleRef:t,collapsed:n,animation:r}=e;const o=(0,a.useRef)(!1);(0,a.useEffect)((()=>{const e=t.current;function a(){var t,n;const a=e.scrollHeight,o=null!=(t=null==r?void 0:r.duration)?t:function(e){const t=e/36;return Math.round(10*(4+15*t**.25+t/5))}(a);return{transition:"height "+o+"ms "+(null!=(n=null==r?void 0:r.easing)?n:"ease-in-out"),height:a+"px"}}function i(){const t=a();e.style.transition=t.transition,e.style.height=t.height}if(!o.current)return u(e,n),void(o.current=!0);return e.style.willChange="height",function(){const t=requestAnimationFrame((()=>{n?(i(),requestAnimationFrame((()=>{e.style.height=l.height,e.style.overflow=l.overflow}))):(e.style.display="block",requestAnimationFrame((()=>{i()})))}));return()=>cancelAnimationFrame(t)}()}),[t,n,r])}function d(e){if(!o.Z.canUseDOM)return e?l:s}function f(e){let{as:t="div",collapsed:n,children:r,animation:o,onCollapseTransitionEnd:i,className:l,disableSSRStyle:s}=e;const f=(0,a.useRef)(null);return c({collapsibleRef:f,collapsed:n,animation:o}),a.createElement(t,{ref:f,style:s?void 0:d(n),onTransitionEnd:e=>{"height"===e.propertyName&&(u(f.current,n),null==i||i(n))},className:l},r)}function p(e){let{collapsed:t,...n}=e;const[o,i]=(0,a.useState)(!t),[l,s]=(0,a.useState)(t);return(0,a.useLayoutEffect)((()=>{t||i(!0)}),[t]),(0,a.useLayoutEffect)((()=>{o&&s(t)}),[o,t]),o?a.createElement(f,(0,r.Z)({},n,{collapsed:l})):null}function m(e){let{lazy:t,...n}=e;const r=t?p:f;return a.createElement(r,n)}},5830:(e,t,n)=>{"use strict";n.d(t,{nT:()=>m,pl:()=>p});var r=n(7294),a=n(1048),o=n(2560),i=n(3768),l=n(107);const s=(0,o.W)("docusaurus.announcement.dismiss"),u=(0,o.W)("docusaurus.announcement.id"),c=()=>"true"===s.get(),d=e=>s.set(String(e)),f=r.createContext(null);function p(e){let{children:t}=e;const n=function(){const{announcementBar:e}=(0,l.L)(),t=(0,a.Z)(),[n,o]=(0,r.useState)((()=>!!t&&c()));(0,r.useEffect)((()=>{o(c())}),[]);const i=(0,r.useCallback)((()=>{d(!0),o(!0)}),[]);return(0,r.useEffect)((()=>{if(!e)return;const{id:t}=e;let n=u.get();"annoucement-bar"===n&&(n="announcement-bar");const r=t!==n;u.set(t),r&&d(!1),!r&&c()||o(!1)}),[e]),(0,r.useMemo)((()=>({isActive:!!e&&!n,close:i})),[e,n,i])}();return r.createElement(f.Provider,{value:n},t)}function m(){const e=(0,r.useContext)(f);if(!e)throw new i.i6("AnnouncementBarProvider");return e}},9200:(e,t,n)=>{"use strict";n.d(t,{I:()=>g,S:()=>h});var r=n(7294),a=n(6136),o=n(3768),i=n(2560),l=n(107);const s=r.createContext(void 0),u="theme",c=(0,i.W)(u),d="light",f="dark",p=e=>e===f?f:d;function m(){const{colorMode:{defaultMode:e,disableSwitch:t,respectPrefersColorScheme:n}}=(0,l.L)(),[o,i]=(0,r.useState)((e=>a.Z.canUseDOM?p(document.documentElement.getAttribute("data-theme")):p(e))(e));(0,r.useEffect)((()=>{t&&c.del()}),[t]);const s=(0,r.useCallback)((function(t,r){void 0===r&&(r={});const{persist:a=!0}=r;t?(i(t),a&&(e=>{c.set(p(e))})(t)):(i(n?window.matchMedia("(prefers-color-scheme: dark)").matches?f:d:e),c.del())}),[n,e]);(0,r.useEffect)((()=>{document.documentElement.setAttribute("data-theme",p(o))}),[o]),(0,r.useEffect)((()=>{if(t)return;const e=e=>{if(e.key!==u)return;const t=c.get();null!==t&&s(p(t))};return window.addEventListener("storage",e),()=>window.removeEventListener("storage",e)}),[t,s]);const m=(0,r.useRef)(!1);return(0,r.useEffect)((()=>{if(t&&!n)return;const e=window.matchMedia("(prefers-color-scheme: dark)"),r=()=>{window.matchMedia("print").matches||m.current?m.current=window.matchMedia("print").matches:s(null)};return e.addListener(r),()=>e.removeListener(r)}),[s,t,n]),(0,r.useMemo)((()=>({colorMode:o,setColorMode:s,get isDarkTheme(){return o===f},setLightTheme(){s(d)},setDarkTheme(){s(f)}})),[o,s])}function h(e){let{children:t}=e;const n=m();return r.createElement(s.Provider,{value:n},t)}function g(){const e=(0,r.useContext)(s);if(null==e)throw new o.i6("ColorModeProvider","Please see https://docusaurus.io/docs/api/themes/configuration#use-color-mode.");return e}},6409:(e,t,n)=>{"use strict";n.d(t,{J:()=>y,L5:()=>v});var r=n(7294),a=n(9871),o=n(6875),i=n(107),l=n(3734),s=n(3768),u=n(2560);const c=e=>"docs-preferred-version-"+e,d=(e,t,n)=>{(0,u.W)(c(e),{persistence:t}).set(n)},f=(e,t)=>(0,u.W)(c(e),{persistence:t}).get(),p=(e,t)=>{(0,u.W)(c(e),{persistence:t}).del()};const m=r.createContext(null);function h(){const e=(0,a._r)(),t=(0,i.L)().docs.versionPersistence,n=(0,r.useMemo)((()=>Object.keys(e)),[e]),[o,l]=(0,r.useState)((()=>(e=>Object.fromEntries(e.map((e=>[e,{preferredVersionName:null}]))))(n)));(0,r.useEffect)((()=>{l(function(e){let{pluginIds:t,versionPersistence:n,allDocsData:r}=e;function a(e){const t=f(e,n);return r[e].versions.some((e=>e.name===t))?{preferredVersionName:t}:(p(e,n),{preferredVersionName:null})}return Object.fromEntries(t.map((e=>[e,a(e)])))}({allDocsData:e,versionPersistence:t,pluginIds:n}))}),[e,t,n]);return[o,(0,r.useMemo)((()=>({savePreferredVersion:function(e,n){d(e,t,n),l((t=>({...t,[e]:{preferredVersionName:n}})))}})),[t])]}function g(e){let{children:t}=e;const n=h();return r.createElement(m.Provider,{value:n},t)}function v(e){let{children:t}=e;return l.cE?r.createElement(g,null,t):r.createElement(r.Fragment,null,t)}function b(){const e=(0,r.useContext)(m);if(!e)throw new s.i6("DocsPreferredVersionContextProvider");return e}function y(e){var t;void 0===e&&(e=o.m);const n=(0,a.zh)(e),[i,l]=b(),{preferredVersionName:s}=i[e];return{preferredVersion:null!=(t=n.versions.find((e=>e.name===s)))?t:null,savePreferredVersionName:(0,r.useCallback)((t=>{l.savePreferredVersion(e,t)}),[l,e])}}},4432:(e,t,n)=>{"use strict";n.d(t,{V:()=>s,b:()=>l});var r=n(7294),a=n(3768);const o=Symbol("EmptyContext"),i=r.createContext(o);function l(e){let{children:t,name:n,items:a}=e;const o=(0,r.useMemo)((()=>n&&a?{name:n,items:a}:null),[n,a]);return r.createElement(i.Provider,{value:o},t)}function s(){const e=(0,r.useContext)(i);if(e===o)throw new a.i6("DocsSidebarProvider");return e}},2600:(e,t,n)=>{"use strict";n.d(t,{M:()=>f,e:()=>p});var r=n(7294),a=n(3086),o=n(3488),i=n(6775),l=n(3768);function s(e){!function(e){const t=(0,i.k6)(),n=(0,l.zX)(e);(0,r.useEffect)((()=>t.block(((e,t)=>n(e,t)))),[t,n])}(((t,n)=>{if("POP"===n)return e(t,n)}))}var u=n(107);const c=r.createContext(void 0);function d(){const e=function(){const e=(0,a.HY)(),{items:t}=(0,u.L)().navbar;return 0===t.length&&!e.component}(),t=(0,o.i)(),n=!e&&"mobile"===t,[i,l]=(0,r.useState)(!1);s((()=>{if(i)return l(!1),!1}));const c=(0,r.useCallback)((()=>{l((e=>!e))}),[]);return(0,r.useEffect)((()=>{"desktop"===t&&l(!1)}),[t]),(0,r.useMemo)((()=>({disabled:e,shouldRender:n,toggle:c,shown:i})),[e,n,c,i])}function f(e){let{children:t}=e;const n=d();return r.createElement(c.Provider,{value:n},t)}function p(){const e=r.useContext(c);if(void 0===e)throw new l.i6("NavbarMobileSidebarProvider");return e}},3086:(e,t,n)=>{"use strict";n.d(t,{HY:()=>l,Zo:()=>s,n2:()=>i});var r=n(7294),a=n(3768);const o=r.createContext(null);function i(e){let{children:t}=e;const n=(0,r.useState)({component:null,props:null});return r.createElement(o.Provider,{value:n},t)}function l(){const e=(0,r.useContext)(o);if(!e)throw new a.i6("NavbarSecondaryMenuContentProvider");return e[0]}function s(e){let{component:t,props:n}=e;const i=(0,r.useContext)(o);if(!i)throw new a.i6("NavbarSecondaryMenuContentProvider");const[,l]=i,s=(0,a.Ql)(n);return(0,r.useEffect)((()=>{l({component:t,props:s})}),[l,t,s]),(0,r.useEffect)((()=>()=>l({component:null,props:null})),[l]),null}},8181:(e,t,n)=>{"use strict";n.d(t,{h:()=>a,t:()=>o});var r=n(7294);const a="navigation-with-keyboard";function o(){(0,r.useEffect)((()=>{function e(e){"keydown"===e.type&&"Tab"===e.key&&document.body.classList.add(a),"mousedown"===e.type&&document.body.classList.remove(a)}return document.addEventListener("keydown",e),document.addEventListener("mousedown",e),()=>{document.body.classList.remove(a),document.removeEventListener("keydown",e),document.removeEventListener("mousedown",e)}}),[])}},3488:(e,t,n)=>{"use strict";n.d(t,{i:()=>u});var r=n(7294),a=n(6136);const o="desktop",i="mobile",l="ssr";function s(){return a.Z.canUseDOM?window.innerWidth>996?o:i:l}function u(){const[e,t]=(0,r.useState)((()=>s()));return(0,r.useEffect)((()=>{function e(){t(s())}return window.addEventListener("resize",e),()=>{window.removeEventListener("resize",e),clearTimeout(undefined)}}),[]),e}},3702:(e,t,n)=>{"use strict";n.d(t,{k:()=>r});const r={page:{blogListPage:"blog-list-page",blogPostPage:"blog-post-page",blogTagsListPage:"blog-tags-list-page",blogTagPostListPage:"blog-tags-post-list-page",docsDocPage:"docs-doc-page",docsTagsListPage:"docs-tags-list-page",docsTagDocListPage:"docs-tags-doc-list-page",mdxPage:"mdx-page"},wrapper:{main:"main-wrapper",blogPages:"blog-wrapper",docsPages:"docs-wrapper",mdxPages:"mdx-wrapper"},common:{editThisPage:"theme-edit-this-page",lastUpdated:"theme-last-updated",backToTopButton:"theme-back-to-top-button",codeBlock:"theme-code-block",admonition:"theme-admonition",admonitionType:e=>"theme-admonition-"+e},layout:{},docs:{docVersionBanner:"theme-doc-version-banner",docVersionBadge:"theme-doc-version-badge",docBreadcrumbs:"theme-doc-breadcrumbs",docMarkdown:"theme-doc-markdown",docTocMobile:"theme-doc-toc-mobile",docTocDesktop:"theme-doc-toc-desktop",docFooter:"theme-doc-footer",docFooterTagsRow:"theme-doc-footer-tags-row",docFooterEditMetaRow:"theme-doc-footer-edit-meta-row",docSidebarContainer:"theme-doc-sidebar-container",docSidebarMenu:"theme-doc-sidebar-menu",docSidebarItemCategory:"theme-doc-sidebar-item-category",docSidebarItemLink:"theme-doc-sidebar-item-link",docSidebarItemCategoryLevel:e=>"theme-doc-sidebar-item-category-level-"+e,docSidebarItemLinkLevel:e=>"theme-doc-sidebar-item-link-level-"+e},blog:{}}},3734:(e,t,n)=>{"use strict";n.d(t,{Wl:()=>f,_F:()=>m,cE:()=>d,hI:()=>y,lO:()=>g,vY:()=>b,oz:()=>v,s1:()=>h});var r=n(7294),a=n(6775),o=n(8790),i=n(9871),l=n(6409),s=n(4432);function u(e){return Array.from(new Set(e))}var c=n(9003);const d=!!i._r;function f(e){if(e.href)return e.href;for(const t of e.items){if("link"===t.type)return t.href;if("category"===t.type){const e=f(t);if(e)return e}}}const p=(e,t)=>void 0!==e&&(0,c.Mg)(e,t);function m(e,t){return"link"===e.type?p(e.href,t):"category"===e.type&&(p(e.href,t)||((e,t)=>e.some((e=>m(e,t))))(e.items,t))}function h(){var e;const t=(0,s.V)(),{pathname:n}=(0,a.TH)();if(!1===(null==(e=(0,i.gA)())?void 0:e.pluginData.breadcrumbs)||!t)return null;const r=[];return function e(t){for(const a of t)if("category"===a.type&&((0,c.Mg)(a.href,n)||e(a.items))||"link"===a.type&&(0,c.Mg)(a.href,n))return r.push(a),!0;return!1}(t.items),r.reverse()}function g(e){const{activeVersion:t}=(0,i.Iw)(e),{preferredVersion:n}=(0,l.J)(e),a=(0,i.yW)(e);return(0,r.useMemo)((()=>u([t,n,a].filter(Boolean))),[t,n,a])}function v(e,t){const n=g(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.sidebars?Object.entries(e.sidebars):[])),r=t.find((t=>t[0]===e));if(!r)throw new Error("Can't find any sidebar with id \""+e+'" in version'+(n.length>1?"s":"")+" "+n.map((e=>e.name)).join(", ")+'".\n Available sidebar ids are:\n - '+Object.keys(t).join("\n- "));return r[1]}),[e,n])}function b(e,t){const n=g(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.docs)),r=t.find((t=>t.id===e));if(!r){if(n.flatMap((e=>e.draftIds)).includes(e))return null;throw new Error("DocNavbarItem: couldn't find any doc with id \""+e+'" in version'+(n.length>1?"s":"")+" "+n.map((e=>e.name)).join(", ")+'".\nAvailable doc ids are:\n- '+u(t.map((e=>e.id))).join("\n- "))}return r}),[e,n])}function y(e){let{route:t,versionMetadata:n}=e;const r=(0,a.TH)(),i=t.routes,l=i.find((e=>(0,a.LX)(r.pathname,e)));if(!l)return null;const s=l.sidebar,u=s?n.docsSidebars[s]:void 0;return{docElement:(0,o.H)(i),sidebarName:s,sidebarItems:u}}},5463:(e,t,n)=>{"use strict";n.d(t,{FG:()=>f,d:()=>c,VC:()=>p});var r=n(7294),a=n(6010),o=n(1514),i=n(9656);function l(){const e=r.useContext(i._);if(!e)throw new Error("Unexpected: no Docusaurus route context found");return e}var s=n(9524),u=n(9962);function c(e){let{title:t,description:n,keywords:a,image:i,children:l}=e;const c=function(e){const{siteConfig:t}=(0,u.Z)(),{title:n,titleDelimiter:r}=t;return null!=e&&e.trim().length?e.trim()+" "+r+" "+n:n}(t),{withBaseUrl:d}=(0,s.C)(),f=i?d(i,{absolute:!0}):void 0;return r.createElement(o.Z,null,t&&r.createElement("title",null,c),t&&r.createElement("meta",{property:"og:title",content:c}),n&&r.createElement("meta",{name:"description",content:n}),n&&r.createElement("meta",{property:"og:description",content:n}),a&&r.createElement("meta",{name:"keywords",content:Array.isArray(a)?a.join(","):a}),f&&r.createElement("meta",{property:"og:image",content:f}),f&&r.createElement("meta",{name:"twitter:image",content:f}),l)}const d=r.createContext(void 0);function f(e){let{className:t,children:n}=e;const i=r.useContext(d),l=(0,a.Z)(i,t);return r.createElement(d.Provider,{value:l},r.createElement(o.Z,null,r.createElement("html",{className:l})),n)}function p(e){let{children:t}=e;const n=l(),o="plugin-"+n.plugin.name.replace(/docusaurus-(?:plugin|theme)-(?:content-)?/gi,"");const i="plugin-id-"+n.plugin.id;return r.createElement(f,{className:(0,a.Z)(o,i)},t)}},3768:(e,t,n)=>{"use strict";n.d(t,{D9:()=>i,Qc:()=>u,Ql:()=>s,i6:()=>l,zX:()=>o});var r=n(7294);const a=n(6136).Z.canUseDOM?r.useLayoutEffect:r.useEffect;function o(e){const t=(0,r.useRef)(e);return a((()=>{t.current=e}),[e]),(0,r.useCallback)((function(){return t.current(...arguments)}),[])}function i(e){const t=(0,r.useRef)();return a((()=>{t.current=e})),t.current}class l extends Error{constructor(e,t){var n,r,a,o;super(),this.name="ReactContextError",this.message="Hook "+(null!=(n=null==(r=this.stack)||null==(a=r.split("\n")[1])||null==(o=a.match(/at (?:\w+\.)?(?\w+)/))?void 0:o.groups.name)?n:"")+" is called outside the <"+e+">. "+(null!=t?t:"")}}function s(e){const t=Object.entries(e);return t.sort(((e,t)=>e[0].localeCompare(t[0]))),(0,r.useMemo)((()=>e),t.flat())}function u(e){return t=>{let{children:n}=t;return r.createElement(r.Fragment,null,e.reduceRight(((e,t)=>r.createElement(t,null,e)),n))}}},9003:(e,t,n)=>{"use strict";n.d(t,{Mg:()=>i,Ns:()=>l});var r=n(7294),a=n(997),o=n(9962);function i(e,t){const n=e=>{var t;return null==(t=!e||e.endsWith("/")?e:e+"/")?void 0:t.toLowerCase()};return n(e)===n(t)}function l(){const{baseUrl:e}=(0,o.Z)().siteConfig;return(0,r.useMemo)((()=>function(e){let{baseUrl:t,routes:n}=e;function r(e){return e.path===t&&!0===e.exact}function a(e){return e.path===t&&!e.exact}return function e(t){if(0===t.length)return;return t.find(r)||e(t.filter(a).flatMap((e=>{var t;return null!=(t=e.routes)?t:[]})))}(n)}({routes:a.Z,baseUrl:e})),[e])}},2957:(e,t,n)=>{"use strict";n.d(t,{Ct:()=>f,OC:()=>s,RF:()=>d});var r=n(7294),a=n(6136),o=n(1048),i=n(3768);const l=r.createContext(void 0);function s(e){let{children:t}=e;const n=function(){const e=(0,r.useRef)(!0);return(0,r.useMemo)((()=>({scrollEventsEnabledRef:e,enableScrollEvents:()=>{e.current=!0},disableScrollEvents:()=>{e.current=!1}})),[])}();return r.createElement(l.Provider,{value:n},t)}function u(){const e=(0,r.useContext)(l);if(null==e)throw new i.i6("ScrollControllerProvider");return e}const c=()=>a.Z.canUseDOM?{scrollX:window.pageXOffset,scrollY:window.pageYOffset}:null;function d(e,t){void 0===t&&(t=[]);const{scrollEventsEnabledRef:n}=u(),a=(0,r.useRef)(c()),o=(0,i.zX)(e);(0,r.useEffect)((()=>{const e=()=>{if(!n.current)return;const e=c();o(e,a.current),a.current=e},t={passive:!0};return e(),window.addEventListener("scroll",e,t),()=>window.removeEventListener("scroll",e,t)}),[o,n,...t])}function f(){const e=(0,r.useRef)(null),t=(0,o.Z)()&&"smooth"===getComputedStyle(document.documentElement).scrollBehavior;return{startScroll:n=>{e.current=t?function(e){return window.scrollTo({top:e,behavior:"smooth"}),()=>{}}(n):function(e){let t=null;const n=document.documentElement.scrollTop>e;return function r(){const a=document.documentElement.scrollTop;(n&&a>e||!n&&at&&cancelAnimationFrame(t)}(n)},cancelScroll:()=>null==e.current?void 0:e.current()}}},246:(e,t,n)=>{"use strict";n.d(t,{HX:()=>r,os:()=>a});n(9962);const r="default";function a(e,t){return"docs-"+e+"-"+t}},2560:(e,t,n)=>{"use strict";n.d(t,{W:()=>l,_:()=>s});const r="localStorage";function a(e){if(void 0===e&&(e=r),"undefined"==typeof window)throw new Error("Browser storage is not available on Node.js/Docusaurus SSR process.");if("none"===e)return null;try{return window[e]}catch(n){return t=n,o||(console.warn("Docusaurus browser storage is not available.\nPossible reasons: running Docusaurus in an iframe, in an incognito browser session, or using too strict browser privacy settings.",t),o=!0),null}var t}let o=!1;const i={get:()=>null,set:()=>{},del:()=>{}};function l(e,t){if("undefined"==typeof window)return function(e){function t(){throw new Error('Illegal storage API usage for storage key "'+e+'".\nDocusaurus storage APIs are not supposed to be called on the server-rendering process.\nPlease only call storage APIs in effects and event handlers.')}return{get:t,set:t,del:t}}(e);const n=a(null==t?void 0:t.persistence);return null===n?i:{get:()=>{try{return n.getItem(e)}catch(t){return console.error("Docusaurus storage error, can't get key="+e,t),null}},set:t=>{try{n.setItem(e,t)}catch(r){console.error("Docusaurus storage error, can't set "+e+"="+t,r)}},del:()=>{try{n.removeItem(e)}catch(t){console.error("Docusaurus storage error, can't delete key="+e,t)}}}}function s(e){void 0===e&&(e=r);const t=a(e);if(!t)return[];const n=[];for(let r=0;r{"use strict";n.d(t,{l:()=>o});var r=n(9962),a=n(6775);function o(){const{siteConfig:{baseUrl:e,url:t},i18n:{defaultLocale:n,currentLocale:o}}=(0,r.Z)(),{pathname:i}=(0,a.TH)(),l=o===n?e:e.replace("/"+o+"/","/"),s=i.replace(e,"");return{createUrl:function(e){let{locale:r,fullyQualified:a}=e;return""+(a?t:"")+function(e){return e===n?""+l:""+l+e+"/"}(r)+s}}}},3266:(e,t,n)=>{"use strict";n.d(t,{S:()=>i});var r=n(7294),a=n(6775),o=n(3768);function i(e){const t=(0,a.TH)(),n=(0,o.D9)(t),i=(0,o.zX)(e);(0,r.useEffect)((()=>{n&&t!==n&&i({location:t,previousLocation:n})}),[i,t,n])}},107:(e,t,n)=>{"use strict";n.d(t,{L:()=>a});var r=n(9962);function a(){return(0,r.Z)().siteConfig.themeConfig}},4136:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){const{trailingSlash:n,baseUrl:r}=t;if(e.startsWith("#"))return e;if(void 0===n)return e;const[a]=e.split(/[#?]/),o="/"===a||a===r?a:(i=a,n?function(e){return e.endsWith("/")?e:e+"/"}(i):function(e){return e.endsWith("/")?e.slice(0,-1):e}(i));var i;return e.replace(a,o)}},3905:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.applyTrailingSlash=t.blogPostContainerID=void 0,t.blogPostContainerID="post-content";var a=n(4136);Object.defineProperty(t,"applyTrailingSlash",{enumerable:!0,get:function(){return r(a).default}})},6010:(e,t,n)=>{"use strict";function r(e){var t,n,a="";if("string"==typeof e||"number"==typeof e)a+=e;else if("object"==typeof e)if(Array.isArray(e))for(t=0;ta});const a=function(){for(var e,t,n=0,a="";n{"use strict";n.d(t,{lX:()=>w,q_:()=>C,ob:()=>p,PP:()=>A,Ep:()=>f});var r=n(7462);function a(e){return"/"===e.charAt(0)}function o(e,t){for(var n=t,r=n+1,a=e.length;r=0;f--){var p=i[f];"."===p?o(i,f):".."===p?(o(i,f),d++):d&&(o(i,f),d--)}if(!u)for(;d--;d)i.unshift("..");!u||""===i[0]||i[0]&&a(i[0])||i.unshift("");var m=i.join("/");return n&&"/"!==m.substr(-1)&&(m+="/"),m};var l=n(2177);function s(e){return"/"===e.charAt(0)?e:"/"+e}function u(e){return"/"===e.charAt(0)?e.substr(1):e}function c(e,t){return function(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}(e,t)?e.substr(t.length):e}function d(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function f(e){var t=e.pathname,n=e.search,r=e.hash,a=t||"/";return n&&"?"!==n&&(a+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(a+="#"===r.charAt(0)?r:"#"+r),a}function p(e,t,n,a){var o;"string"==typeof e?(o=function(e){var t=e||"/",n="",r="",a=t.indexOf("#");-1!==a&&(r=t.substr(a),t=t.substr(0,a));var o=t.indexOf("?");return-1!==o&&(n=t.substr(o),t=t.substr(0,o)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}}(e),o.state=t):(void 0===(o=(0,r.Z)({},e)).pathname&&(o.pathname=""),o.search?"?"!==o.search.charAt(0)&&(o.search="?"+o.search):o.search="",o.hash?"#"!==o.hash.charAt(0)&&(o.hash="#"+o.hash):o.hash="",void 0!==t&&void 0===o.state&&(o.state=t));try{o.pathname=decodeURI(o.pathname)}catch(l){throw l instanceof URIError?new URIError('Pathname "'+o.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):l}return n&&(o.key=n),a?o.pathname?"/"!==o.pathname.charAt(0)&&(o.pathname=i(o.pathname,a.pathname)):o.pathname=a.pathname:o.pathname||(o.pathname="/"),o}function m(){var e=null;var t=[];return{setPrompt:function(t){return e=t,function(){e===t&&(e=null)}},confirmTransitionTo:function(t,n,r,a){if(null!=e){var o="function"==typeof e?e(t,n):e;"string"==typeof o?"function"==typeof r?r(o,a):a(!0):a(!1!==o)}else a(!0)},appendListener:function(e){var n=!0;function r(){n&&e.apply(void 0,arguments)}return t.push(r),function(){n=!1,t=t.filter((function(e){return e!==r}))}},notifyListeners:function(){for(var e=arguments.length,n=new Array(e),r=0;rt?n.splice(t,n.length-t,a):n.push(a),d({action:r,location:a,index:t,entries:n})}}))},replace:function(e,t){var r="REPLACE",a=p(e,t,h(),w.location);c.confirmTransitionTo(a,r,n,(function(e){e&&(w.entries[w.index]=a,d({action:r,location:a}))}))},go:y,goBack:function(){y(-1)},goForward:function(){y(1)},canGo:function(e){var t=w.index+e;return t>=0&&t{"use strict";var r=n(9864),a={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},o={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},l={};function s(e){return r.isMemo(e)?i:l[e.$$typeof]||a}l[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},l[r.Memo]=i;var u=Object.defineProperty,c=Object.getOwnPropertyNames,d=Object.getOwnPropertySymbols,f=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,m=Object.prototype;e.exports=function e(t,n,r){if("string"!=typeof n){if(m){var a=p(n);a&&a!==m&&e(t,a,r)}var i=c(n);d&&(i=i.concat(d(n)));for(var l=s(t),h=s(n),g=0;g{"use strict";e.exports=function(e,t,n,r,a,o,i,l){if(!e){var s;if(void 0===t)s=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var u=[n,r,a,o,i,l],c=0;(s=new Error(t.replace(/%s/g,(function(){return u[c++]})))).name="Invariant Violation"}throw s.framesToPop=1,s}}},5826:e=>{e.exports=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)}},984:(e,t,n)=>{"use strict";n.r(t)},6930:(e,t,n)=>{"use strict";n.r(t)},4865:function(e,t,n){var r,a;r=function(){var e,t,n={version:"0.2.0"},r=n.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'
'};function a(e,t,n){return en?n:e}function o(e){return 100*(-1+e)}function i(e,t,n){var a;return(a="translate3d"===r.positionUsing?{transform:"translate3d("+o(e)+"%,0,0)"}:"translate"===r.positionUsing?{transform:"translate("+o(e)+"%,0)"}:{"margin-left":o(e)+"%"}).transition="all "+t+"ms "+n,a}n.configure=function(e){var t,n;for(t in e)void 0!==(n=e[t])&&e.hasOwnProperty(t)&&(r[t]=n);return this},n.status=null,n.set=function(e){var t=n.isStarted();e=a(e,r.minimum,1),n.status=1===e?null:e;var o=n.render(!t),u=o.querySelector(r.barSelector),c=r.speed,d=r.easing;return o.offsetWidth,l((function(t){""===r.positionUsing&&(r.positionUsing=n.getPositioningCSS()),s(u,i(e,c,d)),1===e?(s(o,{transition:"none",opacity:1}),o.offsetWidth,setTimeout((function(){s(o,{transition:"all "+c+"ms linear",opacity:0}),setTimeout((function(){n.remove(),t()}),c)}),c)):setTimeout(t,c)})),this},n.isStarted=function(){return"number"==typeof n.status},n.start=function(){n.status||n.set(0);var e=function(){setTimeout((function(){n.status&&(n.trickle(),e())}),r.trickleSpeed)};return r.trickle&&e(),this},n.done=function(e){return e||n.status?n.inc(.3+.5*Math.random()).set(1):this},n.inc=function(e){var t=n.status;return t?("number"!=typeof e&&(e=(1-t)*a(Math.random()*t,.1,.95)),t=a(t+e,0,.994),n.set(t)):n.start()},n.trickle=function(){return n.inc(Math.random()*r.trickleRate)},e=0,t=0,n.promise=function(r){return r&&"resolved"!==r.state()?(0===t&&n.start(),e++,t++,r.always((function(){0==--t?(e=0,n.done()):n.set((e-t)/e)})),this):this},n.render=function(e){if(n.isRendered())return document.getElementById("nprogress");c(document.documentElement,"nprogress-busy");var t=document.createElement("div");t.id="nprogress",t.innerHTML=r.template;var a,i=t.querySelector(r.barSelector),l=e?"-100":o(n.status||0),u=document.querySelector(r.parent);return s(i,{transition:"all 0 linear",transform:"translate3d("+l+"%,0,0)"}),r.showSpinner||(a=t.querySelector(r.spinnerSelector))&&p(a),u!=document.body&&c(u,"nprogress-custom-parent"),u.appendChild(t),t},n.remove=function(){d(document.documentElement,"nprogress-busy"),d(document.querySelector(r.parent),"nprogress-custom-parent");var e=document.getElementById("nprogress");e&&p(e)},n.isRendered=function(){return!!document.getElementById("nprogress")},n.getPositioningCSS=function(){var e=document.body.style,t="WebkitTransform"in e?"Webkit":"MozTransform"in e?"Moz":"msTransform"in e?"ms":"OTransform"in e?"O":"";return t+"Perspective"in e?"translate3d":t+"Transform"in e?"translate":"margin"};var l=function(){var e=[];function t(){var n=e.shift();n&&n(t)}return function(n){e.push(n),1==e.length&&t()}}(),s=function(){var e=["Webkit","O","Moz","ms"],t={};function n(e){return e.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,(function(e,t){return t.toUpperCase()}))}function r(t){var n=document.body.style;if(t in n)return t;for(var r,a=e.length,o=t.charAt(0).toUpperCase()+t.slice(1);a--;)if((r=e[a]+o)in n)return r;return t}function a(e){return e=n(e),t[e]||(t[e]=r(e))}function o(e,t,n){t=a(t),e.style[t]=n}return function(e,t){var n,r,a=arguments;if(2==a.length)for(n in t)void 0!==(r=t[n])&&t.hasOwnProperty(n)&&o(e,n,r);else o(e,a[1],a[2])}}();function u(e,t){return("string"==typeof e?e:f(e)).indexOf(" "+t+" ")>=0}function c(e,t){var n=f(e),r=n+t;u(n,t)||(e.className=r.substring(1))}function d(e,t){var n,r=f(e);u(e,t)&&(n=r.replace(" "+t+" "," "),e.className=n.substring(1,n.length-1))}function f(e){return(" "+(e.className||"")+" ").replace(/\s+/gi," ")}function p(e){e&&e.parentNode&&e.parentNode.removeChild(e)}return n},void 0===(a="function"==typeof r?r.call(t,n,t,e):r)||(e.exports=a)},7418:e=>{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;function a(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(a){return!1}}()?Object.assign:function(e,o){for(var i,l,s=a(e),u=1;u{"use strict";n.d(t,{Z:()=>o});var r=function(){var e=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,n={},r={util:{encode:function e(t){return t instanceof a?new a(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=d.reach);S+=E.value.length,E=E.next){var x=E.value;if(t.length>e.length)return;if(!(x instanceof a)){var _,C=1;if(b){if(!(_=o(k,S,e,v))||_.index>=e.length)break;var T=_.index,A=_.index+_[0].length,L=S;for(L+=E.value.length;T>=L;)L+=(E=E.next).value.length;if(S=L-=E.value.length,E.value instanceof a)continue;for(var R=E;R!==t.tail&&(Ld.reach&&(d.reach=D);var I=E.prev;if(N&&(I=s(t,I,N),S+=N.length),u(t,I,C),E=s(t,I,new a(f,g?r.tokenize(P,g):P,y,P)),O&&s(t,E,O),C>1){var M={cause:f+","+m,reach:D};i(e,t,n,E.prev,S,M),d&&M.reach>d.reach&&(d.reach=M.reach)}}}}}}function l(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function s(e,t,n){var r=t.next,a={value:n,prev:t,next:r};return t.next=a,r.prev=a,e.length++,a}function u(e,t,n){for(var r=t.next,a=0;a"+o.content+""},r}(),a=r;r.default=r,a.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},a.languages.markup.tag.inside["attr-value"].inside.entity=a.languages.markup.entity,a.languages.markup.doctype.inside["internal-subset"].inside=a.languages.markup,a.hooks.add("wrap",(function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))})),Object.defineProperty(a.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:a.languages[t]},n.cdata=/^$/i;var r={"included-cdata":{pattern://i,inside:n}};r["language-"+t]={pattern:/[\s\S]+/,inside:a.languages[t]};var o={};o[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,(function(){return e})),"i"),lookbehind:!0,greedy:!0,inside:r},a.languages.insertBefore("markup","cdata",o)}}),Object.defineProperty(a.languages.markup.tag,"addAttribute",{value:function(e,t){a.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:a.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),a.languages.html=a.languages.markup,a.languages.mathml=a.languages.markup,a.languages.svg=a.languages.markup,a.languages.xml=a.languages.extend("markup",{}),a.languages.ssml=a.languages.xml,a.languages.atom=a.languages.xml,a.languages.rss=a.languages.xml,function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",n={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},r={bash:n,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:r},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:n}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:r},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:r.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:r.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},n.inside=e.languages.bash;for(var a=["comment","function-name","for-or-select","assign-left","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=r.variable[1].inside,i=0;i]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},a.languages.c=a.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),a.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),a.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},a.languages.c.string],char:a.languages.c.char,comment:a.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:a.languages.c}}}}),a.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete a.languages.c.boolean,function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n=/\b(?!)\w+(?:\s*\.\s*\w+)*\b/.source.replace(//g,(function(){return t.source}));e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!)\w+/.source.replace(//g,(function(){return t.source}))),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:import|module)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/(?:\s*:\s*)?|:\s*/.source.replace(//g,(function(){return n}))+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(a),function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-](?:[^;{\s]|\s+(?![\s{]))*(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),n.tag.addAttribute("style","css"))}(a),function(e){var t,n=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css.selector={pattern:e.languages.css.selector.pattern,lookbehind:!0,inside:t={"pseudo-element":/:(?:after|before|first-letter|first-line|selection)|::[-\w]+/,"pseudo-class":/:[-\w]+/,class:/\.[-\w]+/,id:/#[-\w]+/,attribute:{pattern:RegExp("\\[(?:[^[\\]\"']|"+n.source+")*\\]"),greedy:!0,inside:{punctuation:/^\[|\]$/,"case-sensitivity":{pattern:/(\s)[si]$/i,lookbehind:!0,alias:"keyword"},namespace:{pattern:/^(\s*)(?:(?!\s)[-*\w\xA0-\uFFFF])*\|(?!=)/,lookbehind:!0,inside:{punctuation:/\|$/}},"attr-name":{pattern:/^(\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+/,lookbehind:!0},"attr-value":[n,{pattern:/(=\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+(?=\s*$)/,lookbehind:!0}],operator:/[|~*^$]?=/}},"n-th":[{pattern:/(\(\s*)[+-]?\d*[\dn](?:\s*[+-]\s*\d+)?(?=\s*\))/,lookbehind:!0,inside:{number:/[\dn]+/,operator:/[+-]/}},{pattern:/(\(\s*)(?:even|odd)(?=\s*\))/i,lookbehind:!0}],combinator:/>|\+|~|\|\|/,punctuation:/[(),]/}},e.languages.css.atrule.inside["selector-function-argument"].inside=t,e.languages.insertBefore("css","property",{variable:{pattern:/(^|[^-\w\xA0-\uFFFF])--(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*/i,lookbehind:!0}});var r={pattern:/(\b\d+)(?:%|[a-z]+(?![\w-]))/,lookbehind:!0},a={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0};e.languages.insertBefore("css","function",{operator:{pattern:/(\s)[+\-*\/](?=\s)/,lookbehind:!0},hexcode:{pattern:/\B#[\da-f]{3,8}\b/i,alias:"color"},color:[{pattern:/(^|[^\w-])(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)(?![\w-])/i,lookbehind:!0},{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:r,number:a,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:r,number:a})}(a),a.languages.javascript=a.languages.extend("clike",{"class-name":[a.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),a.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,a.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:a.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:a.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:a.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:a.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:a.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),a.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:a.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),a.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),a.languages.markup&&(a.languages.markup.tag.addInlined("script","javascript"),a.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),a.languages.js=a.languages.javascript,function(e){var t=/#(?!\{).+/,n={pattern:/#\{[^}]+\}/,alias:"variable"};e.languages.coffeescript=e.languages.extend("javascript",{comment:t,string:[{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,inside:{interpolation:n}}],keyword:/\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,"class-member":{pattern:/@(?!\d)\w+/,alias:"variable"}}),e.languages.insertBefore("coffeescript","comment",{"multiline-comment":{pattern:/###[\s\S]+?###/,alias:"comment"},"block-regex":{pattern:/\/{3}[\s\S]*?\/{3}/,alias:"regex",inside:{comment:t,interpolation:n}}}),e.languages.insertBefore("coffeescript","string",{"inline-javascript":{pattern:/`(?:\\[\s\S]|[^\\`])*`/,inside:{delimiter:{pattern:/^`|`$/,alias:"punctuation"},script:{pattern:/[\s\S]+/,alias:"language-javascript",inside:e.languages.javascript}}},"multiline-string":[{pattern:/'''[\s\S]*?'''/,greedy:!0,alias:"string"},{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string",inside:{interpolation:n}}]}),e.languages.insertBefore("coffeescript","keyword",{property:/(?!\d)\w+(?=\s*:(?!:))/}),delete e.languages.coffeescript["template-string"],e.languages.coffee=e.languages.coffeescript}(a),function(e){var t=/[*&][^\s[\]{},]+/,n=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,r="(?:"+n.source+"(?:[ \t]+"+t.source+")?|"+t.source+"(?:[ \t]+"+n.source+")?)",a=/(?:[^\s\x00-\x08\x0e-\x1f!"#%&'*,\-:>?@[\]`{|}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]|[?:-])(?:[ \t]*(?:(?![#:])|:))*/.source.replace(//g,(function(){return/[^\s\x00-\x08\x0e-\x1f,[\]{}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]/.source})),o=/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'/.source;function i(e,t){t=(t||"").replace(/m/g,"")+"m";var n=/([:\-,[{]\s*(?:\s<>[ \t]+)?)(?:<>)(?=[ \t]*(?:$|,|\]|\}|(?:[\r\n]\s*)?#))/.source.replace(/<>/g,(function(){return r})).replace(/<>/g,(function(){return e}));return RegExp(n,t)}e.languages.yaml={scalar:{pattern:RegExp(/([\-:]\s*(?:\s<>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\S[^\r\n]*(?:\2[^\r\n]+)*)/.source.replace(/<>/g,(function(){return r}))),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp(/((?:^|[:\-,[{\r\n?])[ \t]*(?:<>[ \t]+)?)<>(?=\s*:\s)/.source.replace(/<>/g,(function(){return r})).replace(/<>/g,(function(){return"(?:"+a+"|"+o+")"}))),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:i(/\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?(?:[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?))?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?/.source),lookbehind:!0,alias:"number"},boolean:{pattern:i(/false|true/.source,"i"),lookbehind:!0,alias:"important"},null:{pattern:i(/null|~/.source,"i"),lookbehind:!0,alias:"important"},string:{pattern:i(o),lookbehind:!0,greedy:!0},number:{pattern:i(/[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|\.inf|\.nan)/.source,"i"),lookbehind:!0},tag:n,important:t,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(a),function(e){var t=/(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source;function n(e){return e=e.replace(//g,(function(){return t})),RegExp(/((?:^|[^\\])(?:\\{2})*)/.source+"(?:"+e+")")}var r=/(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source,a=/\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(/__/g,(function(){return r})),o=/\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source;e.languages.markdown=e.languages.extend("markup",{}),e.languages.insertBefore("markdown","prolog",{"front-matter-block":{pattern:/(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,"front-matter":{pattern:/\S+(?:\s+\S+)*/,alias:["yaml","language-yaml"],inside:e.languages.yaml}}},blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+a+o+"(?:"+a+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+a+o+")(?:"+a+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(r),inside:e.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+a+")"+o+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+a+"$"),inside:{"table-header":{pattern:RegExp(r),alias:"important",inside:e.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,lookbehind:!0,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:n(/\b__(?:(?!_)|_(?:(?!_))+_)+__\b|\*\*(?:(?!\*)|\*(?:(?!\*))+\*)+\*\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:n(/\b_(?:(?!_)|__(?:(?!_))+__)+_\b|\*(?:(?!\*)|\*\*(?:(?!\*))+\*\*)+\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:n(/(~~?)(?:(?!~))+\2/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},"code-snippet":{pattern:/(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:["code","keyword"]},url:{pattern:n(/!?\[(?:(?!\]))+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\]))+\])/.source),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\][ \t]?\[)[^\]]+(?=\]$)/,lookbehind:!0},url:{pattern:/(^\]\()[^\s)]+/,lookbehind:!0},string:{pattern:/(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,lookbehind:!0}}}}),["url","bold","italic","strike"].forEach((function(t){["url","bold","italic","strike","code-snippet"].forEach((function(n){t!==n&&(e.languages.markdown[t].inside.content.inside[n]=e.languages.markdown[n])}))})),e.hooks.add("after-tokenize",(function(e){"markdown"!==e.language&&"md"!==e.language||function e(t){if(t&&"string"!=typeof t)for(var n=0,r=t.length;n",quot:'"'},s=String.fromCodePoint||String.fromCharCode;e.languages.md=e.languages.markdown}(a),a.languages.graphql={comment:/#.*/,description:{pattern:/(?:"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*")(?=\s*[a-z_])/i,greedy:!0,alias:"string",inside:{"language-markdown":{pattern:/(^"(?:"")?)(?!\1)[\s\S]+(?=\1$)/,lookbehind:!0,inside:a.languages.markdown}}},string:{pattern:/"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},number:/(?:\B-|\b)\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,boolean:/\b(?:false|true)\b/,variable:/\$[a-z_]\w*/i,directive:{pattern:/@[a-z_]\w*/i,alias:"function"},"attr-name":{pattern:/\b[a-z_]\w*(?=\s*(?:\((?:[^()"]|"(?:\\.|[^\\"\r\n])*")*\))?:)/i,greedy:!0},"atom-input":{pattern:/\b[A-Z]\w*Input\b/,alias:"class-name"},scalar:/\b(?:Boolean|Float|ID|Int|String)\b/,constant:/\b[A-Z][A-Z_\d]*\b/,"class-name":{pattern:/(\b(?:enum|implements|interface|on|scalar|type|union)\s+|&\s*|:\s*|\[)[A-Z_]\w*/,lookbehind:!0},fragment:{pattern:/(\bfragment\s+|\.{3}\s*(?!on\b))[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-mutation":{pattern:/(\bmutation\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-query":{pattern:/(\bquery\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},keyword:/\b(?:directive|enum|extend|fragment|implements|input|interface|mutation|on|query|repeatable|scalar|schema|subscription|type|union)\b/,operator:/[!=|&]|\.{3}/,"property-query":/\w+(?=\s*\()/,object:/\w+(?=\s*\{)/,punctuation:/[!(){}\[\]:=,]/,property:/\w+/},a.hooks.add("after-tokenize",(function(e){if("graphql"===e.language)for(var t=e.tokens.filter((function(e){return"string"!=typeof e&&"comment"!==e.type&&"scalar"!==e.type})),n=0;n0)){var l=f(/^\{$/,/^\}$/);if(-1===l)continue;for(var s=n;s=0&&p(u,"variable-input")}}}}function c(e){return t[n+e]}function d(e,t){t=t||0;for(var n=0;n?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/},function(e){var t=e.languages.javascript["template-string"],n=t.pattern.source,r=t.inside.interpolation,a=r.inside["interpolation-punctuation"],o=r.pattern.source;function i(t,r){if(e.languages[t])return{pattern:RegExp("((?:"+r+")\\s*)"+n),lookbehind:!0,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},"embedded-code":{pattern:/[\s\S]+/,alias:t}}}}function l(e,t){return"___"+t.toUpperCase()+"_"+e+"___"}function s(t,n,r){var a={code:t,grammar:n,language:r};return e.hooks.run("before-tokenize",a),a.tokens=e.tokenize(a.code,a.grammar),e.hooks.run("after-tokenize",a),a.tokens}function u(t){var n={};n["interpolation-punctuation"]=a;var o=e.tokenize(t,n);if(3===o.length){var i=[1,1];i.push.apply(i,s(o[1],e.languages.javascript,"javascript")),o.splice.apply(o,i)}return new e.Token("interpolation",o,r.alias,t)}function c(t,n,r){var a=e.tokenize(t,{interpolation:{pattern:RegExp(o),lookbehind:!0}}),i=0,c={},d=s(a.map((function(e){if("string"==typeof e)return e;for(var n,a=e.content;-1!==t.indexOf(n=l(i++,r)););return c[n]=a,n})).join(""),n,r),f=Object.keys(c);return i=0,function e(t){for(var n=0;n=f.length)return;var r=t[n];if("string"==typeof r||"string"==typeof r.content){var a=f[i],o="string"==typeof r?r:r.content,l=o.indexOf(a);if(-1!==l){++i;var s=o.substring(0,l),d=u(c[a]),p=o.substring(l+a.length),m=[];if(s&&m.push(s),m.push(d),p){var h=[p];e(h),m.push.apply(m,h)}"string"==typeof r?(t.splice.apply(t,[n,1].concat(m)),n+=m.length-1):r.content=m}}else{var g=r.content;Array.isArray(g)?e(g):e([g])}}}(d),new e.Token(r,d,"language-"+r,t)}e.languages.javascript["template-string"]=[i("css",/\b(?:styled(?:\([^)]*\))?(?:\s*\.\s*\w+(?:\([^)]*\))*)*|css(?:\s*\.\s*(?:global|resolve))?|createGlobalStyle|keyframes)/.source),i("html",/\bhtml|\.\s*(?:inner|outer)HTML\s*\+?=/.source),i("svg",/\bsvg/.source),i("markdown",/\b(?:markdown|md)/.source),i("graphql",/\b(?:gql|graphql(?:\s*\.\s*experimental)?)/.source),i("sql",/\bsql/.source),t].filter(Boolean);var d={javascript:!0,js:!0,typescript:!0,ts:!0,jsx:!0,tsx:!0};function f(e){return"string"==typeof e?e:Array.isArray(e)?e.map(f).join(""):f(e.content)}e.hooks.add("after-tokenize",(function(t){t.language in d&&function t(n){for(var r=0,a=n.length;r]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var t=e.languages.extend("typescript",{});delete t["class-name"],e.languages.typescript["class-name"].inside=t,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:t}}}}),e.languages.ts=e.languages.typescript}(a),function(e){function t(e,t){return RegExp(e.replace(//g,(function(){return/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/.source})),t)}e.languages.insertBefore("javascript","function-variable",{"method-variable":{pattern:RegExp("(\\.\\s*)"+e.languages.javascript["function-variable"].pattern.source),lookbehind:!0,alias:["function-variable","method","function","property-access"]}}),e.languages.insertBefore("javascript","function",{method:{pattern:RegExp("(\\.\\s*)"+e.languages.javascript.function.source),lookbehind:!0,alias:["function","property-access"]}}),e.languages.insertBefore("javascript","constant",{"known-class-name":[{pattern:/\b(?:(?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)?Array|ArrayBuffer|BigInt|Boolean|DataView|Date|Error|Function|Intl|JSON|(?:Weak)?(?:Map|Set)|Math|Number|Object|Promise|Proxy|Reflect|RegExp|String|Symbol|WebAssembly)\b/,alias:"class-name"},{pattern:/\b(?:[A-Z]\w*)Error\b/,alias:"class-name"}]}),e.languages.insertBefore("javascript","keyword",{imports:{pattern:t(/(\bimport\b\s*)(?:(?:\s*,\s*(?:\*\s*as\s+|\{[^{}]*\}))?|\*\s*as\s+|\{[^{}]*\})(?=\s*\bfrom\b)/.source),lookbehind:!0,inside:e.languages.javascript},exports:{pattern:t(/(\bexport\b\s*)(?:\*(?:\s*as\s+)?(?=\s*\bfrom\b)|\{[^{}]*\})/.source),lookbehind:!0,inside:e.languages.javascript}}),e.languages.javascript.keyword.unshift({pattern:/\b(?:as|default|export|from|import)\b/,alias:"module"},{pattern:/\b(?:await|break|catch|continue|do|else|finally|for|if|return|switch|throw|try|while|yield)\b/,alias:"control-flow"},{pattern:/\bnull\b/,alias:["null","nil"]},{pattern:/\bundefined\b/,alias:"nil"}),e.languages.insertBefore("javascript","operator",{spread:{pattern:/\.{3}/,alias:"operator"},arrow:{pattern:/=>/,alias:"operator"}}),e.languages.insertBefore("javascript","punctuation",{"property-access":{pattern:t(/(\.\s*)#?/.source),lookbehind:!0},"maybe-class-name":{pattern:/(^|[^$\w\xA0-\uFFFF])[A-Z][$\w\xA0-\uFFFF]+/,lookbehind:!0},dom:{pattern:/\b(?:document|(?:local|session)Storage|location|navigator|performance|window)\b/,alias:"variable"},console:{pattern:/\bconsole(?=\s*\.)/,alias:"class-name"}});for(var n=["function","function-variable","method","method-variable","property-access"],r=0;r*\.{3}(?:[^{}]|)*\})/.source;function o(e,t){return e=e.replace(//g,(function(){return n})).replace(//g,(function(){return r})).replace(//g,(function(){return a})),RegExp(e,t)}a=o(a).source,e.languages.jsx=e.languages.extend("markup",t),e.languages.jsx.tag.pattern=o(/<\/?(?:[\w.:-]+(?:+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|))?|))**\/?)?>/.source),e.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,e.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,e.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,e.languages.jsx.tag.inside.comment=t.comment,e.languages.insertBefore("inside","attr-name",{spread:{pattern:o(//.source),inside:e.languages.jsx}},e.languages.jsx.tag),e.languages.insertBefore("inside","special-attr",{script:{pattern:o(/=/.source),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:e.languages.jsx}}},e.languages.jsx.tag);var i=function(e){return e?"string"==typeof e?e:"string"==typeof e.content?e.content:e.content.map(i).join(""):""},l=function(t){for(var n=[],r=0;r0&&n[n.length-1].tagName===i(a.content[0].content[1])&&n.pop():"/>"===a.content[a.content.length-1].content||n.push({tagName:i(a.content[0].content[1]),openedBraces:0}):n.length>0&&"punctuation"===a.type&&"{"===a.content?n[n.length-1].openedBraces++:n.length>0&&n[n.length-1].openedBraces>0&&"punctuation"===a.type&&"}"===a.content?n[n.length-1].openedBraces--:o=!0),(o||"string"==typeof a)&&n.length>0&&0===n[n.length-1].openedBraces){var s=i(a);r0&&("string"==typeof t[r-1]||"plain-text"===t[r-1].type)&&(s=i(t[r-1])+s,t.splice(r-1,1),r--),t[r]=new e.Token("plain-text",s,null,s)}a.content&&"string"!=typeof a.content&&l(a.content)}};e.hooks.add("after-tokenize",(function(e){"jsx"!==e.language&&"tsx"!==e.language||l(e.tokens)}))}(a),function(e){e.languages.diff={coord:[/^(?:\*{3}|-{3}|\+{3}).*$/m,/^@@.*@@$/m,/^\d.*$/m]};var t={"deleted-sign":"-","deleted-arrow":"<","inserted-sign":"+","inserted-arrow":">",unchanged:" ",diff:"!"};Object.keys(t).forEach((function(n){var r=t[n],a=[];/^\w+$/.test(n)||a.push(/\w+/.exec(n)[0]),"diff"===n&&a.push("bold"),e.languages.diff[n]={pattern:RegExp("^(?:["+r+"].*(?:\r\n?|\n|(?![\\s\\S])))+","m"),alias:a,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(n)[0]}}}})),Object.defineProperty(e.languages.diff,"PREFIXES",{value:t})}(a),a.languages.git={comment:/^#.*/m,deleted:/^[-\u2013].*/m,inserted:/^\+.*/m,string:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,command:{pattern:/^.*\$ git .*$/m,inside:{parameter:/\s--?\w+/}},coord:/^@@.*@@$/m,"commit-sha1":/^commit \w{40}$/m},a.languages.go=a.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/}),a.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}}),delete a.languages.go["class-name"],function(e){function t(e,t){return"___"+e.toUpperCase()+t+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(n,r,a,o){if(n.language===r){var i=n.tokenStack=[];n.code=n.code.replace(a,(function(e){if("function"==typeof o&&!o(e))return e;for(var a,l=i.length;-1!==n.code.indexOf(a=t(r,l));)++l;return i[l]=e,a})),n.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(n,r){if(n.language===r&&n.tokenStack){n.grammar=e.languages[r];var a=0,o=Object.keys(n.tokenStack);!function i(l){for(var s=0;s=o.length);s++){var u=l[s];if("string"==typeof u||u.content&&"string"==typeof u.content){var c=o[a],d=n.tokenStack[c],f="string"==typeof u?u:u.content,p=t(r,c),m=f.indexOf(p);if(m>-1){++a;var h=f.substring(0,m),g=new e.Token(r,e.tokenize(d,n.grammar),"language-"+r,d),v=f.substring(m+p.length),b=[];h&&b.push.apply(b,i([h])),b.push(g),v&&b.push.apply(b,i([v])),"string"==typeof u?l.splice.apply(l,[s,1].concat(b)):u.content=b}}else u.content&&i(u.content)}return l}(n.tokens)}}}})}(a),function(e){e.languages.handlebars={comment:/\{\{![\s\S]*?\}\}/,delimiter:{pattern:/^\{\{\{?|\}\}\}?$/,alias:"punctuation"},string:/(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/,number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee][+-]?\d+)?/,boolean:/\b(?:false|true)\b/,block:{pattern:/^(\s*(?:~\s*)?)[#\/]\S+?(?=\s*(?:~\s*)?$|\s)/,lookbehind:!0,alias:"keyword"},brackets:{pattern:/\[[^\]]+\]/,inside:{punctuation:/\[|\]/,variable:/[\s\S]+/}},punctuation:/[!"#%&':()*+,.\/;<=>@\[\\\]^`{|}~]/,variable:/[^!"#%&'()*+,\/;<=>@\[\\\]^`{|}~\s]+/},e.hooks.add("before-tokenize",(function(t){e.languages["markup-templating"].buildPlaceholders(t,"handlebars",/\{\{\{[\s\S]+?\}\}\}|\{\{[\s\S]+?\}\}/g)})),e.hooks.add("after-tokenize",(function(t){e.languages["markup-templating"].tokenizePlaceholders(t,"handlebars")})),e.languages.hbs=e.languages.handlebars}(a),a.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},a.languages.webmanifest=a.languages.json,a.languages.less=a.languages.extend("css",{comment:[/\/\*[\s\S]*?\*\//,{pattern:/(^|[^\\])\/\/.*/,lookbehind:!0}],atrule:{pattern:/@[\w-](?:\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{punctuation:/[:()]/}},selector:{pattern:/(?:@\{[\w-]+\}|[^{};\s@])(?:@\{[\w-]+\}|\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};@\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{variable:/@+[\w-]+/}},property:/(?:@\{[\w-]+\}|[\w-])+(?:\+_?)?(?=\s*:)/,operator:/[+\-*\/]/}),a.languages.insertBefore("less","property",{variable:[{pattern:/@[\w-]+\s*:/,inside:{punctuation:/:/}},/@@?[\w-]+/],"mixin-usage":{pattern:/([{;]\s*)[.#](?!\d)[\w-].*?(?=[(;])/,lookbehind:!0,alias:"function"}}),a.languages.makefile={comment:{pattern:/(^|[^\\])#(?:\\(?:\r\n|[\s\S])|[^\\\r\n])*/,lookbehind:!0},string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"builtin-target":{pattern:/\.[A-Z][^:#=\s]+(?=\s*:(?!=))/,alias:"builtin"},target:{pattern:/^(?:[^:=\s]|[ \t]+(?![\s:]))+(?=\s*:(?!=))/m,alias:"symbol",inside:{variable:/\$+(?:(?!\$)[^(){}:#=\s]+|(?=[({]))/}},variable:/\$+(?:(?!\$)[^(){}:#=\s]+|\([@*%<^+?][DF]\)|(?=[({]))/,keyword:/-include\b|\b(?:define|else|endef|endif|export|ifn?def|ifn?eq|include|override|private|sinclude|undefine|unexport|vpath)\b/,function:{pattern:/(\()(?:abspath|addsuffix|and|basename|call|dir|error|eval|file|filter(?:-out)?|findstring|firstword|flavor|foreach|guile|if|info|join|lastword|load|notdir|or|origin|patsubst|realpath|shell|sort|strip|subst|suffix|value|warning|wildcard|word(?:list|s)?)(?=[ \t])/,lookbehind:!0},operator:/(?:::|[?:+!])?=|[|@]/,punctuation:/[:;(){}]/},a.languages.objectivec=a.languages.extend("c",{string:{pattern:/@?"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},keyword:/\b(?:asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|in|inline|int|long|register|return|self|short|signed|sizeof|static|struct|super|switch|typedef|typeof|union|unsigned|void|volatile|while)\b|(?:@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/,operator:/-[->]?|\+\+?|!=?|<>?=?|==?|&&?|\|\|?|[~^%?*\/@]/}),delete a.languages.objectivec["class-name"],a.languages.objc=a.languages.objectivec,a.languages.ocaml={comment:{pattern:/\(\*[\s\S]*?\*\)/,greedy:!0},char:{pattern:/'(?:[^\\\r\n']|\\(?:.|[ox]?[0-9a-f]{1,3}))'/i,greedy:!0},string:[{pattern:/"(?:\\(?:[\s\S]|\r\n)|[^\\\r\n"])*"/,greedy:!0},{pattern:/\{([a-z_]*)\|[\s\S]*?\|\1\}/,greedy:!0}],number:[/\b(?:0b[01][01_]*|0o[0-7][0-7_]*)\b/i,/\b0x[a-f0-9][a-f0-9_]*(?:\.[a-f0-9_]*)?(?:p[+-]?\d[\d_]*)?(?!\w)/i,/\b\d[\d_]*(?:\.[\d_]*)?(?:e[+-]?\d[\d_]*)?(?!\w)/i],directive:{pattern:/\B#\w+/,alias:"property"},label:{pattern:/\B~\w+/,alias:"property"},"type-variable":{pattern:/\B'\w+/,alias:"function"},variant:{pattern:/`\w+/,alias:"symbol"},keyword:/\b(?:as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|match|method|module|mutable|new|nonrec|object|of|open|private|rec|sig|struct|then|to|try|type|val|value|virtual|when|where|while|with)\b/,boolean:/\b(?:false|true)\b/,"operator-like-punctuation":{pattern:/\[[<>|]|[>|]\]|\{<|>\}/,alias:"punctuation"},operator:/\.[.~]|:[=>]|[=<>@^|&+\-*\/$%!?~][!$%&*+\-.\/:<=>?@^|~]*|\b(?:and|asr|land|lor|lsl|lsr|lxor|mod|or)\b/,punctuation:/;;|::|[(){}\[\].,:;#]|\b_\b/},a.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},a.languages.python["string-interpolation"].inside.interpolation.inside.rest=a.languages.python,a.languages.py=a.languages.python,a.languages.reason=a.languages.extend("clike",{string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/,greedy:!0},"class-name":/\b[A-Z]\w*/,keyword:/\b(?:and|as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|method|module|mutable|new|nonrec|object|of|open|or|private|rec|sig|struct|switch|then|to|try|type|val|virtual|when|while|with)\b/,operator:/\.{3}|:[:=]|\|>|->|=(?:==?|>)?|<=?|>=?|[|^?'#!~`]|[+\-*\/]\.?|\b(?:asr|land|lor|lsl|lsr|lxor|mod)\b/}),a.languages.insertBefore("reason","class-name",{char:{pattern:/'(?:\\x[\da-f]{2}|\\o[0-3][0-7][0-7]|\\\d{3}|\\.|[^'\\\r\n])'/,greedy:!0},constructor:/\b[A-Z]\w*\b(?!\s*\.)/,label:{pattern:/\b[a-z]\w*(?=::)/,alias:"symbol"}}),delete a.languages.reason.function,function(e){e.languages.sass=e.languages.extend("css",{comment:{pattern:/^([ \t]*)\/[\/*].*(?:(?:\r?\n|\r)\1[ \t].+)*/m,lookbehind:!0,greedy:!0}}),e.languages.insertBefore("sass","atrule",{"atrule-line":{pattern:/^(?:[ \t]*)[@+=].+/m,greedy:!0,inside:{atrule:/(?:@[\w-]+|[+=])/}}}),delete e.languages.sass.atrule;var t=/\$[-\w]+|#\{\$[-\w]+\}/,n=[/[+*\/%]|[=!]=|<=?|>=?|\b(?:and|not|or)\b/,{pattern:/(\s)-(?=\s)/,lookbehind:!0}];e.languages.insertBefore("sass","property",{"variable-line":{pattern:/^[ \t]*\$.+/m,greedy:!0,inside:{punctuation:/:/,variable:t,operator:n}},"property-line":{pattern:/^[ \t]*(?:[^:\s]+ *:.*|:[^:\s].*)/m,greedy:!0,inside:{property:[/[^:\s]+(?=\s*:)/,{pattern:/(:)[^:\s]+/,lookbehind:!0}],punctuation:/:/,variable:t,operator:n,important:e.languages.sass.important}}}),delete e.languages.sass.property,delete e.languages.sass.important,e.languages.insertBefore("sass","punctuation",{selector:{pattern:/^([ \t]*)\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*(?:,(?:\r?\n|\r)\1[ \t]+\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*)*/m,lookbehind:!0,greedy:!0}})}(a),a.languages.scss=a.languages.extend("css",{comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},atrule:{pattern:/@[\w-](?:\([^()]+\)|[^()\s]|\s+(?!\s))*?(?=\s+[{;])/,inside:{rule:/@[\w-]+/}},url:/(?:[-a-z]+-)?url(?=\()/i,selector:{pattern:/(?=\S)[^@;{}()]?(?:[^@;{}()\s]|\s+(?!\s)|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}][^:{}]*[:{][^}]))/,inside:{parent:{pattern:/&/,alias:"important"},placeholder:/%[-\w]+/,variable:/\$[-\w]+|#\{\$[-\w]+\}/}},property:{pattern:/(?:[-\w]|\$[-\w]|#\{\$[-\w]+\})+(?=\s*:)/,inside:{variable:/\$[-\w]+|#\{\$[-\w]+\}/}}}),a.languages.insertBefore("scss","atrule",{keyword:[/@(?:content|debug|each|else(?: if)?|extend|for|forward|function|if|import|include|mixin|return|use|warn|while)\b/i,{pattern:/( )(?:from|through)(?= )/,lookbehind:!0}]}),a.languages.insertBefore("scss","important",{variable:/\$[-\w]+|#\{\$[-\w]+\}/}),a.languages.insertBefore("scss","function",{"module-modifier":{pattern:/\b(?:as|hide|show|with)\b/i,alias:"keyword"},placeholder:{pattern:/%[-\w]+/,alias:"selector"},statement:{pattern:/\B!(?:default|optional)\b/i,alias:"keyword"},boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"},operator:{pattern:/(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|not|or)(?=\s)/,lookbehind:!0}}),a.languages.scss.atrule.inside.rest=a.languages.scss,function(e){var t={pattern:/(\b\d+)(?:%|[a-z]+)/,lookbehind:!0},n={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0},r={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},url:{pattern:/\burl\((["']?).*?\1\)/i,greedy:!0},string:{pattern:/("|')(?:(?!\1)[^\\\r\n]|\\(?:\r\n|[\s\S]))*\1/,greedy:!0},interpolation:null,func:null,important:/\B!(?:important|optional)\b/i,keyword:{pattern:/(^|\s+)(?:(?:else|for|if|return|unless)(?=\s|$)|@[\w-]+)/,lookbehind:!0},hexcode:/#[\da-f]{3,6}/i,color:[/\b(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)\b/i,{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:t,number:n,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:t,boolean:/\b(?:false|true)\b/,operator:[/~|[+!\/%<>?=]=?|[-:]=|\*[*=]?|\.{2,3}|&&|\|\||\B-\B|\b(?:and|in|is(?: a| defined| not|nt)?|not|or)\b/],number:n,punctuation:/[{}()\[\];:,]/};r.interpolation={pattern:/\{[^\r\n}:]+\}/,alias:"variable",inside:{delimiter:{pattern:/^\{|\}$/,alias:"punctuation"},rest:r}},r.func={pattern:/[\w-]+\([^)]*\).*/,inside:{function:/^[^(]+/,rest:r}},e.languages.stylus={"atrule-declaration":{pattern:/(^[ \t]*)@.+/m,lookbehind:!0,inside:{atrule:/^@[\w-]+/,rest:r}},"variable-declaration":{pattern:/(^[ \t]*)[\w$-]+\s*.?=[ \t]*(?:\{[^{}]*\}|\S.*|$)/m,lookbehind:!0,inside:{variable:/^\S+/,rest:r}},statement:{pattern:/(^[ \t]*)(?:else|for|if|return|unless)[ \t].+/m,lookbehind:!0,inside:{keyword:/^\S+/,rest:r}},"property-declaration":{pattern:/((?:^|\{)([ \t]*))(?:[\w-]|\{[^}\r\n]+\})+(?:\s*:\s*|[ \t]+)(?!\s)[^{\r\n]*(?:;|[^{\r\n,]$(?!(?:\r?\n|\r)(?:\{|\2[ \t])))/m,lookbehind:!0,inside:{property:{pattern:/^[^\s:]+/,inside:{interpolation:r.interpolation}},rest:r}},selector:{pattern:/(^[ \t]*)(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)(?:(?:\r?\n|\r)(?:\1(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)))*(?:,$|\{|(?=(?:\r?\n|\r)(?:\{|\1[ \t])))/m,lookbehind:!0,inside:{interpolation:r.interpolation,comment:r.comment,punctuation:/[{},]/}},func:r.func,string:r.string,comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0,greedy:!0},interpolation:r.interpolation,punctuation:/[{}()\[\];:.]/}}(a),function(e){var t=e.util.clone(e.languages.typescript);e.languages.tsx=e.languages.extend("jsx",t),delete e.languages.tsx.parameter,delete e.languages.tsx["literal-property"];var n=e.languages.tsx.tag;n.pattern=RegExp(/(^|[^\w$]|(?=<\/))/.source+"(?:"+n.pattern.source+")",n.pattern.flags),n.lookbehind=!0}(a),a.languages.wasm={comment:[/\(;[\s\S]*?;\)/,{pattern:/;;.*/,greedy:!0}],string:{pattern:/"(?:\\[\s\S]|[^"\\])*"/,greedy:!0},keyword:[{pattern:/\b(?:align|offset)=/,inside:{operator:/=/}},{pattern:/\b(?:(?:f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|neg?|nearest|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|sqrt|store(?:8|16|32)?|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))?|memory\.(?:grow|size))\b/,inside:{punctuation:/\./}},/\b(?:anyfunc|block|br(?:_if|_table)?|call(?:_indirect)?|data|drop|elem|else|end|export|func|get_(?:global|local)|global|if|import|local|loop|memory|module|mut|nop|offset|param|result|return|select|set_(?:global|local)|start|table|tee_local|then|type|unreachable)\b/],variable:/\$[\w!#$%&'*+\-./:<=>?@\\^`|~]+/,number:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/,punctuation:/[()]/};const o=a},9901:e=>{e.exports&&(e.exports={core:{meta:{path:"components/prism-core.js",option:"mandatory"},core:"Core"},themes:{meta:{path:"themes/{id}.css",link:"index.html?theme={id}",exclusive:!0},prism:{title:"Default",option:"default"},"prism-dark":"Dark","prism-funky":"Funky","prism-okaidia":{title:"Okaidia",owner:"ocodia"},"prism-twilight":{title:"Twilight",owner:"remybach"},"prism-coy":{title:"Coy",owner:"tshedor"},"prism-solarizedlight":{title:"Solarized Light",owner:"hectormatos2011 "},"prism-tomorrow":{title:"Tomorrow Night",owner:"Rosey"}},languages:{meta:{path:"components/prism-{id}",noCSS:!0,examplesPath:"examples/prism-{id}",addCheckAll:!0},markup:{title:"Markup",alias:["html","xml","svg","mathml","ssml","atom","rss"],aliasTitles:{html:"HTML",xml:"XML",svg:"SVG",mathml:"MathML",ssml:"SSML",atom:"Atom",rss:"RSS"},option:"default"},css:{title:"CSS",option:"default",modify:"markup"},clike:{title:"C-like",option:"default"},javascript:{title:"JavaScript",require:"clike",modify:"markup",optional:"regex",alias:"js",option:"default"},abap:{title:"ABAP",owner:"dellagustin"},abnf:{title:"ABNF",owner:"RunDevelopment"},actionscript:{title:"ActionScript",require:"javascript",modify:"markup",owner:"Golmote"},ada:{title:"Ada",owner:"Lucretia"},agda:{title:"Agda",owner:"xy-ren"},al:{title:"AL",owner:"RunDevelopment"},antlr4:{title:"ANTLR4",alias:"g4",owner:"RunDevelopment"},apacheconf:{title:"Apache Configuration",owner:"GuiTeK"},apex:{title:"Apex",require:["clike","sql"],owner:"RunDevelopment"},apl:{title:"APL",owner:"ngn"},applescript:{title:"AppleScript",owner:"Golmote"},aql:{title:"AQL",owner:"RunDevelopment"},arduino:{title:"Arduino",require:"cpp",alias:"ino",owner:"dkern"},arff:{title:"ARFF",owner:"Golmote"},armasm:{title:"ARM Assembly",alias:"arm-asm",owner:"RunDevelopment"},arturo:{title:"Arturo",alias:"art",optional:["bash","css","javascript","markup","markdown","sql"],owner:"drkameleon"},asciidoc:{alias:"adoc",title:"AsciiDoc",owner:"Golmote"},aspnet:{title:"ASP.NET (C#)",require:["markup","csharp"],owner:"nauzilus"},asm6502:{title:"6502 Assembly",owner:"kzurawel"},asmatmel:{title:"Atmel AVR Assembly",owner:"cerkit"},autohotkey:{title:"AutoHotkey",owner:"aviaryan"},autoit:{title:"AutoIt",owner:"Golmote"},avisynth:{title:"AviSynth",alias:"avs",owner:"Zinfidel"},"avro-idl":{title:"Avro IDL",alias:"avdl",owner:"RunDevelopment"},awk:{title:"AWK",alias:"gawk",aliasTitles:{gawk:"GAWK"},owner:"RunDevelopment"},bash:{title:"Bash",alias:"shell",aliasTitles:{shell:"Shell"},owner:"zeitgeist87"},basic:{title:"BASIC",owner:"Golmote"},batch:{title:"Batch",owner:"Golmote"},bbcode:{title:"BBcode",alias:"shortcode",aliasTitles:{shortcode:"Shortcode"},owner:"RunDevelopment"},bicep:{title:"Bicep",owner:"johnnyreilly"},birb:{title:"Birb",require:"clike",owner:"Calamity210"},bison:{title:"Bison",require:"c",owner:"Golmote"},bnf:{title:"BNF",alias:"rbnf",aliasTitles:{rbnf:"RBNF"},owner:"RunDevelopment"},brainfuck:{title:"Brainfuck",owner:"Golmote"},brightscript:{title:"BrightScript",owner:"RunDevelopment"},bro:{title:"Bro",owner:"wayward710"},bsl:{title:"BSL (1C:Enterprise)",alias:"oscript",aliasTitles:{oscript:"OneScript"},owner:"Diversus23"},c:{title:"C",require:"clike",owner:"zeitgeist87"},csharp:{title:"C#",require:"clike",alias:["cs","dotnet"],owner:"mvalipour"},cpp:{title:"C++",require:"c",owner:"zeitgeist87"},cfscript:{title:"CFScript",require:"clike",alias:"cfc",owner:"mjclemente"},chaiscript:{title:"ChaiScript",require:["clike","cpp"],owner:"RunDevelopment"},cil:{title:"CIL",owner:"sbrl"},clojure:{title:"Clojure",owner:"troglotit"},cmake:{title:"CMake",owner:"mjrogozinski"},cobol:{title:"COBOL",owner:"RunDevelopment"},coffeescript:{title:"CoffeeScript",require:"javascript",alias:"coffee",owner:"R-osey"},concurnas:{title:"Concurnas",alias:"conc",owner:"jasontatton"},csp:{title:"Content-Security-Policy",owner:"ScottHelme"},cooklang:{title:"Cooklang",owner:"ahue"},coq:{title:"Coq",owner:"RunDevelopment"},crystal:{title:"Crystal",require:"ruby",owner:"MakeNowJust"},"css-extras":{title:"CSS Extras",require:"css",modify:"css",owner:"milesj"},csv:{title:"CSV",owner:"RunDevelopment"},cue:{title:"CUE",owner:"RunDevelopment"},cypher:{title:"Cypher",owner:"RunDevelopment"},d:{title:"D",require:"clike",owner:"Golmote"},dart:{title:"Dart",require:"clike",owner:"Golmote"},dataweave:{title:"DataWeave",owner:"machaval"},dax:{title:"DAX",owner:"peterbud"},dhall:{title:"Dhall",owner:"RunDevelopment"},diff:{title:"Diff",owner:"uranusjr"},django:{title:"Django/Jinja2",require:"markup-templating",alias:"jinja2",owner:"romanvm"},"dns-zone-file":{title:"DNS zone file",owner:"RunDevelopment",alias:"dns-zone"},docker:{title:"Docker",alias:"dockerfile",owner:"JustinBeckwith"},dot:{title:"DOT (Graphviz)",alias:"gv",optional:"markup",owner:"RunDevelopment"},ebnf:{title:"EBNF",owner:"RunDevelopment"},editorconfig:{title:"EditorConfig",owner:"osipxd"},eiffel:{title:"Eiffel",owner:"Conaclos"},ejs:{title:"EJS",require:["javascript","markup-templating"],owner:"RunDevelopment",alias:"eta",aliasTitles:{eta:"Eta"}},elixir:{title:"Elixir",owner:"Golmote"},elm:{title:"Elm",owner:"zwilias"},etlua:{title:"Embedded Lua templating",require:["lua","markup-templating"],owner:"RunDevelopment"},erb:{title:"ERB",require:["ruby","markup-templating"],owner:"Golmote"},erlang:{title:"Erlang",owner:"Golmote"},"excel-formula":{title:"Excel Formula",alias:["xlsx","xls"],owner:"RunDevelopment"},fsharp:{title:"F#",require:"clike",owner:"simonreynolds7"},factor:{title:"Factor",owner:"catb0t"},false:{title:"False",owner:"edukisto"},"firestore-security-rules":{title:"Firestore security rules",require:"clike",owner:"RunDevelopment"},flow:{title:"Flow",require:"javascript",owner:"Golmote"},fortran:{title:"Fortran",owner:"Golmote"},ftl:{title:"FreeMarker Template Language",require:"markup-templating",owner:"RunDevelopment"},gml:{title:"GameMaker Language",alias:"gamemakerlanguage",require:"clike",owner:"LiarOnce"},gap:{title:"GAP (CAS)",owner:"RunDevelopment"},gcode:{title:"G-code",owner:"RunDevelopment"},gdscript:{title:"GDScript",owner:"RunDevelopment"},gedcom:{title:"GEDCOM",owner:"Golmote"},gettext:{title:"gettext",alias:"po",owner:"RunDevelopment"},gherkin:{title:"Gherkin",owner:"hason"},git:{title:"Git",owner:"lgiraudel"},glsl:{title:"GLSL",require:"c",owner:"Golmote"},gn:{title:"GN",alias:"gni",owner:"RunDevelopment"},"linker-script":{title:"GNU Linker Script",alias:"ld",owner:"RunDevelopment"},go:{title:"Go",require:"clike",owner:"arnehormann"},"go-module":{title:"Go module",alias:"go-mod",owner:"RunDevelopment"},graphql:{title:"GraphQL",optional:"markdown",owner:"Golmote"},groovy:{title:"Groovy",require:"clike",owner:"robfletcher"},haml:{title:"Haml",require:"ruby",optional:["css","css-extras","coffeescript","erb","javascript","less","markdown","scss","textile"],owner:"Golmote"},handlebars:{title:"Handlebars",require:"markup-templating",alias:["hbs","mustache"],aliasTitles:{mustache:"Mustache"},owner:"Golmote"},haskell:{title:"Haskell",alias:"hs",owner:"bholst"},haxe:{title:"Haxe",require:"clike",optional:"regex",owner:"Golmote"},hcl:{title:"HCL",owner:"outsideris"},hlsl:{title:"HLSL",require:"c",owner:"RunDevelopment"},hoon:{title:"Hoon",owner:"matildepark"},http:{title:"HTTP",optional:["csp","css","hpkp","hsts","javascript","json","markup","uri"],owner:"danielgtaylor"},hpkp:{title:"HTTP Public-Key-Pins",owner:"ScottHelme"},hsts:{title:"HTTP Strict-Transport-Security",owner:"ScottHelme"},ichigojam:{title:"IchigoJam",owner:"BlueCocoa"},icon:{title:"Icon",owner:"Golmote"},"icu-message-format":{title:"ICU Message Format",owner:"RunDevelopment"},idris:{title:"Idris",alias:"idr",owner:"KeenS",require:"haskell"},ignore:{title:".ignore",owner:"osipxd",alias:["gitignore","hgignore","npmignore"],aliasTitles:{gitignore:".gitignore",hgignore:".hgignore",npmignore:".npmignore"}},inform7:{title:"Inform 7",owner:"Golmote"},ini:{title:"Ini",owner:"aviaryan"},io:{title:"Io",owner:"AlesTsurko"},j:{title:"J",owner:"Golmote"},java:{title:"Java",require:"clike",owner:"sherblot"},javadoc:{title:"JavaDoc",require:["markup","java","javadoclike"],modify:"java",optional:"scala",owner:"RunDevelopment"},javadoclike:{title:"JavaDoc-like",modify:["java","javascript","php"],owner:"RunDevelopment"},javastacktrace:{title:"Java stack trace",owner:"RunDevelopment"},jexl:{title:"Jexl",owner:"czosel"},jolie:{title:"Jolie",require:"clike",owner:"thesave"},jq:{title:"JQ",owner:"RunDevelopment"},jsdoc:{title:"JSDoc",require:["javascript","javadoclike","typescript"],modify:"javascript",optional:["actionscript","coffeescript"],owner:"RunDevelopment"},"js-extras":{title:"JS Extras",require:"javascript",modify:"javascript",optional:["actionscript","coffeescript","flow","n4js","typescript"],owner:"RunDevelopment"},json:{title:"JSON",alias:"webmanifest",aliasTitles:{webmanifest:"Web App Manifest"},owner:"CupOfTea696"},json5:{title:"JSON5",require:"json",owner:"RunDevelopment"},jsonp:{title:"JSONP",require:"json",owner:"RunDevelopment"},jsstacktrace:{title:"JS stack trace",owner:"sbrl"},"js-templates":{title:"JS Templates",require:"javascript",modify:"javascript",optional:["css","css-extras","graphql","markdown","markup","sql"],owner:"RunDevelopment"},julia:{title:"Julia",owner:"cdagnino"},keepalived:{title:"Keepalived Configure",owner:"dev-itsheng"},keyman:{title:"Keyman",owner:"mcdurdin"},kotlin:{title:"Kotlin",alias:["kt","kts"],aliasTitles:{kts:"Kotlin Script"},require:"clike",owner:"Golmote"},kumir:{title:"KuMir (\u041a\u0443\u041c\u0438\u0440)",alias:"kum",owner:"edukisto"},kusto:{title:"Kusto",owner:"RunDevelopment"},latex:{title:"LaTeX",alias:["tex","context"],aliasTitles:{tex:"TeX",context:"ConTeXt"},owner:"japborst"},latte:{title:"Latte",require:["clike","markup-templating","php"],owner:"nette"},less:{title:"Less",require:"css",optional:"css-extras",owner:"Golmote"},lilypond:{title:"LilyPond",require:"scheme",alias:"ly",owner:"RunDevelopment"},liquid:{title:"Liquid",require:"markup-templating",owner:"cinhtau"},lisp:{title:"Lisp",alias:["emacs","elisp","emacs-lisp"],owner:"JuanCaicedo"},livescript:{title:"LiveScript",owner:"Golmote"},llvm:{title:"LLVM IR",owner:"porglezomp"},log:{title:"Log file",optional:"javastacktrace",owner:"RunDevelopment"},lolcode:{title:"LOLCODE",owner:"Golmote"},lua:{title:"Lua",owner:"Golmote"},magma:{title:"Magma (CAS)",owner:"RunDevelopment"},makefile:{title:"Makefile",owner:"Golmote"},markdown:{title:"Markdown",require:"markup",optional:"yaml",alias:"md",owner:"Golmote"},"markup-templating":{title:"Markup templating",require:"markup",owner:"Golmote"},mata:{title:"Mata",owner:"RunDevelopment"},matlab:{title:"MATLAB",owner:"Golmote"},maxscript:{title:"MAXScript",owner:"RunDevelopment"},mel:{title:"MEL",owner:"Golmote"},mermaid:{title:"Mermaid",owner:"RunDevelopment"},mizar:{title:"Mizar",owner:"Golmote"},mongodb:{title:"MongoDB",owner:"airs0urce",require:"javascript"},monkey:{title:"Monkey",owner:"Golmote"},moonscript:{title:"MoonScript",alias:"moon",owner:"RunDevelopment"},n1ql:{title:"N1QL",owner:"TMWilds"},n4js:{title:"N4JS",require:"javascript",optional:"jsdoc",alias:"n4jsd",owner:"bsmith-n4"},"nand2tetris-hdl":{title:"Nand To Tetris HDL",owner:"stephanmax"},naniscript:{title:"Naninovel Script",owner:"Elringus",alias:"nani"},nasm:{title:"NASM",owner:"rbmj"},neon:{title:"NEON",owner:"nette"},nevod:{title:"Nevod",owner:"nezaboodka"},nginx:{title:"nginx",owner:"volado"},nim:{title:"Nim",owner:"Golmote"},nix:{title:"Nix",owner:"Golmote"},nsis:{title:"NSIS",owner:"idleberg"},objectivec:{title:"Objective-C",require:"c",alias:"objc",owner:"uranusjr"},ocaml:{title:"OCaml",owner:"Golmote"},odin:{title:"Odin",owner:"edukisto"},opencl:{title:"OpenCL",require:"c",modify:["c","cpp"],owner:"Milania1"},openqasm:{title:"OpenQasm",alias:"qasm",owner:"RunDevelopment"},oz:{title:"Oz",owner:"Golmote"},parigp:{title:"PARI/GP",owner:"Golmote"},parser:{title:"Parser",require:"markup",owner:"Golmote"},pascal:{title:"Pascal",alias:"objectpascal",aliasTitles:{objectpascal:"Object Pascal"},owner:"Golmote"},pascaligo:{title:"Pascaligo",owner:"DefinitelyNotAGoat"},psl:{title:"PATROL Scripting Language",owner:"bertysentry"},pcaxis:{title:"PC-Axis",alias:"px",owner:"RunDevelopment"},peoplecode:{title:"PeopleCode",alias:"pcode",owner:"RunDevelopment"},perl:{title:"Perl",owner:"Golmote"},php:{title:"PHP",require:"markup-templating",owner:"milesj"},phpdoc:{title:"PHPDoc",require:["php","javadoclike"],modify:"php",owner:"RunDevelopment"},"php-extras":{title:"PHP Extras",require:"php",modify:"php",owner:"milesj"},"plant-uml":{title:"PlantUML",alias:"plantuml",owner:"RunDevelopment"},plsql:{title:"PL/SQL",require:"sql",owner:"Golmote"},powerquery:{title:"PowerQuery",alias:["pq","mscript"],owner:"peterbud"},powershell:{title:"PowerShell",owner:"nauzilus"},processing:{title:"Processing",require:"clike",owner:"Golmote"},prolog:{title:"Prolog",owner:"Golmote"},promql:{title:"PromQL",owner:"arendjr"},properties:{title:".properties",owner:"Golmote"},protobuf:{title:"Protocol Buffers",require:"clike",owner:"just-boris"},pug:{title:"Pug",require:["markup","javascript"],optional:["coffeescript","ejs","handlebars","less","livescript","markdown","scss","stylus","twig"],owner:"Golmote"},puppet:{title:"Puppet",owner:"Golmote"},pure:{title:"Pure",optional:["c","cpp","fortran"],owner:"Golmote"},purebasic:{title:"PureBasic",require:"clike",alias:"pbfasm",owner:"HeX0R101"},purescript:{title:"PureScript",require:"haskell",alias:"purs",owner:"sriharshachilakapati"},python:{title:"Python",alias:"py",owner:"multipetros"},qsharp:{title:"Q#",require:"clike",alias:"qs",owner:"fedonman"},q:{title:"Q (kdb+ database)",owner:"Golmote"},qml:{title:"QML",require:"javascript",owner:"RunDevelopment"},qore:{title:"Qore",require:"clike",owner:"temnroegg"},r:{title:"R",owner:"Golmote"},racket:{title:"Racket",require:"scheme",alias:"rkt",owner:"RunDevelopment"},cshtml:{title:"Razor C#",alias:"razor",require:["markup","csharp"],optional:["css","css-extras","javascript","js-extras"],owner:"RunDevelopment"},jsx:{title:"React JSX",require:["markup","javascript"],optional:["jsdoc","js-extras","js-templates"],owner:"vkbansal"},tsx:{title:"React TSX",require:["jsx","typescript"]},reason:{title:"Reason",require:"clike",owner:"Golmote"},regex:{title:"Regex",owner:"RunDevelopment"},rego:{title:"Rego",owner:"JordanSh"},renpy:{title:"Ren'py",alias:"rpy",owner:"HyuchiaDiego"},rescript:{title:"ReScript",alias:"res",owner:"vmarcosp"},rest:{title:"reST (reStructuredText)",owner:"Golmote"},rip:{title:"Rip",owner:"ravinggenius"},roboconf:{title:"Roboconf",owner:"Golmote"},robotframework:{title:"Robot Framework",alias:"robot",owner:"RunDevelopment"},ruby:{title:"Ruby",require:"clike",alias:"rb",owner:"samflores"},rust:{title:"Rust",owner:"Golmote"},sas:{title:"SAS",optional:["groovy","lua","sql"],owner:"Golmote"},sass:{title:"Sass (Sass)",require:"css",optional:"css-extras",owner:"Golmote"},scss:{title:"Sass (Scss)",require:"css",optional:"css-extras",owner:"MoOx"},scala:{title:"Scala",require:"java",owner:"jozic"},scheme:{title:"Scheme",owner:"bacchus123"},"shell-session":{title:"Shell session",require:"bash",alias:["sh-session","shellsession"],owner:"RunDevelopment"},smali:{title:"Smali",owner:"RunDevelopment"},smalltalk:{title:"Smalltalk",owner:"Golmote"},smarty:{title:"Smarty",require:"markup-templating",optional:"php",owner:"Golmote"},sml:{title:"SML",alias:"smlnj",aliasTitles:{smlnj:"SML/NJ"},owner:"RunDevelopment"},solidity:{title:"Solidity (Ethereum)",alias:"sol",require:"clike",owner:"glachaud"},"solution-file":{title:"Solution file",alias:"sln",owner:"RunDevelopment"},soy:{title:"Soy (Closure Template)",require:"markup-templating",owner:"Golmote"},sparql:{title:"SPARQL",require:"turtle",owner:"Triply-Dev",alias:"rq"},"splunk-spl":{title:"Splunk SPL",owner:"RunDevelopment"},sqf:{title:"SQF: Status Quo Function (Arma 3)",require:"clike",owner:"RunDevelopment"},sql:{title:"SQL",owner:"multipetros"},squirrel:{title:"Squirrel",require:"clike",owner:"RunDevelopment"},stan:{title:"Stan",owner:"RunDevelopment"},stata:{title:"Stata Ado",require:["mata","java","python"],owner:"RunDevelopment"},iecst:{title:"Structured Text (IEC 61131-3)",owner:"serhioromano"},stylus:{title:"Stylus",owner:"vkbansal"},supercollider:{title:"SuperCollider",alias:"sclang",owner:"RunDevelopment"},swift:{title:"Swift",owner:"chrischares"},systemd:{title:"Systemd configuration file",owner:"RunDevelopment"},"t4-templating":{title:"T4 templating",owner:"RunDevelopment"},"t4-cs":{title:"T4 Text Templates (C#)",require:["t4-templating","csharp"],alias:"t4",owner:"RunDevelopment"},"t4-vb":{title:"T4 Text Templates (VB)",require:["t4-templating","vbnet"],owner:"RunDevelopment"},tap:{title:"TAP",owner:"isaacs",require:"yaml"},tcl:{title:"Tcl",owner:"PeterChaplin"},tt2:{title:"Template Toolkit 2",require:["clike","markup-templating"],owner:"gflohr"},textile:{title:"Textile",require:"markup",optional:"css",owner:"Golmote"},toml:{title:"TOML",owner:"RunDevelopment"},tremor:{title:"Tremor",alias:["trickle","troy"],owner:"darach",aliasTitles:{trickle:"trickle",troy:"troy"}},turtle:{title:"Turtle",alias:"trig",aliasTitles:{trig:"TriG"},owner:"jakubklimek"},twig:{title:"Twig",require:"markup-templating",owner:"brandonkelly"},typescript:{title:"TypeScript",require:"javascript",optional:"js-templates",alias:"ts",owner:"vkbansal"},typoscript:{title:"TypoScript",alias:"tsconfig",aliasTitles:{tsconfig:"TSConfig"},owner:"dkern"},unrealscript:{title:"UnrealScript",alias:["uscript","uc"],owner:"RunDevelopment"},uorazor:{title:"UO Razor Script",owner:"jaseowns"},uri:{title:"URI",alias:"url",aliasTitles:{url:"URL"},owner:"RunDevelopment"},v:{title:"V",require:"clike",owner:"taggon"},vala:{title:"Vala",require:"clike",optional:"regex",owner:"TemplarVolk"},vbnet:{title:"VB.Net",require:"basic",owner:"Bigsby"},velocity:{title:"Velocity",require:"markup",owner:"Golmote"},verilog:{title:"Verilog",owner:"a-rey"},vhdl:{title:"VHDL",owner:"a-rey"},vim:{title:"vim",owner:"westonganger"},"visual-basic":{title:"Visual Basic",alias:["vb","vba"],aliasTitles:{vba:"VBA"},owner:"Golmote"},warpscript:{title:"WarpScript",owner:"RunDevelopment"},wasm:{title:"WebAssembly",owner:"Golmote"},"web-idl":{title:"Web IDL",alias:"webidl",owner:"RunDevelopment"},wiki:{title:"Wiki markup",require:"markup",owner:"Golmote"},wolfram:{title:"Wolfram language",alias:["mathematica","nb","wl"],aliasTitles:{mathematica:"Mathematica",nb:"Mathematica Notebook"},owner:"msollami"},wren:{title:"Wren",owner:"clsource"},xeora:{title:"Xeora",require:"markup",alias:"xeoracube",aliasTitles:{xeoracube:"XeoraCube"},owner:"freakmaxi"},"xml-doc":{title:"XML doc (.net)",require:"markup",modify:["csharp","fsharp","vbnet"],owner:"RunDevelopment"},xojo:{title:"Xojo (REALbasic)",owner:"Golmote"},xquery:{title:"XQuery",require:"markup",owner:"Golmote"},yaml:{title:"YAML",alias:"yml",owner:"hason"},yang:{title:"YANG",owner:"RunDevelopment"},zig:{title:"Zig",owner:"RunDevelopment"}},plugins:{meta:{path:"plugins/{id}/prism-{id}",link:"plugins/{id}/"},"line-highlight":{title:"Line Highlight",description:"Highlights specific lines and/or line ranges."},"line-numbers":{title:"Line Numbers",description:"Line number at the beginning of code lines.",owner:"kuba-kubula"},"show-invisibles":{title:"Show Invisibles",description:"Show hidden characters such as tabs and line breaks.",optional:["autolinker","data-uri-highlight"]},autolinker:{title:"Autolinker",description:"Converts URLs and emails in code to clickable links. Parses Markdown links in comments."},wpd:{title:"WebPlatform Docs",description:'Makes tokens link to WebPlatform.org documentation. The links open in a new tab.'},"custom-class":{title:"Custom Class",description:"This plugin allows you to prefix Prism's default classes (.comment can become .namespace--comment) or replace them with your defined ones (like .editor__comment). You can even add new classes.",owner:"dvkndn",noCSS:!0},"file-highlight":{title:"File Highlight",description:"Fetch external files and highlight them with Prism. Used on the Prism website itself.",noCSS:!0},"show-language":{title:"Show Language",description:"Display the highlighted language in code blocks (inline code does not show the label).",owner:"nauzilus",noCSS:!0,require:"toolbar"},"jsonp-highlight":{title:"JSONP Highlight",description:"Fetch content with JSONP and highlight some interesting content (e.g. GitHub/Gists or Bitbucket API).",noCSS:!0,owner:"nauzilus"},"highlight-keywords":{title:"Highlight Keywords",description:"Adds special CSS classes for each keyword for fine-grained highlighting.",owner:"vkbansal",noCSS:!0},"remove-initial-line-feed":{title:"Remove initial line feed",description:"Removes the initial line feed in code blocks.",owner:"Golmote",noCSS:!0},"inline-color":{title:"Inline color",description:"Adds a small inline preview for colors in style sheets.",require:"css-extras",owner:"RunDevelopment"},previewers:{title:"Previewers",description:"Previewers for angles, colors, gradients, easing and time.",require:"css-extras",owner:"Golmote"},autoloader:{title:"Autoloader",description:"Automatically loads the needed languages to highlight the code blocks.",owner:"Golmote",noCSS:!0},"keep-markup":{title:"Keep Markup",description:"Prevents custom markup from being dropped out during highlighting.",owner:"Golmote",optional:"normalize-whitespace",noCSS:!0},"command-line":{title:"Command Line",description:"Display a command line with a prompt and, optionally, the output/response from the commands.",owner:"chriswells0"},"unescaped-markup":{title:"Unescaped Markup",description:"Write markup without having to escape anything."},"normalize-whitespace":{title:"Normalize Whitespace",description:"Supports multiple operations to normalize whitespace in code blocks.",owner:"zeitgeist87",optional:"unescaped-markup",noCSS:!0},"data-uri-highlight":{title:"Data-URI Highlight",description:"Highlights data-URI contents.",owner:"Golmote",noCSS:!0},toolbar:{title:"Toolbar",description:"Attach a toolbar for plugins to easily register buttons on the top of a code block.",owner:"mAAdhaTTah"},"copy-to-clipboard":{title:"Copy to Clipboard Button",description:"Add a button that copies the code block to the clipboard when clicked.",owner:"mAAdhaTTah",require:"toolbar",noCSS:!0},"download-button":{title:"Download Button",description:"A button in the toolbar of a code block adding a convenient way to download a code file.",owner:"Golmote",require:"toolbar",noCSS:!0},"match-braces":{title:"Match braces",description:"Highlights matching braces.",owner:"RunDevelopment"},"diff-highlight":{title:"Diff Highlight",description:"Highlights the code inside diff blocks.",owner:"RunDevelopment",require:"diff"},"filter-highlight-all":{title:"Filter highlightAll",description:"Filters the elements the highlightAll and highlightAllUnder methods actually highlight.",owner:"RunDevelopment",noCSS:!0},treeview:{title:"Treeview",description:"A language with special styles to highlight file system tree structures.",owner:"Golmote"}}})},2885:(e,t,n)=>{const r=n(9901),a=n(9642),o=new Set;function i(e){void 0===e?e=Object.keys(r.languages).filter((e=>"meta"!=e)):Array.isArray(e)||(e=[e]);const t=[...o,...Object.keys(Prism.languages)];a(r,e,t).load((e=>{if(!(e in r.languages))return void(i.silent||console.warn("Language does not exist: "+e));const t="./prism-"+e;delete n.c[n(6500).resolve(t)],delete Prism.languages[e],n(6500)(t),o.add(e)}))}i.silent=!1,e.exports=i},6726:(e,t,n)=>{var r={"./":2885};function a(e){var t=o(e);return n(t)}function o(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=o,e.exports=a,a.id=6726},6500:(e,t,n)=>{var r={"./":2885};function a(e){var t=o(e);return n(t)}function o(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=o,e.exports=a,a.id=6500},9642:e=>{"use strict";var t=function(){var e=function(){};function t(e,t){Array.isArray(e)?e.forEach(t):null!=e&&t(e,0)}function n(e){for(var t={},n=0,r=e.length;n "));var l={},s=e[r];if(s){function u(t){if(!(t in e))throw new Error(r+" depends on an unknown component "+t);if(!(t in l))for(var i in a(t,o),l[t]=!0,n[t])l[i]=!0}t(s.require,u),t(s.optional,u),t(s.modify,u)}n[r]=l,o.pop()}}return function(e){var t=n[e];return t||(a(e,r),t=n[e]),t}}function a(e){for(var t in e)return!0;return!1}return function(o,i,l){var s=function(e){var t={};for(var n in e){var r=e[n];for(var a in r)if("meta"!=a){var o=r[a];t[a]="string"==typeof o?{title:o}:o}}return t}(o),u=function(e){var n;return function(r){if(r in e)return r;if(!n)for(var a in n={},e){var o=e[a];t(o&&o.alias,(function(t){if(t in n)throw new Error(t+" cannot be alias for both "+a+" and "+n[t]);if(t in e)throw new Error(t+" cannot be alias of "+a+" because it is a component.");n[t]=a}))}return n[r]||r}}(s);i=i.map(u),l=(l||[]).map(u);var c=n(i),d=n(l);i.forEach((function e(n){var r=s[n];t(r&&r.require,(function(t){t in d||(c[t]=!0,e(t))}))}));for(var f,p=r(s),m=c;a(m);){for(var h in f={},m){var g=s[h];t(g&&g.modify,(function(e){e in d&&(f[e]=!0)}))}for(var v in d)if(!(v in c))for(var b in p(v))if(b in c){f[v]=!0;break}for(var y in m=f)c[y]=!0}var w={getIds:function(){var e=[];return w.load((function(t){e.push(t)})),e},load:function(t,n){return function(t,n,r,a){var o=a?a.series:void 0,i=a?a.parallel:e,l={},s={};function u(e){if(e in l)return l[e];s[e]=!0;var a,c=[];for(var d in t(e))d in n&&c.push(d);if(0===c.length)a=r(e);else{var f=i(c.map((function(e){var t=u(e);return delete s[e],t})));o?a=o(f,(function(){return r(e)})):r(e)}return l[e]=a}for(var c in n)u(c);var d=[];for(var f in s)d.push(l[f]);return i(d)}(p,c,t,n)}};return w}}();e.exports=t},2703:(e,t,n)=>{"use strict";var r=n(414);function a(){}function o(){}o.resetWarningCache=a,e.exports=function(){function e(e,t,n,a,o,i){if(i!==r){var l=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw l.name="Invariant Violation",l}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:o,resetWarningCache:a};return n.PropTypes=n,n}},5697:(e,t,n)=>{e.exports=n(2703)()},414:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},4448:(e,t,n)=>{"use strict";var r=n(7294),a=n(7418),o=n(3840);function i(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n