puml-for-markdown 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ v17.3.1
@@ -0,0 +1 @@
1
+ TODO
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [2022] [Daniel Yakobian]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # puml-for-markdown
2
+
3
+ ## Goal
4
+ The goal is to make PlantUML diagrams easily accessible from markdown, specifically GitHub flavored.
5
+ * Should work with both private and public repositories
6
+ * Should not have to use tokens to get it to work with private repositories
7
+ * Should support hyperlinking to other diagrams
8
+ * Should support PlantUML `!include`
9
+ * Should support PlantUML [sprites](https://crashedmind.github.io/PlantUMLHitchhikersGuide/PlantUMLSpriteLibraries/plantuml_sprites.html) (small graphic images)
10
+ * Any time you make changes to diagrams you should be able to run the CLI tool to update the markdown links
11
+
12
+ ## Examples
13
+ The following examples are of a project I worked on, **yellow components will link you to another diagram**. Obviously the
14
+ diagram content has been obfuscated.
15
+
16
+ ### Example With Link Only
17
+
18
+ [Only Link](https://tinyurl.com/y847en68)<!--[Only Link](./puml/level_1_system_view.puml)-->
19
+
20
+ ### Example with Image, Click to Open Interactive Diagram
21
+
22
+ [![Example With Graph Image](https://tinyurl.com/y847en68)](https://tinyurl.com/y847en68)<!--![Example With Graph Image](./puml/level_1_system_view.puml)-->
23
+
24
+ ## Background
25
+ [PlantUML](https://plantuml.com) allows you to create diagrams that are defined using a simple and intuitive language.
26
+ PlantUML diagrams are great for designing new projects but they don't work very well in Github markdown preview. There
27
+ are some [workarounds](https://stackoverflow.com/questions/32203610/how-to-integrate-uml-diagrams-into-gitlab-or-github),
28
+ but I found these to be unstable and they have a lot of caveats.
29
+
30
+ ### Installation
31
+ `npm i -g puml-for-markdown`
32
+
33
+ ### Basic Usage
34
+ Whenever you run the CLI it will add a tinyurl link to the rendered SVG next to the markdown comments referencing a
35
+ puml diagram. E.g. `<!--[Example With Only Link](./puml/level_1_system_view.puml)-->` will be replaced with
36
+ ```
37
+ [Example With Only Link](https://tinyurl.com/yfpclfpp)<!--[Example With Only Link](./puml/level_1_system_view.puml)-->
38
+ ```
39
+
40
+ When `!` is included in front of the markdown link, it will render the diagram image in the markdown. If the image is
41
+ clicked it will open up the diagram. E.g. `<!--![Example With Graph Image](./puml/level_1_system_view.puml)-->` will be
42
+ replaced with
43
+ ```
44
+ [![Example With Graph Image](https://tinyurl.com/yfpclfpp)](https://tinyurl.com/yfpclfpp)<!--![Example With Graph Image](./puml/level_1_system_view.puml)-->
45
+ ```
46
+
47
+ If you want to update the link text or switch it between image and link, just update the comment and rerun the CLI. You
48
+ don't need to delete the rendered image or link.
49
+
50
+ Optionally you can specify the CLI to output the diagram images as png and/or svg.
51
+
52
+ ### CLI Usage
53
+ ```
54
+ Usage: puml-for-markdown [options]
55
+
56
+ An application to add interactive PUML diagrams to your github markdown files. If running with default arguments, run in project root directory.
57
+
58
+ Options:
59
+ -x, --root-directory <path> The path to your project (default: CWD)
60
+ -r, --hot-reload Rerun markdown generator every `interval` seconds, determined by interval option
61
+ -v, --interval-seconds <number> If --hot-reload is set, how often should it reload (default: 2)
62
+ -p, --puml-directory <path> Path to directory containing puml files which are referenced in markdown files (default: rootDirectory)
63
+ -m, --markdown-directory <path> Path to directory containing markdown files referencing puml files (default: rootDirectory)
64
+ -g, --respect-gitignore Automatically ignore MD files in .gitignore paths (default: true)
65
+ -i, --gitignore-path <path> If --respect-gitignore is set, use set this as path to .gitignore file. (default: rootDirectory/.gitignore)
66
+ -d, --output-images If set, will output images of diagrams to the dist directory
67
+ -b, --dist-directory <path> If --output-images is set, path to output diagram images (default: rootDirectory/dist_puml)
68
+ -f, --image-formats <format> If --output-images is set, sets the output image format (choices: "png", "svg", "both", default: "png")
69
+ -t, --turn-off-link-shortening Use the full puml server link instead of the tiny url, if your diagrams are too big this won't work
70
+ -h, --help display help for command
71
+ ```
72
+
73
+ ### Notes
74
+ - Comments within inline or multiline code styling will be ignored
75
+ - Currently doesn't support cyclic graph references, i.e. a diagram can't reference any diagrams which reference back
76
+ to it
77
+ - See the [pre-commit hook](./.husky/pre-commit) to see how to add a git hook
78
+ - If you are saving diagram images and have puml files which only define constants/settings
79
+ (i.e. [example](./puml/constants.puml)) these aren't renderable on their own (since there is nothing to render)
80
+ and you'll see a warning in the console saying it failed to save the image to file.
81
+
82
+ ### How It Works
83
+ #### Using PlantUML Web Service to Render PUML Diagrams
84
+ You can use PlantUML using the online web service to generate images on-the-fly. A online demonstration is available at
85
+ [http://www.plantuml.com/plantuml](http://www.plantuml.com/plantuml). You can pass the
86
+ [encoded text](https://plantuml.com/text-encoding) of your diagrams to the web service in the url path and it will
87
+ generate an SVG or PNG for you. Here's a simple HelloWorld example
88
+ [http://www.plantuml.com/plantuml/uml/Aov9B2hXil98pSd9LoZFByf9iUOgBial0000](http://www.plantuml.com/plantuml/uml/Aov9B2hXil98pSd9LoZFByf9iUOgBial0000).
89
+ Large diagrams will have very long encoding strings, they can exceed maximum url length. They also don't look very good
90
+ in markdown files. By default the CLI will use the tinyurl.com service to shorten the link to the diagram.
91
+
92
+ #### Encoding PUML Diagrams for the Web Service
93
+ The CLI will use the `plantuml-encoder` package to encode puml files. To support hyperlinking diagrams we need to parse
94
+ all hyperlinks in the puml files. A dependency graph is created for the files and a DFS is performed where we create
95
+ links for the leaf nodes first, then replace the links in the parent nodes with the links to the leaf nodes. The puml
96
+ files are not actually modified, only the puml files content in memory is modified. By default the tinyurl free service
97
+ is used to shorten the links.
98
+
99
+ In order to support `!include` we parse puml files and replace any `!include` with the contents of the file referenced.
100
+
101
+ #### Parsing Markdown
102
+ The markdown files are then parsed for markdown comments. If the comments reference a PlantUML file, a link to the web
103
+ service url will be added next to the comment. Because these links contain the full PlantUML diagram encoding in it,
104
+ there are no issues using them in private repositories.
105
+
106
+ ### Testing
107
+ Currently there is no TDD, for now I've just been using the example diagrams to test. The examples are pretty extensive
108
+ though.
package/bin/index.js ADDED
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+
3
+ const path = require('path')
4
+
5
+ const {Command, Option} = require('commander')
6
+
7
+ const run = require(path.resolve(__dirname, '../index.js'))
8
+
9
+
10
+ // =============
11
+ // = CLI Setup =
12
+ // =============
13
+
14
+ new Command()
15
+ .description('An application to add interactive PUML diagrams to your github markdown files. If running with default arguments, run in project root directory.')
16
+ .addOption(new Option(
17
+ '-x, --root-directory <path>',
18
+ 'The path to your project'
19
+ ).default(process.cwd(), 'CWD')
20
+ )
21
+ .option(
22
+ '-r, --hot-reload',
23
+ 'Rerun markdown generator every `interval` seconds, determined by interval option',
24
+ )
25
+ .option(
26
+ '-v, --interval-seconds <number>',
27
+ 'If --hot-reload is set, how often should it reload',
28
+ 2,
29
+ )
30
+ .addOption(new Option(
31
+ '-p, --puml-directory <path>',
32
+ 'Path to directory containing puml files which are referenced in markdown files',
33
+ ).default(false, 'rootDirectory')
34
+ )
35
+ .addOption(new Option(
36
+ '-m, --markdown-directory <path>',
37
+ 'Path to directory containing markdown files referencing puml files',
38
+ ).default(false, 'rootDirectory')
39
+ )
40
+ .option(
41
+ '-g, --respect-gitignore',
42
+ 'Automatically ignore MD files in .gitignore paths',
43
+ true,
44
+ )
45
+ .addOption(new Option(
46
+ '-i, --gitignore-path <path>',
47
+ 'If --respect-gitignore is set, use set this as path to .gitignore file.'
48
+ ).default(false, 'rootDirectory/.gitignore')
49
+ )
50
+ .option(
51
+ '-d, --output-images',
52
+ 'If set, will output images of diagrams to the dist directory',
53
+ )
54
+ .addOption(new Option(
55
+ '-b, --dist-directory <path>',
56
+ 'If --output-images is set, path to output diagram images'
57
+ ).default(false, 'rootDirectory/dist_puml')
58
+ )
59
+ .addOption(new Option(
60
+ '-f, --image-formats <format>',
61
+ 'If --output-images is set, sets the output image format',
62
+ ).choices(['png', 'svg', 'both']).default('png')
63
+ )
64
+ .option(
65
+ '-t, --turn-off-link-shortening',
66
+ "Use the full puml server link instead of the tiny url, if your diagrams are too big this won't work",
67
+ )
68
+ .action(opts => {
69
+ opts.distDirectory = opts.distDirectory || path.resolve(opts.rootDirectory, 'dist_puml')
70
+ opts.gitignorePath = opts.gitignorePath || path.resolve(opts.rootDirectory, '.gitignore')
71
+ opts.markdownDirectory = opts.markdownDirectory || opts.rootDirectory
72
+ opts.pumlDirectory = opts.pumlDirectory || opts.rootDirectory
73
+ if (opts.imageFormats === 'both') {
74
+ opts.imageFormats = ['png', 'svg']
75
+ } else {
76
+ opts.imageFormats = [opts.imageFormats]
77
+ }
78
+ return run(opts).catch((e) => {
79
+ console.error('FATAL EXCEPTION')
80
+ console.error(e)
81
+ })
82
+ })
83
+ .parse(process.argv)
package/index.js ADDED
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Update any markdown links in a specified directory which reference a PUML diagram so they link directly to a
3
+ * puml server svg link. This is required for GitHub integration
4
+ * NOTE: Does not support cyclic dependencies, only DAGS
5
+ */
6
+
7
+ const fs = require('fs')
8
+ const path = require('path')
9
+ const assert = require('assert')
10
+
11
+ const bent = require('bent')
12
+ const Promise = require('bluebird')
13
+ const _ = require('lodash')
14
+ const glob = require('glob')
15
+ const plantUmlEncoder = require('plantuml-encoder')
16
+ const tiny = require('tinyurl');
17
+ const parseGitIgnore = require('gitignore-globs');
18
+
19
+
20
+ // CONSTANTS
21
+
22
+ const PUML_SVG_SERVER = 'https://www.plantuml.com/plantuml/svg'
23
+ const PUML_PNG_SERVER = 'https://www.plantuml.com/plantuml/png'
24
+
25
+ // HELPERS
26
+
27
+ const getBuffer = bent('buffer')
28
+ const mkdirIfDoesntExist = (p) => !fs.existsSync(p) && fs.mkdirSync(p, {recursive: true})
29
+ const mapUniqMatches = (s, re, mapper, match = re.exec(s), results = []) => {
30
+ if (match) return mapUniqMatches(s, re, mapper, re.exec(s), results.concat([match]))
31
+ return _.uniqBy(results, String).map(mapper)
32
+ }
33
+
34
+ const replaceIgnore = (ignoreRegex, str, replaceFn) => {
35
+ const splitRegex = new RegExp(`(?:(${ignoreRegex}))`, 'g')
36
+ const splitStrArr = str.split(splitRegex)
37
+ const replacedSplitStrArr = splitStrArr.map((splitStr) => {
38
+ if (splitStr.match(new RegExp(ignoreRegex))) return splitStr
39
+ return replaceFn(splitStr)
40
+ })
41
+ return replacedSplitStrArr.join('')
42
+ }
43
+
44
+ const replaceMdIgnoringInlineCode = (mdString, replaceFn) => {
45
+ return replaceIgnore('`[^\n]*?(?=`)`', mdString, replaceFn)
46
+ }
47
+
48
+ const replaceMdIgnoringCodeBlocks = (mdString, replaceFn) => {
49
+ return replaceIgnore('```[\\s\\S]*?(?=```)```', mdString, replaceFn)
50
+ }
51
+
52
+ const replaceMdIgnoringCode = (mdString, replaceFn) => {
53
+ return replaceMdIgnoringCodeBlocks(mdString, str => replaceMdIgnoringInlineCode(str, replaceFn))
54
+ }
55
+
56
+ const getPngUrl = encodedData => `${PUML_PNG_SERVER}/${encodedData}}`
57
+ const getSvgUrl = encodedData => `${PUML_SVG_SERVER}/${encodedData}}`
58
+ const getPumlTinyUrl = encodedData => tiny.shorten(getSvgUrl(encodedData))
59
+ const getImgUrl = (imageFormat, encodedData) => {
60
+ const imgFormatToGetUrlFn = {
61
+ png: getPngUrl,
62
+ svg: getSvgUrl
63
+ }
64
+ return imgFormatToGetUrlFn[imageFormat](encodedData)
65
+ }
66
+
67
+ // DATA STRUCTURES
68
+
69
+ class PumlLinks extends Map {
70
+ constructor(pumlPaths, turnOffLinkShortening) {
71
+ super();
72
+ this.turnOffLinkShortening = turnOffLinkShortening
73
+ // Mark paths so we know which should be visited
74
+ // Any path that's not marked 0 we know isn't a puml path because it doesn't correspond to a puml fil
75
+ pumlPaths.forEach(p => this.set(p, 0))
76
+ }
77
+
78
+ get(p) {
79
+ return super.get(p);
80
+ }
81
+
82
+ async set(pumlPath, v) {
83
+ if (typeof v === 'number') return super.set(pumlPath, v)
84
+
85
+ const encodedData = plantUmlEncoder.encode(v)
86
+ const url = this.turnOffLinkShortening ? getSvgUrl(encodedData) : await getPumlTinyUrl(encodedData)
87
+ return super.set(pumlPath, {encodedData, url, data: v});
88
+ }
89
+
90
+ has(pumlPath) {
91
+ return super.has(pumlPath);
92
+ }
93
+ }
94
+
95
+ // SAVING DIAGRAMS
96
+
97
+ const downloadImg = async (outputPath, imgUrl) => {
98
+ const imgBuffer = await getBuffer(imgUrl)
99
+ fs.writeFileSync(outputPath, imgBuffer)
100
+ }
101
+
102
+ const saveDiagram = async ({rootDirectory, distDirectory, pumlPath, imageFormat, encodedData}) => {
103
+ const outputPath = path.join(
104
+ distDirectory, pumlPath.replace(rootDirectory, '').replace(/\.puml$/, `.${imageFormat}`)
105
+ )
106
+
107
+ mkdirIfDoesntExist(path.dirname(outputPath))
108
+ const imgUrl = getImgUrl(imageFormat, encodedData)
109
+ try {
110
+ await downloadImg(outputPath, imgUrl)
111
+ } catch (e) {
112
+ console.warn(`\nWARN: Failed to save ${imgUrl} to ${outputPath}\n`)
113
+ if (e.statusCode !== 400) throw e
114
+ }
115
+ }
116
+
117
+ const saveDiagrams = async ({ rootDirectory, distDirectory, imageFormats, pumlLinks }) => {
118
+ fs.rmSync(distDirectory, {recursive: true, force: true});
119
+ for (let [pumlPath, {encodedData}] of pumlLinks) {
120
+ for (let imageFormat of imageFormats) {
121
+ await saveDiagram({
122
+ distDirectory, rootDirectory, pumlPath, imageFormat, encodedData
123
+ })
124
+ }
125
+ }
126
+ }
127
+
128
+ // PROCESSORS
129
+
130
+ /**
131
+ * Add the whole relative includes file into the puml instead of a reference
132
+ * @returns {string} The puml data where relative includes are replaced with the data from referenced file
133
+ */
134
+ const processIncludes = (pumlPath, data) => {
135
+ return data.replace(/!include (.*)/g, (fullMatch, url) => {
136
+ const fullUrl = path.resolve(path.dirname(pumlPath), url)
137
+ return fs.existsSync(fullUrl) ? fs.readFileSync(fullUrl, 'utf8') : fullMatch
138
+ })
139
+ }
140
+
141
+
142
+ /**
143
+ * Walk through each PUML file recursively so we can encode any linked files first since the files they link
144
+ * from are dependent
145
+ * @returns {Promise<string> | Promise<undefined>} Returns either processed puml file link, or original link
146
+ */
147
+ const processPumlFile = async (pumlPath, pumlLinks) => {
148
+ // Base Cases
149
+ if (!pumlLinks.has(pumlPath)) return pumlPath
150
+ if (pumlLinks.get(pumlPath)) return pumlLinks.get(pumlPath)
151
+
152
+ let data = fs.readFileSync(pumlPath, 'utf8')
153
+ await Promise.all(mapUniqMatches(data, /\$link=["']([^"']+)['"]/gm, async ([pumlLink, pumlLinkPath]) => {
154
+ const newLink = await processPumlFile(path.resolve(path.dirname(pumlPath), pumlLinkPath), pumlLinks)
155
+ data = data.replaceAll(pumlLink, `$link="${newLink.url}"`)
156
+ }))
157
+
158
+ data = processIncludes(pumlPath, data)
159
+ await pumlLinks.set(pumlPath, data)
160
+ return pumlLinks.get(pumlPath)
161
+ }
162
+
163
+ /**
164
+ * Update any markdown links which reference PUML diagram so they link directly to a puml server svg link
165
+ * Output them as new markdown in the docs directory
166
+ */
167
+ const processMdFile = async (mdPath, pumlLinks) => {
168
+ console.info(`Processing md file at ${mdPath}`)
169
+ let originalMdStr = fs.readFileSync(mdPath, 'utf8')
170
+ const findMdPumlLinksRE = new RegExp(
171
+ `(\\[?!?\\[?\\[[^\\[]*)?<!\-\-(!?\\[[^\\]]+\\])\\(([^)]+\\.puml)\\)\-\->`, 'g'
172
+ )
173
+ // Add puml server tinyurl link for puml links indicated in markdown comments
174
+ const mdWithUpdatedPumlLinks = replaceMdIgnoringCode(originalMdStr, (str) => {
175
+ return str.replace(findMdPumlLinksRE, (match, _, linkText, mdPumlLinkPath) => {
176
+ const pumlPath = path.resolve(path.dirname(mdPath), mdPumlLinkPath)
177
+ const pumlLink = pumlLinks.get(pumlPath)
178
+ if (!pumlLink) {
179
+ throw Error(`Could not find puml for md link path = ${mdPumlLinkPath}, absolute path = ${pumlPath}`)
180
+ }
181
+ // If the puml link is a markdown image
182
+ if (linkText[0] === '!') {
183
+ return `[${linkText}(${pumlLink.url})](${pumlLink.url})<!--${linkText}(${mdPumlLinkPath})-->`
184
+ }
185
+ // If the puml link is a markdown hyperlink
186
+ return `${linkText}(${pumlLink.url})<!--${linkText}(${mdPumlLinkPath})-->`
187
+ })
188
+ })
189
+
190
+
191
+ fs.writeFileSync(mdPath, mdWithUpdatedPumlLinks)
192
+ }
193
+
194
+ const runOnce = async (
195
+ {
196
+ rootDirectory,
197
+ markdownDirectory,
198
+ pumlDirectory,
199
+ distDirectory,
200
+ outputImages,
201
+ imageFormats,
202
+ respectGitignore,
203
+ gitignorePath,
204
+ turnOffLinkShortening,
205
+ }
206
+ ) => {
207
+ assert(fs.existsSync(markdownDirectory), "That's an invalid md folder path")
208
+ assert(fs.existsSync(pumlDirectory), "That's an invalid puml folder path")
209
+
210
+ const ignore = respectGitignore ? parseGitIgnore(gitignorePath) : []
211
+ const mdPaths = glob.sync(`${markdownDirectory}/**/*.md`, {ignore, nodir: true})
212
+ const pumlPaths = glob.sync(`${pumlDirectory}/**/*.puml`, {ignore, nodir: true})
213
+ const pumlLinks = new PumlLinks(pumlPaths, turnOffLinkShortening)
214
+
215
+ for (let p of pumlPaths) await processPumlFile(p, pumlLinks)
216
+ for (let p of mdPaths) await processMdFile(p, pumlLinks)
217
+
218
+ if (outputImages) {
219
+ await saveDiagrams({ rootDirectory, distDirectory, imageFormats, pumlLinks })
220
+ }
221
+ }
222
+
223
+ const reloadRun = ({intervalSeconds, ...rest}) =>
224
+ Promise.delay(intervalSeconds * 1000, rest)
225
+ .then(run).catch(console.error)
226
+ .finally(() => reloadRun({intervalSeconds, ...rest}))
227
+
228
+ const run = ({hotReload, ...rest}) => hotReload ? reloadRun(rest) : runOnce(rest)
229
+
230
+ module.exports = run
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "puml-for-markdown",
3
+ "version": "1.0.0",
4
+ "description": "An application to add interactive PUML diagrams to your github markdown files. If running with default arguments, run in project root directory.",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "prepare": "husky install"
8
+ },
9
+ "author": "danielyaa5",
10
+ "license": "ISC",
11
+ "dependencies": {
12
+ "bent": "7.3.12",
13
+ "bluebird": "3.7.2",
14
+ "commander": "8.2.0",
15
+ "gitignore-globs": "0.1.1",
16
+ "glob": "7.2.0",
17
+ "lodash": "4.17.21",
18
+ "plantuml-encoder": "1.4.0",
19
+ "tinyurl": "1.1.7"
20
+ },
21
+ "bin": {
22
+ "puml-for-markdown": "./bin/index.js"
23
+ },
24
+ "keywords": [
25
+ "uml",
26
+ "puml",
27
+ "plantuml",
28
+ "c4-plantuml",
29
+ "c4",
30
+ "diagrams",
31
+ "git",
32
+ "github",
33
+ "md",
34
+ "markdown",
35
+ "generator",
36
+ "cli",
37
+ "diagrams",
38
+ "documentation"
39
+ ],
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/danielyaa5/puml-for-markdown"
43
+ },
44
+ "devDependencies": {
45
+ "husky": "7.0.4"
46
+ }
47
+ }
@@ -0,0 +1,37 @@
1
+ @startuml
2
+
3
+ 'Icons
4
+ !define FA_5_URL https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/e53fd61259c0f2449edfe304cad83f51d0fbb5c1/font-awesome-5
5
+ !include FA_5_URL/cloud.puml
6
+ !include FA_5_URL/youtube.puml
7
+ !include FA_5_URL/server.puml
8
+
9
+ !define MATERIAL_URL https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/e53fd61259c0f2449edfe304cad83f51d0fbb5c1/material
10
+ !include MATERIAL_URL/zoom_in.puml
11
+
12
+ !define osaPuml https://raw.githubusercontent.com/Crashedmind/PlantUML-opensecurityarchitecture2-icons/master
13
+ !include osaPuml/Common.puml
14
+ !include osaPuml/User/all.puml
15
+
16
+ !include <office/Servers/database_server>
17
+ !include <office/Servers/file_server>
18
+ !include <office/Servers/application_server>
19
+ !include <office/Concepts/service_application>
20
+ !include <office/Concepts/firewall>
21
+
22
+ !define AWS_PUML https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/main/dist
23
+ !include AWS_PUML/AWSCommon.puml
24
+ !include AWS_PUML/Robotics/RoboMaker.puml
25
+
26
+ 'Common Styling
27
+ skinparam wrapWidth 250
28
+
29
+ UpdateElementStyle("System", $bgColor="#ffffff", $fontColor="#000000", $shadowing="true")
30
+ UpdateElementStyle("Person", $bgColor="#e0f1fb", $fontColor="#000000", $shape=RoundedBoxShape())
31
+
32
+ AddElementTag("clickableText", $fontColor="purple")
33
+ AddElementTag("button", $bgColor="#444444", $fontColor="#ffffff")
34
+ AddElementTag("primary", $bgColor="#ffdb58", $fontColor="#000000", $shadowing="false", $borderColor="Gray")
35
+ AddElementTag("highlightBoundary", $bgColor="#fce8ee")
36
+
37
+ @enduml
@@ -0,0 +1,32 @@
1
+ @startuml
2
+ ' Tables
3
+ !global $TYPE_X_SAMPLE_POOL_TABLE = "type_x_sample_pool"
4
+ !global $TYPE_Y_SAMPLE_POOL_TABLE = "type_y_sample_pool"
5
+ !unquoted procedure $SAMPLE_POOL_TABLE_DESC()
6
+ A subquery from which to query samples, should be joined to any
7
+ columns that you want to include in the $DAILY_COLLECTED_SAMPLES_TABLE table
8
+ !endprocedure
9
+
10
+ !global $SAMPLE_LABELS_TABLE = "some_other_label_db"
11
+ !global $SAMPLE_LABELS_ID = "sampleLabelsDb"
12
+ !global $SAMPLE_LABELS_CONTAINER_DESC = $LABELED_DATASOURCE_DESC
13
+ !global $SAMPLE_LABELS_COMPONENT_DESC = "Stores all samples which have been labeled.\n"
14
+ !global $SAMPLE_LABELS_TYPE = "BigTable"
15
+ !global $SAMPLE_LABELS_SCHEMA = "https://docs.google.com/spreadsheets"
16
+
17
+ !global $DAILY_COLLECTED_SAMPLES_TABLE = "type_y_samples_{date}"
18
+ !global $DAILY_COLLECTED_SAMPLES_ID = "dailySamplesDb"
19
+ !global $DAILY_COLLECTED_SAMPLES_DESC = "A days worth of samples collected during single run of the pipeline.\n"
20
+ !global $DAILY_COLLECTED_SAMPLES_TYPE = "BigQueryDailyTable"
21
+
22
+ !global $LABELED_DATASOURCE_ID = "labeledDatasource"
23
+ !global $LABELED_DATASOURCE_NAME = "labeled_datasource_bucket"
24
+ !global $LABELED_DATASOURCE_TYPE = "GCS"
25
+ !global $LABELED_DATASOURCE_DESC = "Where the labelers write their labeling results"
26
+
27
+ 'Columns
28
+ !global $SEND_FOR_LABELING_COLUMN = "send_for_labeling"
29
+ !global $CONTENT_ID_COLUMN = "some_id"
30
+
31
+ !global $SYSTEM_NAME = "My Project Name"
32
+ @enduml
@@ -0,0 +1,36 @@
1
+ @startuml
2
+ !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
3
+ !include ./c4_styles.puml
4
+ !include ./constants.puml
5
+
6
+ left header
7
+
8
+ \t<font size=22 color=black>$SYSTEM_NAME - System View</font>
9
+
10
+ \t<font size=14 color=#484848>The goals of my project are described here</font>
11
+
12
+
13
+
14
+
15
+ end header
16
+
17
+
18
+ System(sysName, "Labeling System\n", "", $sprite="cloud", $tags="primary", $link="./level_2_container_view.puml")
19
+
20
+ Boundary(labelers, ' \n<font color=black size=16>Labelers</font>', 'Human Resources') {
21
+ Person(labelerB, "Labelers B", "Labeler Type 2\n", $tags="a")
22
+ Person(labelerA, "Labelers A", "Labeler Type 2\n", $tags="a")
23
+ }
24
+ Rel_U(sysName, labelerB," Trigger samples for labeling")
25
+ Rel_D(labelerB, sysName," Store labeling results ")
26
+
27
+ System(videoSamples, "Videos", "Users and internal customers", $sprite="youtube")
28
+ Rel_D(videoSamples, labelerA, " Send samples to labelers")
29
+
30
+ System(modelTrainingInference, "Model Training + Inference\n\n<font color=#e0575e><$RoboMaker></font>\n", "", $tags="secondary")
31
+ Rel_D(labelerA, modelTrainingInference," Send labels")
32
+ Rel_R(modelTrainingInference, sysName, "Send model scores")
33
+ Rel_L(sysName, modelTrainingInference, "Send labels")
34
+
35
+ HIDE_STEREOTYPE()
36
+ @enduml
@@ -0,0 +1,55 @@
1
+ @startuml
2
+ !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
3
+ !include ./c4_styles.puml
4
+ !include ./constants.puml
5
+
6
+
7
+ left header
8
+
9
+ \t<font size=22 color=black>$SYSTEM_NAME - Container View</font>
10
+
11
+ \t<font size=14 color=black><U+25CF> Goal 1</font>
12
+ \t<font size=14 color=black><U+25CF> Goal 2</font>
13
+ \t<font size=14 color=black><U+25CF> Goal 3</font>
14
+ \t<font size=14 color=black><U+25CF> Goal 4</font>
15
+
16
+
17
+
18
+
19
+
20
+ end header
21
+
22
+
23
+ Person(labelerA, "Labeler A", "\t Labeler-A Description \t\n", $tags="a")
24
+ ContainerDb(bigQueryLabelDB, "LabelADb", "BigQuery", "A database where type A labels are stored")
25
+ ContainerDb(modelScoreDb, "ModelScoreDB", "BigQuery", "A database where model scores are stored")
26
+ Person(labelerB, "Labeler B", "\t Labeler-B Description \t\n", $tags="a")
27
+ ContainerDb($LABELED_DATASOURCE_ID, "$LABELED_DATASOURCE_NAME", "$LABELED_DATASOURCE_TYPE", $LABELED_DATASOURCE_DESC)
28
+ System_Boundary(sysName, ' \n<font color=black size=16>Label System FooBarBaz</font>') {
29
+ Container(samplingPipeline, "Pipeline", "some-etl-framework", "ETL job for sampling / labeling", $tags="primary", $link="./level_3_component_view_pipeline.puml")
30
+ Container(labelRetrievalJob, "LabelRetrievalJob", "some-etl-framework", "DAG for retrieving and storing label data from labelers.", $tags="primary", $link="./level_3_component_view_label_retrieval_job.puml")
31
+ ContainerDb($SAMPLE_LABELS_ID, $SAMPLE_LABELS_TABLE, $SAMPLE_LABELS_TYPE, $SAMPLE_LABELS_COMPONENT_DESC)
32
+ ContainerDb($DAILY_COLLECTED_SAMPLES_ID, $DAILY_COLLECTED_SAMPLES_TABLE, $DAILY_COLLECTED_SAMPLES_TYPE, $DAILY_COLLECTED_SAMPLES_DESC)
33
+ ContainerQueue(sampleQueue, "SampleQueueFoo", "SQS", "Samples which should be double reviewed")
34
+ Container(labelerBApi, "ReviewQueueAPI", "NodeJS", "API for pulling samples for review", $sprite="server")
35
+ Container(btBqSnapshotJob, "ExportBigQuerySnapshot", "some-etl-framework", "A job to take daily BQ snapshots\nof //$SAMPLE_LABELS_TABLE//", $tags="primary", $link="./level_4_activity_diagram_export_bq_snapshot_job.puml")
36
+ }
37
+ System(modelTrainingInference, "Model Training + Inference\n\n<font color=#e0575e><$RoboMaker></font>\n", "")
38
+
39
+ Rel_R(samplingPipeline, $DAILY_COLLECTED_SAMPLES_ID, " ")
40
+ Rel_D(samplingPipeline, sampleQueue, " ")
41
+ BiRel_D($SAMPLE_LABELS_ID, samplingPipeline, " ")
42
+ Rel_L(labelRetrievalJob, $SAMPLE_LABELS_ID, " ")
43
+ Rel_L($LABELED_DATASOURCE_ID, labelRetrievalJob, " ")
44
+ Rel_R(sampleQueue, labelerBApi, " ")
45
+ Rel_R(labelerBApi, labelerB, " ")
46
+ Rel_R(labelerB, $LABELED_DATASOURCE_ID, " ")
47
+ Rel_L(modelScoreDb, samplingPipeline, " ")
48
+ Rel_D(modelTrainingInference, modelScoreDb, " ")
49
+ Rel_D(bigQueryLabelDB, modelTrainingInference, " ")
50
+ Rel_L(labelerA, bigQueryLabelDB, " ")
51
+ Rel_L($SAMPLE_LABELS_ID, modelTrainingInference, " ")
52
+ Rel_D($SAMPLE_LABELS_ID, btBqSnapshotJob, " ")
53
+
54
+ HIDE_STEREOTYPE()
55
+ @enduml
@@ -0,0 +1,40 @@
1
+ @startuml
2
+ !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml
3
+ !include ./c4_styles.puml
4
+ !include ./constants.puml
5
+
6
+ LAYOUT_LANDSCAPE()
7
+
8
+ left header
9
+
10
+ \t<font size=22 color=black>Some Job - Component View</font>
11
+
12
+
13
+
14
+
15
+ end header
16
+
17
+
18
+ Component(scheduler, "Scheduler", "some-etl-framework", "Kicks off the job at some interval")
19
+ Component(extractTransform, "DatasourceExtractTransform", "Operator", "Pull data from labeler datasource and perform any transforms before storing the data")
20
+ Component(loader, "DatasourceLoader", "Operator", "Update //$SAMPLE_LABELS_TABLE// with new labels")
21
+ Component(someMicroService, "SomeMicroService", "SubDag", "Foo bar baz")
22
+ Component(performCalculations, "PerformCalculation", "Operator", "Trigger the calculation of xyz")
23
+ Component(calculatePrevalence, "CalculatePrevalence", "Operator", "Trigger the calculation prevalence and store it")
24
+ ComponentDb($SAMPLE_LABELS_ID, $SAMPLE_LABELS_TABLE, $SAMPLE_LABELS_TYPE, $SAMPLE_LABELS_COMPONENT_DESC)
25
+
26
+ Rel_R(scheduler, extractTransform, " ")
27
+ Rel_R(extractTransform, loader, " ")
28
+ Rel_R(loader, performCalculations, "When all labels retrieved")
29
+ Rel_U(loader, $SAMPLE_LABELS_ID, " ")
30
+ Rel_D(loader, "someMicroService", " ")
31
+ Rel_R(performCalculations, calculatePrevalence, " ")
32
+
33
+ center footer
34
+
35
+
36
+
37
+ end footer
38
+
39
+ HIDE_STEREOTYPE()
40
+ @enduml
@@ -0,0 +1,58 @@
1
+ @startuml
2
+ !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml
3
+ !include ./c4_styles.puml
4
+ !include ./constants.puml
5
+
6
+ LAYOUT_LANDSCAPE()
7
+
8
+ left header
9
+
10
+ \t<font size=22 color=black>The Pipeline - Component View</font>
11
+
12
+ \t<font size=14 color=black><U+25CF> Goal 1</font>
13
+ \t<font size=14 color=black><U+25CF> Goal 2</font>
14
+ \t<font size=14 color=black><U+25CF> Goal 3</font>
15
+ \t<font size=14 color=black><U+25CF> Goal 4</font>
16
+
17
+
18
+
19
+
20
+
21
+ end header
22
+
23
+
24
+ Person(labelerB, "Labeler B", " Labeler B Description \n", $tags="a")
25
+ ContainerDb($LABELED_DATASOURCE_ID, "$LABELED_DATASOURCE_NAME", "$LABELED_DATASOURCE_TYPE", $LABELED_DATASOURCE_DESC)
26
+ Boundary(pipeline, " \nEntire Pipeline Infrastructure\n") {
27
+ ComponentDb($SAMPLE_LABELS_ID, $SAMPLE_LABELS_TABLE, $SAMPLE_LABELS_TYPE, $SAMPLE_LABELS_COMPONENT_DESC)
28
+ Boundary(pipelineDag, "Pipeline DAG", $tags="highlightBoundary") {
29
+ Component(scheduler, "Scheduler", "some-etl-framework", "Kicks off pipeline at some interval")
30
+ Component(waitForDependentTables, "Wait For Dependent Tables", "worker", "Waits for dependent tables to be populated")
31
+ Component(prevalenceSampler, "Label Type A Sampler", "worker", "Queries for type A labels", $tags="primary", $link="./level_4_activity_diagram_sampler_a.puml")
32
+ Component(trainingSampler, "Label Type B Sampler", "worker", "Receives all sampling labels type B", $tags="primary", $link="./level_4_activity_diagram_sampler_b.puml")
33
+ Component(sendSamplesForLabeling, "SendSamplesForLabeling", "worker", "Send samples for labeling")
34
+ }
35
+ Container(reviewQueueApi, "ReviewQueueAPI", "NodeJS", "An API used for pulling labels", $sprite="server")
36
+ ComponentDb($DAILY_COLLECTED_SAMPLES_ID, $DAILY_COLLECTED_SAMPLES_TABLE, $DAILY_COLLECTED_SAMPLES_TYPE, $DAILY_COLLECTED_SAMPLES_DESC)
37
+ ComponentQueue(samplesForLabelingQueue, "SamplesForLabelingQueue", "SQS", "Queue for double review")
38
+ }
39
+ ContainerDb(modelScoreDb, "ModelScoreDB", "BigQuery", "A database where the inference score is stored")
40
+
41
+
42
+
43
+ Rel_R(scheduler, waitForDependentTables, " ")
44
+ Rel_R(waitForDependentTables, prevalenceSampler, "Query used to create table to sample from")
45
+ Rel_R(prevalenceSampler, trainingSampler, " ")
46
+ Rel_U(modelScoreDb, prevalenceSampler, " ")
47
+ Rel_D($SAMPLE_LABELS_ID, prevalenceSampler, "Check what samples have already been sent for labeling**\n** ")
48
+ Rel_R(trainingSampler, sendSamplesForLabeling, " ")
49
+ Rel_D(sendSamplesForLabeling, $SAMPLE_LABELS_ID, " ")
50
+ Rel_R(sendSamplesForLabeling, samplesForLabelingQueue, " ")
51
+ Rel_U(trainingSampler, $DAILY_COLLECTED_SAMPLES_ID, " ")
52
+ Rel_U(prevalenceSampler, $DAILY_COLLECTED_SAMPLES_ID, " ")
53
+ Rel_R(samplesForLabelingQueue, reviewQueueApi, " ")
54
+ Rel_R(reviewQueueApi, labelerB, " ")
55
+ Rel_U(labelerB, $LABELED_DATASOURCE_ID, " ")
56
+
57
+ HIDE_STEREOTYPE()
58
+ @enduml
@@ -0,0 +1,13 @@
1
+ @startuml
2
+
3
+ !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml
4
+ !include ./c4_styles.puml
5
+
6
+ center header
7
+
8
+
9
+ \t<font size=30 color=black>Under construction... </font>
10
+
11
+
12
+ end header
13
+ @enduml
@@ -0,0 +1,79 @@
1
+ @startuml
2
+ !include ./styles.puml
3
+ !include ./constants.puml
4
+
5
+ center header
6
+
7
+ \t<font size=22 color=black>Sampler A - Activity Diagram</font>
8
+
9
+
10
+ end header
11
+
12
+ start
13
+ #E4D4EF: <font size=14>Inputs</font>
14
+ ----
15
+
16
+ *<U+00A0>""sample_padding_factor"": Description of // sample_padding_factor //
17
+
18
+ *<U+00A0>""sample_strategies"": Description of // sample_strategies //
19
+
20
+ *<U+00A0>""$TYPE_X_SAMPLE_POOL_TABLE"": $SAMPLE_POOL_TABLE_DESC()
21
+ ]
22
+ -> \n;
23
+
24
+ partition "**operator** OperatorABC " {
25
+ :**REMOVE** samples of type x from ""$TYPE_X_SAMPLE_POOL_TABLE"" //i.e.//
26
+
27
+ ""SELECT * FROM $TYPE_X_SAMPLE_POOL_TABLE EXCEPT DISTINCT (SELECT * FROM $DAILY_COLLECTED_SAMPLES_TABLE)"";
28
+ while(""**FOR EACH:** sampling_strategy"")
29
+ #E5E5E5: Create sub-query table ]
30
+ #E5E5E5: ""LIMIT"" = ""sampling_strategy.sample_size * sample_padding_factor""]
31
+ #E5E5E5: ""SELECT sampling_strategy.name AS strategy"" (to add strategy column)]
32
+ #LightBlue: <size: 11>**NEXT:** ""sampling_strategy"">
33
+ endwhile;
34
+ : ""UNION ALL"" strategy tables;
35
+ : **DEDUPLICATE** samples by adding the """strategies""" column. For each duplicate add the strategy to the
36
+ ""strategies"" column in the distinct row. //i.e.//
37
+
38
+ ""SELECT ARRAY_AGG(strategy) as strategies... FROM all_strategies_table GROUP BY uid"";
39
+ : **SAVE** subquery to ""distinct_type_x_sample_pool"" table;
40
+ }
41
+ -> \n;
42
+ -> \n;
43
+ #E4D4EF: <font size=14>Outputs</font>
44
+ ----
45
+
46
+ *<U+00A0>""distinct_type_x_sample_pool"": Description of //distinct_type_x_sample_pool// output
47
+
48
+ ]
49
+ -> \n;
50
+ -> \n;
51
+ partition "**operator** OperatorDEF\n<font size=10>Note: [[https://google.com More details on algorithm here]]</font> " {
52
+ :**SET** variable... ""extra_space **=** COUNT(*) of $DAILY_COLLECTED_SAMPLES_TABLE WHERE $SEND_FOR_LABELING_COLUMN = TRUE"";
53
+ #LightBlue:while(""**FOR EACH:** sampling_strategy"")
54
+ #E5E5E5: ""samples_for_strategy = SELECT * WHERE sampling_strategy.name IN strategies ORDER BY len(strategies) ASC""]
55
+
56
+ #E5E5E5: **""WHILE len(samples_for_strategy) > sampling_strategy.sample_size:""**
57
+
58
+ ****<U+00A0> bla bla bla
59
+ ****<U+00A0> code code code;
60
+
61
+ #E5E5E5: **""FOR sample IN samples_for_strategy""**
62
+
63
+ ****<U+00A0> 1234
64
+ ****<U+00A0> 5678]
65
+ #E5E5E5: **IS** ""len(strategy_samples)"" **less than** ""strategy.sample_size"" **?**]
66
+ split
67
+ <<true>>: **Yes** ]
68
+ <<true>>: ""extra_space += sampling_strategy.sample_size - len(strategy_samples)""]
69
+ split again
70
+ #FFFFFF: **No** ]
71
+ #FFFFFF: \t\t\t\t\t\t Do nothing \t\t\t\t\t\t]
72
+ end split
73
+ #LightBlue: <size: 11>**NEXT:** ""sampling_strategy"">
74
+ endwhile;
75
+ : **REALLOCATE** ""extra_space"" to increase the sample sizes of strategies whose size was reduced;
76
+ : **WRITE** output to ""$SAMPLE_LABELS_TABLE"" table;
77
+ }
78
+ stop
79
+ @enduml
@@ -0,0 +1,32 @@
1
+ @startuml
2
+ !include ./styles.puml
3
+ !include ./constants.puml
4
+
5
+ left header
6
+
7
+ \t<font size=22 color=black>Sampler B - Activity Diagram</font>
8
+
9
+ \t<font size=14 color=black>Description of goal </font>
10
+
11
+
12
+ end header
13
+
14
+ start
15
+ #E4D4EF: <font size=14>Inputs</font>
16
+ ----
17
+
18
+ *<U+00A0>""$TYPE_Y_SAMPLE_POOL_TABLE"": These are samples of type y
19
+
20
+ *<U+00A0>""sample_size"": How many samples to collect
21
+ ]
22
+ -> \n;
23
+
24
+ partition "**operator** OperatorFooBar " {
25
+ : **LIMIT** ""$TYPE_Y_SAMPLE_POOL_TABLE"" to ""sample_size"";
26
+ : **SET** ""field_a = 'a'"" on all samples;
27
+ :\t **SET** ""$SEND_FOR_LABELING_COLUMN = (sample IN $SAMPLE_LABELS_TABLE)"" \t;
28
+ :**WRITE** result to $DAILY_COLLECTED_SAMPLES_TABLE table;
29
+ }
30
+
31
+ stop
32
+ @enduml
@@ -0,0 +1,106 @@
1
+ @startuml
2
+ !include ./constants.puml
3
+
4
+ hide circle
5
+ skinparam linetype ortho
6
+ skinparam wrapWidth 350
7
+
8
+ left header
9
+
10
+ \t<font size=22 color=black>Entity Relation Diagram</font>
11
+
12
+
13
+ end header
14
+
15
+ !global $TABLE_1 = 'table 1'
16
+ !global $TABLE_1_TYPE = 'BigQuery'
17
+ !global $TABLE_1_DESC = 'Description for table 1'
18
+
19
+ !global $TABLE_2 = 'table 2'
20
+ !global $TABLE_2_TYPE = 'BigQuery'
21
+ !global $TABLE_2_DESC = 'Description for table 2'
22
+
23
+ !global $TABLE_3 = 'table 3'
24
+ !global $TABLE_3_TYPE = 'BigQuery'
25
+ !global $TABLE_3_DESC = 'Description for table 3'
26
+
27
+ !global $TABLE_4 = 'table 4'
28
+ !global $TABLE_4_TYPE = 'BigQuery'
29
+ !global $TABLE_4_DESC = 'Description for table 4'
30
+
31
+ entity "**$TABLE_1**" {
32
+ **$TABLE_1_TYPE**
33
+
34
+ $TABLE_1_DESC
35
+ ..
36
+ + ""id"": //string //
37
+ --
38
+ * ""some_id"": //string //: Description for some_id
39
+ * ""some_id_2"": //string// : Description for some_id_2
40
+ * ""config"": // JSON //: Description for config
41
+ }
42
+
43
+ entity "**$TABLE_2**" {
44
+ **$TABLE_2_TYPE**
45
+
46
+ $TABLE_2_DESC
47
+ ..
48
+ + ""id_1"": //string //
49
+ + ""id_2"": //string //
50
+ --
51
+ * ""label"": //string //: Description for label
52
+ * ""scheduled_at"": //timestamp //: Description for scheduled_at
53
+ * ""foo_bar_ts"": //timestamp //: Description for foo_bar_ts
54
+ * ""baz_ts"": //timestamp //: Description for baz_ts
55
+ * ""abc_ts"": //timestamp //: Description for abc_ts
56
+ }
57
+
58
+ entity "**$TABLE_3**" {
59
+ **$TABLE_3_TYPE**
60
+
61
+ $TABLE_3_DESC
62
+ ..
63
+ + ""id"": //string //
64
+ --
65
+ * ""thingy_id"": //string //: Description for thingy_id
66
+ * ""config"": // JSON //: Configuration used
67
+ }
68
+
69
+ entity "**$DAILY_COLLECTED_SAMPLES_TABLE**" {
70
+ **$DAILY_COLLECTED_SAMPLES_TYPE**
71
+
72
+ $DAILY_COLLECTED_SAMPLES_DESC
73
+ ..
74
+ + ""$CONTENT_ID_COLUMN"": //string //
75
+ --
76
+ *""strategy"": //string //: Description of strategy
77
+ *""$SEND_FOR_LABELING_COLUMN"": //boolean //: Description of send for labeling
78
+ ..
79
+ Metadata Columns
80
+ *""model_score"": //number //
81
+ *""num_views"": //number //
82
+ *""is_awesome"": //boolean //
83
+ .
84
+ .
85
+ .
86
+ }
87
+
88
+ entity "**$TABLE_4**" {
89
+ **$TABLE_4_TYPE**
90
+
91
+ $TABLE_4_DESC
92
+ ..
93
+ + ""id"": //string//
94
+ --
95
+ *""name"": //string //: Description of name
96
+ *""value"": //string //: Description of value
97
+ *""labeler_id"": //string //: Description of labeler_id
98
+ *""source"": //string //: Description of source
99
+ *""some_other_field"": //string //: Description of some_other_field
100
+ }
101
+
102
+ "**$TABLE_1**" }o-- "**$TABLE_2**"
103
+ "**$TABLE_2**" }o-- "**$TABLE_3**"
104
+ "**$TABLE_2**" }o-- "**$TABLE_4**"
105
+
106
+ @enduml
@@ -0,0 +1,22 @@
1
+ @startuml
2
+ <style>
3
+ activityDiagram {
4
+ diamond {
5
+ BackgroundColor LightBlue
6
+ }
7
+
8
+ .input {
9
+ MaximumWidth 500
10
+ BackgroundColor #E4D4EF
11
+ }
12
+ }
13
+ * {
14
+ }
15
+
16
+ .true {
17
+ FontColor White
18
+ BackgroundColor Black
19
+ }
20
+
21
+ </style>
22
+ @enduml