diff --git a/developer-guide/03-Background jobs/03-avoiding-od.md b/developer-guide/03-Background jobs/03-avoiding-od.md index e13e04889..03ac62490 100644 --- a/developer-guide/03-Background jobs/03-avoiding-od.md +++ b/developer-guide/03-Background jobs/03-avoiding-od.md @@ -36,7 +36,33 @@ class JustPause(BackgroundJobWithDodgingContrib): ``` -3. We also want some "buffer" time before and after an OD reading. For example, if using a bubbler, we want the bubbles to dissipate completely before taking an OD reading. We should stop bubbling early then. To set these times, add the following to your config.ini: +3. You should also consider what the job should do if OD reading isn't turned on. That is, what should the default behaviour be without OD reading. Also, what should occur when the OD reading is turned on _late_. + +You can handle the "dodging" case and "continuous" with the `initialize_*` methods: + +```python +class JustPause(BackgroundJobWithDodgingContrib): + + ... + + def initialize_dodging_operation(self): + self.logger("OD reading is ON and I'm enabled for dodging, so set up what I need...") + + def initialize_continuous_operation(self): + self.logger("OD reading is off, or being ignored, so set up what I need for that...") + + def action_to_do_before_od_reading(self): + # example + self.logger.debug("Pausing") + self.stop_pumping() + + def action_to_do_after_od_reading(self): + # example + self.logger.debug("Unpausing") + self.start_pumping() +``` + +4. We also want some "buffer" time before and after an OD reading. For example, if using a bubbler, we want the bubbles to dissipate completely before taking an OD reading. We should stop bubbling early then. To set these times, add the following to your config.ini: ``` [] diff --git a/developer-guide/07-Plugins/03-plugin-as-python-package.md b/developer-guide/07-Plugins/03-plugin-as-python-package.md index 023735dab..7c681b90c 100644 --- a/developer-guide/07-Plugins/03-plugin-as-python-package.md +++ b/developer-guide/07-Plugins/03-plugin-as-python-package.md @@ -206,7 +206,7 @@ fields: There are lots of examples of automation yaml files [here](https://github.com/Pioreactor/pioreactorui/tree/master/contrib/automations). -#### 5. Optional: adding tables to the SQL store +#### 5. Optional: adding tables to the SQL database and exposing them on the Export Data page You can also add a file called `additional_sql.sql` that will run against the SQLite database. For example, a CO₂ sensor may want to create a new table in the database to store its sensor data. It's `additional_sql.sql` may look like: @@ -267,6 +267,33 @@ include /additional_sql.sql See an example plugin that uses this idea [here](https://github.com/Pioreactor/co2-reading-plugin). +---- + +Now that you've added the code for adding to the database, you can also allow users to export your data from the UI's Export Data page. To do this, added a new folder `/exportable_datasets` to your project's source folder (along side the `__init__.py` file), and add a YAML file: + +```yaml +dataset_name: some_unique_dataset_name +default_order_by: timestamp # for example +description: A lovely description which shows up in the UI +display_name: A lovely name which shows up in the UI +has_experiment: true # does your SQL table have an experiment column.? +has_unit: true # does your SQL table have an pioreactor_unit column.? +source: your_plugin_name +table: the_target_table # see also query below +timestamp_columns: +- timestamp +always_partition_by_unit: false +query: SELECT * FROM the_target_table WHERE reading < 4 AND ... # optional: you can specify a query. +``` + +You can add multiple dataset YAML files, too. + +:::note +Include the following in your MANIFEST.IN: +``` +recursive-include your_plugin_name/exportable_datasets *.yaml +``` +::: #### 6. Optional: adding a custom chart to the UI diff --git a/developer-guide/20-User interface/04-adding-datasets.md b/developer-guide/20-User interface/04-adding-datasets.md new file mode 100644 index 000000000..03fc97a7f --- /dev/null +++ b/developer-guide/20-User interface/04-adding-datasets.md @@ -0,0 +1,23 @@ +--- +title: Adding datasets to the Export Data page +slug: /dataset-to-ui +--- + +You can allow users to export your data from the UI's Export Data page. To do this, add a YAML file to `~/.pioreactor/plugins/exportable_datasets` with the following information: + +```yaml +dataset_name: some_unique_dataset_name +default_order_by: timestamp # for example +description: A lovely description which shows up in the UI +display_name: A lovely name which shows up in the UI +has_experiment: true # does your SQL table have an experiment column.? +has_unit: true # does your SQL table have an pioreactor_unit column.? +source: app +table: the_target_table # see also query below +timestamp_columns: +- timestamp +always_partition_by_unit: false +query: SELECT * FROM the_target_table WHERE reading < 4 AND ... # optional: you can specify a query. +``` + +After adding this file, visit the Export Page in the UI. \ No newline at end of file diff --git a/developer-guide/30-Development/01-local-development.md b/developer-guide/30-Development/01-local-development.md index 28e9e3879..1b2d1b121 100644 --- a/developer-guide/30-Development/01-local-development.md +++ b/developer-guide/30-Development/01-local-development.md @@ -14,12 +14,12 @@ slug: /local-development pip3 install -e . pip3 install -r requirements/requirements_dev.txt ``` -4. In the pioreactor folder, create a folder called `.pioreactor/` and `.pioreactor/storage`. +4. In the pioreactor folder, create a folder called `.pioreactor/` and other important subdirectories. ``` mkdir .pioreactor - mkdir .pioreactor/storage + mkdir .pioreactor/storage .pioreactor/plugins .pioreactor/exportable_datasets ``` -5. The configuration file that is used is `config.dev.ini`, and is provided in the repository. +5. The configuration file that is used is `config.dev.ini`, and is provided in the repository. Move this to `.pioreactor/`. ### MQTT diff --git a/package-lock.json b/package-lock.json index c21f1b0ec..64ff4835d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,9 +13,9 @@ "@docusaurus/theme-classic": "^3.0.1", "@docusaurus/theme-common": "^3.0.1", "@easyops-cn/docusaurus-search-local": "^0.38.1", - "@emeraldpay/hashicon-react": "^0.5.2", "@mdx-js/react": "^3.0.0", "@svgr/webpack": "^5.5.0", + "boring-avatars": "^1.11.2", "clsx": "^1.1.1", "file-loader": "^6.2.0", "hast-util-is-element": "^1.1.0", @@ -9276,43 +9276,6 @@ "react-dom": "^16.14.0 || 17 || ^18" } }, - "node_modules/@emeraldpay/hashicon": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@emeraldpay/hashicon/-/hashicon-0.5.2.tgz", - "integrity": "sha512-MNsjV+Vik+ofOYmGPcdAQW4CoSSrTE2Iq2xYNS8PxV84QrgOLTsC/pV6EWb1N/dTY9ndMV/RAAzGh6cmrZf4zA==", - "dependencies": { - "@stablelib/blake2s": "^1.0.0", - "js-sha3": "^0.8.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@emeraldpay/hashicon-react": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@emeraldpay/hashicon-react/-/hashicon-react-0.5.2.tgz", - "integrity": "sha512-XCoYKpq8QQOniiSZf5ouzdvXbKfG6q4ICHRqCO/GNofiF0Ra+LR/7+tomHlXVcLPBS9sDAoZQQw/Sr24KRAbJg==", - "dependencies": { - "@emeraldpay/hashicon": "^0.5.2", - "react": "^16.8.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@emeraldpay/hashicon-react/node_modules/react": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", - "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@hapi/hoek": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", @@ -9966,39 +9929,6 @@ "node": ">=14" } }, - "node_modules/@stablelib/binary": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", - "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", - "dependencies": { - "@stablelib/int": "^1.0.1" - } - }, - "node_modules/@stablelib/blake2s": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/blake2s/-/blake2s-1.0.1.tgz", - "integrity": "sha512-Nnp7ULL65b4zEOkf3IdfL74xHhZXMCg7HBjBYO666a0o+DIY6GDEhUCqH6dws8nsSZgZO+V5+s2VyYKKGdFMZw==", - "dependencies": { - "@stablelib/binary": "^1.0.1", - "@stablelib/hash": "^1.0.1", - "@stablelib/wipe": "^1.0.1" - } - }, - "node_modules/@stablelib/hash": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz", - "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==" - }, - "node_modules/@stablelib/int": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", - "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==" - }, - "node_modules/@stablelib/wipe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", - "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==" - }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", @@ -11194,6 +11124,11 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, + "node_modules/boring-avatars": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/boring-avatars/-/boring-avatars-1.11.2.tgz", + "integrity": "sha512-3+wkwPeObwS4R37FGXMYViqc4iTrIRj5yzfX9Qy4mnpZ26sX41dGMhsAgmKks1r/uufY1pl4vpgzMWHYfJRb2A==" + }, "node_modules/boxen": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", @@ -15908,11 +15843,6 @@ "@sideway/pinpoint": "^2.0.0" } }, - "node_modules/js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -30270,36 +30200,6 @@ "tslib": "^2.4.0" } }, - "@emeraldpay/hashicon": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@emeraldpay/hashicon/-/hashicon-0.5.2.tgz", - "integrity": "sha512-MNsjV+Vik+ofOYmGPcdAQW4CoSSrTE2Iq2xYNS8PxV84QrgOLTsC/pV6EWb1N/dTY9ndMV/RAAzGh6cmrZf4zA==", - "requires": { - "@stablelib/blake2s": "^1.0.0", - "js-sha3": "^0.8.0" - } - }, - "@emeraldpay/hashicon-react": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@emeraldpay/hashicon-react/-/hashicon-react-0.5.2.tgz", - "integrity": "sha512-XCoYKpq8QQOniiSZf5ouzdvXbKfG6q4ICHRqCO/GNofiF0Ra+LR/7+tomHlXVcLPBS9sDAoZQQw/Sr24KRAbJg==", - "requires": { - "@emeraldpay/hashicon": "^0.5.2", - "react": "^16.8.0" - }, - "dependencies": { - "react": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", - "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - } - } - } - }, "@hapi/hoek": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", @@ -30731,39 +30631,6 @@ "webpack-sources": "^3.2.2" } }, - "@stablelib/binary": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", - "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", - "requires": { - "@stablelib/int": "^1.0.1" - } - }, - "@stablelib/blake2s": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/blake2s/-/blake2s-1.0.1.tgz", - "integrity": "sha512-Nnp7ULL65b4zEOkf3IdfL74xHhZXMCg7HBjBYO666a0o+DIY6GDEhUCqH6dws8nsSZgZO+V5+s2VyYKKGdFMZw==", - "requires": { - "@stablelib/binary": "^1.0.1", - "@stablelib/hash": "^1.0.1", - "@stablelib/wipe": "^1.0.1" - } - }, - "@stablelib/hash": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz", - "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==" - }, - "@stablelib/int": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", - "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==" - }, - "@stablelib/wipe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", - "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==" - }, "@svgr/babel-plugin-add-jsx-attribute": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", @@ -31732,6 +31599,11 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, + "boring-avatars": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/boring-avatars/-/boring-avatars-1.11.2.tgz", + "integrity": "sha512-3+wkwPeObwS4R37FGXMYViqc4iTrIRj5yzfX9Qy4mnpZ26sX41dGMhsAgmKks1r/uufY1pl4vpgzMWHYfJRb2A==" + }, "boxen": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", @@ -35035,11 +34907,6 @@ "@sideway/pinpoint": "^2.0.0" } }, - "js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/package.json b/package.json index 79259ea51..b4718c490 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,6 @@ "@docusaurus/theme-classic": "^3.0.1", "@docusaurus/theme-common": "^3.0.1", "@easyops-cn/docusaurus-search-local": "^0.38.1", - "@emeraldpay/hashicon-react": "^0.5.2", "@mdx-js/react": "^3.0.0", "@svgr/webpack": "^5.5.0", "clsx": "^1.1.1", @@ -31,7 +30,8 @@ "rehype-katex": "^7.0.0", "rehype-math": "^0.2.0", "remark-math": "^6.0.0", - "url-loader": "^4.1.1" + "url-loader": "^4.1.1", + "boring-avatars": "^1.11.2" }, "browserslist": { "production": [ diff --git a/src/components/HomepageFeatures.module.css b/src/components/HomepageFeatures.module.css index af278730d..9c9874f49 100644 --- a/src/components/HomepageFeatures.module.css +++ b/src/components/HomepageFeatures.module.css @@ -10,23 +10,28 @@ width: auto; margin-bottom: 10px; transition: transform 0.4s ease, fill 0.4s ease; +} +.featureSvg, .featureCard { + transition: transform 0.3s ease, box-shadow 0.3s ease, fill 0.3s ease; } .featureCard { - transition: transform 0.4s ease, box-shadow 0.4s ease; - border-radius: 8px; /* Optional: for rounded corners */ - padding: 16px; + transition: transform 0.3s ease, box-shadow 0.3s ease; + border-radius: 8px; + padding: 24px; /* Increased padding for more breathing room */ + text-align: center; /* Ensure content stays centered */ } + .featureCard:hover { - transform: translateY(-2px); /* Less lift */ - box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1); /* Softer shadow */ + transform: translateY(-1px); /* Slight hover effect */ + box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.08); /* Softer shadow */ } .featureCard:hover .featureSvg { - transform: scale(1.05); /* Slightly smaller scale */ - fill: #FFB84D; /* Subtle color change */ + transform: scale(1.03); /* Even smaller scale */ + fill: #FFCC80; /* Softer, lighter color */ } diff --git a/src/components/ListAvailablePlugins.js b/src/components/ListAvailablePlugins.js index 371d6816f..140d08f06 100644 --- a/src/components/ListAvailablePlugins.js +++ b/src/components/ListAvailablePlugins.js @@ -1,6 +1,6 @@ import React from 'react'; import clsx from 'clsx'; -import { Hashicon } from "@emeraldpay/hashicon-react"; +import Avatar from "boring-avatars"; export default function ListAvailablePlugins(){ @@ -28,7 +28,7 @@ export default function ListAvailablePlugins(){
  • - +

    {plugin.name}

    View homepage ↗