[New Feature] Add web folder to support web documentation

This commit is contained in:
Rudy Haryanto
2022-10-04 20:17:17 +07:00
parent c27f624ca8
commit a54aa04c9b
251 changed files with 11079 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
<h1 align="center">Remark SnackPlayer</h1>
<p align="center">Remark plugin to embed <a href="https://snack.expo.dev/">Expo Snack's</a> using Code Blocks</p>
## Usage
This plugin parses codeblocks with language set as `SnackPlayer` and replaces them with embedded Expo's SnackPlayers, you can also provide parameters along with the codeblock to set some basic details.
### Example Code Block
````
```SnackPlayer name=Hello%20World description=This%20is%20a%20description
import React from 'react';
import { Text, View } from 'react-native';
const YourApp = () => {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Try editing me! 🎉</Text>
</View>
);
}
export default YourApp;
```
````
The above code snippet would look like this:
<img width="783" alt="Screenshot 2020-10-03 at 1 11 19 AM" src="https://user-images.githubusercontent.com/11258286/94963203-67de3500-0515-11eb-974a-a2289c0bfdc8.png">
### Parameters
| Name | Description | Default |
| ------------------ | --------------------------------------------------------- | ------------------- |
| name | SnackPlayer name | `"Example"` |
| description | Description of the example | `"Example usage"` |
| dependencies | Additional dependencies, eg. `"expo-constant"` | `""` |
| platform | Example platform | `"web"` |
| supportedPlatforms | Supported platforms | `"ios,android,web"` |
| theme | SnackPlayer theme, `"light"` or `"dark"` | `"light"` |
| preview | Preview visible, `"true"` or `"false"` | `"true"` |
| loading | iFrame loading attribute, `"auto"`, `"lazy"` or `"eager"` | `"lazy"` |
## Styling
To style the Snack Player wrapper you can use `.snack-player` class.

View File

@@ -0,0 +1,31 @@
{
"name": "@react-native-website/remark-snackplayer",
"version": "0.0.7",
"private": true,
"description": "Remark Expo Snack Plugin",
"main": "src/index.js",
"keywords": [
"remark",
"react-native",
"expo",
"snackplayer",
"preview"
],
"files": [
"src"
],
"scripts": {
"prettier": "prettier --write \"{src/**/*.js,tests/**/*.js,*.md}\"",
"test": "yarn tape tests/index.js"
},
"dependencies": {
"dedent": "^0.7.0",
"object.fromentries": "^2.0.3",
"unist-builder": "^2.0.3",
"unist-util-visit-parents": "^3.1.1"
},
"devDependencies": {
"remark": "^12.0.1",
"tape": "^5.0.1"
}
}

View File

@@ -0,0 +1,82 @@
'use strict';
const visit = require('unist-util-visit-parents');
const u = require('unist-builder');
const dedent = require('dedent');
const fromEntries = require('object.fromentries');
const parseParams = (paramString = '') => {
const params = fromEntries(new URLSearchParams(paramString));
if (!params.platform) {
params.platform = 'web';
}
return params;
};
const processNode = (node, parent) => {
return new Promise(async (resolve, reject) => {
try {
const params = parseParams(node.meta);
// Gather necessary Params
const name = params.name ? decodeURIComponent(params.name) : 'Example';
const description = params.description
? decodeURIComponent(params.description)
: 'Example usage';
const sampleCode = node.value;
const encodedSampleCode = encodeURIComponent(sampleCode);
const dependencies = params.dependencies || '';
const platform = params.platform || 'web';
const supportedPlatforms = params.supportedPlatforms || 'ios,android,web';
const theme = params.theme || 'light';
const preview = params.preview || 'true';
const loading = params.loading || 'lazy';
// Generate Node for SnackPlayer
// See https://github.com/expo/snack/blob/main/docs/embedding-snacks.md
const snackPlayerDiv = u('html', {
value: dedent`
<div
class="snack-player"
data-snack-name="${name}"
data-snack-description="${description}"
data-snack-code="${encodedSampleCode}"
data-snack-dependencies="${dependencies}"
data-snack-platform="${platform}"
data-snack-supported-platforms="${supportedPlatforms}"
data-snack-theme="${theme}"
data-snack-preview="${preview}"
data-snack-loading="${loading}"
></div>
`,
});
// Replace code block with SnackPlayer Node
const index = parent[0].children.indexOf(node);
parent[0].children.splice(index, 1, snackPlayerDiv);
} catch (e) {
return reject(e);
}
resolve();
});
};
const SnackPlayer = () => {
return tree =>
new Promise(async (resolve, reject) => {
const nodesToProcess = [];
// Parse all CodeBlocks
visit(tree, 'code', (node, parent) => {
// Add SnackPlayer CodeBlocks to processing queue
if (node.lang == 'SnackPlayer') {
nodesToProcess.push(processNode(node, parent));
}
});
// Wait for all promises to be resolved
Promise.all(nodesToProcess).then(resolve()).catch(reject());
});
};
module.exports = SnackPlayer;

View File

@@ -0,0 +1,30 @@
const path = require('path');
const fs = require('fs');
const test = require('tape');
const remark = require('remark');
const snackplayer = require('../');
const read = name => fs.readFileSync(path.join(__dirname, name), 'utf8');
const normalizeLineEndings = str => str.replace(/\r\n/g, '\n');
test('remark-snackplayer', async t => {
const processor = remark().use(snackplayer);
processor.process(read('markdown/test1.md'), (err, file) => {
if (err) t.fail('Failed to process markdown/test1.md');
t.equal(
normalizeLineEndings(String(file)),
normalizeLineEndings(read('output/output1.html')),
'With 1 Code Block'
);
});
processor.process(read('markdown/test2.md'), (err, file) => {
if (err) t.fail('Failed to process markdown/test2.md');
t.equal(
normalizeLineEndings(String(file)),
normalizeLineEndings(read('output/output2.html')),
'With 2 Code Blocks'
);
});
});

View File

@@ -0,0 +1,16 @@
```SnackPlayer name=Hello%20World
import React from 'react';
import { Text, View } from 'react-native';
const YourApp = () => {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>
Try editing me! 🎉
</Text>
</View>
);
}
export default YourApp;
```

View File

@@ -0,0 +1,38 @@
```SnackPlayer name=FirstPlayer
import React from 'react';
import { Text, View } from 'react-native';
const YourApp = () => {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>
Try editing me! 🎉
</Text>
</View>
);
}
export default YourApp;
```
```SnackPlayer name=SecondPlayer&theme=dark&preview=false&supportedPlatforms=ios&loading=eager&dependencies=@react-native-community/slider
import React from 'react';
import { View } from 'react-native';
import Slider from '@react-native-community/slider';
const YourApp = () => {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Slider
style={{width: 200, height: 40}}
minimumValue={0}
maximumValue={1}
minimumTrackTintColor="#FFFFFF"
maximumTrackTintColor="#000000"
/>
</View>
);
}
export default YourApp;
```

View File

@@ -0,0 +1,12 @@
<div
class="snack-player"
data-snack-name="Hello World"
data-snack-description="Example usage"
data-snack-code="import%20React%20from%20'react'%3B%0Aimport%20%7B%20Text%2C%20View%20%7D%20from%20'react-native'%3B%0A%0Aconst%20YourApp%20%3D%20()%20%3D%3E%20%7B%0A%20%20%20%20return%20(%0A%20%20%20%20%3CView%20style%3D%7B%7B%20flex%3A%201%2C%20justifyContent%3A%20%22center%22%2C%20alignItems%3A%20%22center%22%20%7D%7D%3E%0A%20%20%20%20%20%20%20%20%3CText%3E%0A%20%20%20%20%20%20%20%20Try%20editing%20me!%20%F0%9F%8E%89%0A%20%20%20%20%20%20%20%20%3C%2FText%3E%0A%20%20%20%20%3C%2FView%3E%0A%20%20%20%20)%3B%0A%7D%0A%0Aexport%20default%20YourApp%3B"
data-snack-dependencies=""
data-snack-platform="web"
data-snack-supported-platforms="ios,android,web"
data-snack-theme="light"
data-snack-preview="true"
data-snack-loading="lazy"
></div>

View File

@@ -0,0 +1,25 @@
<div
class="snack-player"
data-snack-name="FirstPlayer"
data-snack-description="Example usage"
data-snack-code="import%20React%20from%20'react'%3B%0Aimport%20%7B%20Text%2C%20View%20%7D%20from%20'react-native'%3B%0A%0Aconst%20YourApp%20%3D%20()%20%3D%3E%20%7B%0A%20%20%20%20return%20(%0A%20%20%20%20%3CView%20style%3D%7B%7B%20flex%3A%201%2C%20justifyContent%3A%20%22center%22%2C%20alignItems%3A%20%22center%22%20%7D%7D%3E%0A%20%20%20%20%20%20%20%20%3CText%3E%0A%20%20%20%20%20%20%20%20Try%20editing%20me!%20%F0%9F%8E%89%0A%20%20%20%20%20%20%20%20%3C%2FText%3E%0A%20%20%20%20%3C%2FView%3E%0A%20%20%20%20)%3B%0A%7D%0A%0Aexport%20default%20YourApp%3B"
data-snack-dependencies=""
data-snack-platform="web"
data-snack-supported-platforms="ios,android,web"
data-snack-theme="light"
data-snack-preview="true"
data-snack-loading="lazy"
></div>
<div
class="snack-player"
data-snack-name="SecondPlayer"
data-snack-description="Example usage"
data-snack-code="import%20React%20from%20'react'%3B%0Aimport%20%7B%20View%20%7D%20from%20'react-native'%3B%0Aimport%20Slider%20from%20'%40react-native-community%2Fslider'%3B%0A%0Aconst%20YourApp%20%3D%20()%20%3D%3E%20%7B%0A%20%20%20%20return%20(%0A%20%20%20%20%3CView%20style%3D%7B%7B%20flex%3A%201%2C%20justifyContent%3A%20%22center%22%2C%20alignItems%3A%20%22center%22%20%7D%7D%3E%0A%20%20%20%20%20%20%20%20%3CSlider%0A%20%20%20%20%20%20%20%20%20%20style%3D%7B%7Bwidth%3A%20200%2C%20height%3A%2040%7D%7D%0A%20%20%20%20%20%20%20%20%20%20minimumValue%3D%7B0%7D%0A%20%20%20%20%20%20%20%20%20%20maximumValue%3D%7B1%7D%0A%20%20%20%20%20%20%20%20%20%20minimumTrackTintColor%3D%22%23FFFFFF%22%0A%20%20%20%20%20%20%20%20%20%20maximumTrackTintColor%3D%22%23000000%22%0A%20%20%20%20%20%20%20%20%2F%3E%0A%20%20%20%20%3C%2FView%3E%0A%20%20%20%20)%3B%0A%7D%0A%0Aexport%20default%20YourApp%3B"
data-snack-dependencies="@react-native-community/slider"
data-snack-platform="web"
data-snack-supported-platforms="ios"
data-snack-theme="dark"
data-snack-preview="false"
data-snack-loading="eager"
></div>

View File

@@ -0,0 +1,76 @@
/**
* Copyright (c) AiCure, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
'use strict';
const fs = require('fs-extra');
const glob = require('glob');
const path = require('path');
const reactDocs = require('@motiz88/react-native-docgen');
const GENERATE_ANNOTATION = '@' + 'generate-docs';
module.exports = extractDocsFromRN;
async function extractDocsFromRN(rnRoot) {
// TODO: make implementation async
const allComponentFiles = glob.sync(
path.join(rnRoot, '/Libraries/{Components,Image,}/**/*.js'),
{
nodir: true,
absolute: true,
}
);
const docs = [];
for (const file of allComponentFiles) {
const contents = fs.readFileSync(file, {encoding: 'utf-8'});
if (!contents.includes(GENERATE_ANNOTATION)) {
continue;
}
console.log(file);
const result = reactDocs.parse(
contents,
reactDocs.resolver.findAllComponentDefinitions,
reactDocs.defaultHandlers.filter(
handler => handler !== reactDocs.handlers.propTypeCompositionHandler
),
{filename: file}
);
const filteredResult = result.filter(item => {
if (item.description) return item;
});
docs.push({
file,
component: cleanComponentResult(...filteredResult),
});
}
// Make sure output is JSON-safe
const docsSerialized = JSON.parse(JSON.stringify(docs));
await fs.writeFile(
path.join(__dirname, 'extracted.json'),
JSON.stringify(docsSerialized, null, 2),
'utf8'
);
return docsSerialized;
}
function cleanComponentResult(component) {
return {
...component,
methods: component.methods.filter(method => !method.name.startsWith('_')),
};
}

View File

@@ -0,0 +1,326 @@
/**
* Copyright (c) AiCure, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const tokenizeComment = require('tokenize-comment');
const {formatTypeColumn, formatDefaultColumn} = require('./propFormatter');
const {
formatMethodType,
formatMethodName,
formatMethodDescription,
} = require('./methodFormatter');
const {
formatMultiplePlatform,
stringToInlineCodeForTable,
maybeLinkifyType,
maybeLinkifyTypeName,
formatType,
} = require('./utils');
// Formats an array of rows as a Markdown table
function generateTable(rows) {
const colWidths = new Map();
for (const row of rows) {
for (const col of Object.keys(row)) {
colWidths.set(
col,
Math.max(colWidths.get(col) || col.length, String(row[col]).length)
);
}
}
if (!colWidths.size) {
return '';
}
let header = '|',
divider = '|';
for (const [col, width] of colWidths) {
header += ' ' + col.padEnd(width + 1) + '|';
divider += ' ' + '-'.repeat(width) + ' ' + '|';
}
let result = header + '\n' + divider + '\n';
for (const row of rows) {
result += '|';
for (const [col, width] of colWidths) {
result += ' ' + String(row[col] || '').padEnd(width + 1) + '|';
}
result += '\n';
}
return result;
}
// Formats information about a prop
function generateProp(propName, prop) {
const infoTable = generateTable([
{
Type: formatTypeColumn(prop),
...formatDefaultColumn(prop),
},
]);
return (
'### ' +
(prop.required ? '<div class="label required basic">Required</div>' : '') +
'`' +
propName +
'`' +
(prop.rnTags && prop.rnTags.platform
? formatMultiplePlatform(prop.rnTags.platform)
: '') +
'\n' +
'\n' +
(prop.description ? prop.description + '\n\n' : '') +
infoTable
);
}
// Formats information about a prop
function generateMethod(method, component) {
let descriptionTokenized = '';
let header = 'Valid `params` keys are:';
let mdPoints = '';
if (method?.params[0]?.type?.raw) {
let desc = method?.params[0]?.type?.raw;
let len = method?.params[0]?.type?.signature?.properties?.length;
descriptionTokenized = tokenizeComment(desc);
if (
descriptionTokenized?.examples &&
descriptionTokenized?.examples.length === len
) {
let obj = [];
for (let i = 0; i < len; i++) {
let newObj = method?.params[0]?.type?.signature?.properties[i];
newObj['description'] = descriptionTokenized?.examples[i]?.value;
obj.push(newObj);
}
obj.map(item => {
if (item.description.trim() !== 'missing')
mdPoints += `- '${item.key}' (${item.value.name}) - ${item.description}`;
else mdPoints += `- '${item.key}' (${item.value.name})`;
});
}
}
if (method?.docblock) {
let dblock = method.docblock
.split('\n')
.map(line => {
return line.replace(/ /, '');
})
.join('\n');
const docblockTokenized = tokenizeComment(dblock);
dblock = dblock.replace(/@platform .*/g, '');
method.rnTags = {};
const platformTag = docblockTokenized.tags.find(
({key}) => key === 'platform'
);
if (platformTag) {
method.rnTags.platform = platformTag.value.split(',');
}
}
return (
'### `' +
method.name +
'()`' +
(method.rnTags && method.rnTags.platform
? formatMultiplePlatform(method.rnTags.platform)
: '') +
'\n' +
'\n' +
(method.description ? method.description + '\n\n' : '') +
generateMethodSignatureTable(method, component) +
(mdPoints && header + '\n' + mdPoints)
).trim();
}
function lowerFirst(s) {
return s[0].toLowerCase() + s.slice(1);
}
function generateMethodSignatureBlock(method, component) {
return (
'```jsx\n' +
(method.modifiers.includes('static')
? component.displayName + '.'
: lowerFirst(component.displayName + '.')) +
method.name +
'(' +
method.params
.map(param => (param.optional ? `[${param.name}]` : param.name))
.join(', ') +
');' +
'\n' +
'```\n\n'
);
}
function generateMethodSignatureTable(method, component) {
if (!method.params.length) {
return '';
}
return (
'**Parameters:**\n\n' +
generateTable(
method.params.map(param => {
return {
Name: formatMethodName(param),
Type: formatMethodType(param),
Required: param.optional ? 'No' : 'Yes',
...(param.description && {
Description: formatMethodDescription(param),
}),
};
})
)
);
}
// Formats information about props
function generateProps({props, composes}) {
if (!props || !Object.keys(props).length) {
return '';
}
return (
'## Props' +
'\n' +
'\n' +
(composes && composes.length
? composes
.map(parent => 'Inherits ' + maybeLinkifyTypeName(parent) + '.')
.join('\n\n') + '\n\n'
: '') +
Object.keys(props)
.sort((a, b) => a.localeCompare(b))
.sort((a, b) => props[b].required - props[a].required)
.map(function (propName) {
return generateProp(propName, props[propName]);
})
.join('\n\n---\n\n')
);
}
function generateMethods(component) {
const {methods} = component;
if (!methods || !methods.length) {
return '';
}
return (
'## Methods' +
'\n' +
'\n' +
[...methods]
.sort((a, b) =>
a.name.localeCompare(
b.name /* TODO @nocommit what's a neutral locale */
)
)
.map(function (method) {
return generateMethod(method, component);
})
.join('\n\n---\n\n')
);
}
// Generates a Docusaurus header for a component page
function generateHeader({id, title}) {
return (
'---' + '\n' + 'id: ' + id + '\n' + 'title: ' + title + '\n' + '---' + '\n'
);
}
// Function to process example contained description
function preprocessDescription(desc) {
// Playground tabs for the class and functional components
const playgroundTab = `<div class="toggler">
<ul role="tablist" class="toggle-syntax">
<li id="functional" class="button-functional" aria-selected="false" role="tab" tabindex="0" aria-controls="functionaltab" onclick="displayTabs('syntax', 'functional')">
Function Component Example
</li>
<li id="classical" class="button-classical" aria-selected="false" role="tab" tabindex="0" aria-controls="classicaltab" onclick="displayTabs('syntax', 'classical')">
Class Component Example
</li>
</ul>
</div>`;
//Blocks for different syntax sections
const functionalBlock = `<block class='functional syntax' />`;
const classBlock = `<block class='classical syntax' />`;
const endBlock = `<block class='endBlock syntax' />`;
desc = desc
.split('\n')
.map(line => {
return line.replace(/ /, '');
})
.join('\n');
const descriptionTokenized = tokenizeComment(desc);
// Tabs counter for examples
let tabs = 0;
descriptionTokenized.examples.map(item => {
const matchSnackPlayer = item.language.match(/(SnackPlayer name=).*/g);
if (matchSnackPlayer) {
const matchClassComp = matchSnackPlayer[0].match(
/Class%20Component%20Example/
);
const matchFuncComp = matchSnackPlayer[0].match(
/Function%20Component%20Example/
);
if (matchClassComp || matchFuncComp) tabs++;
}
});
if (tabs === 2) {
const firstExample = desc.slice(desc.search('```SnackPlayer') + 1);
const secondExample = firstExample.slice(
firstExample.search('```SnackPlayer') + 1
);
return (
desc.substring(0, desc.search('```SnackPlayer')) +
`\n## Example\n` +
`${playgroundTab}\n\n${functionalBlock}\n\n${
'`' + firstExample.slice(0, firstExample.search('```') + 3)
}\n\n${classBlock}\n\n${
'`' + secondExample.slice(0, secondExample.search('```') + 3)
}\n\n${endBlock}` +
secondExample.slice(secondExample.search('```') + 3)
);
} else {
if (desc.search('```SnackPlayer') !== -1) {
return (
desc.slice(0, desc.search('```SnackPlayer')) +
'\n' +
'\n## Example\n' +
'\n' +
desc.slice(desc.search('```SnackPlayer'))
);
} else return desc;
}
}
function generateMarkdown({id, title}, component) {
const markdownString =
generateHeader({id, title}) +
'\n' +
preprocessDescription(component.description) +
'\n\n' +
'---\n\n' +
'# Reference\n\n' +
generateProps(component) +
generateMethods(component);
return markdownString.replace(/\n{3,}/g, '\n\n');
}
module.exports = generateMarkdown;

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) AiCure, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// Hard-coded knowledge about the OpenDBM codebase and how to document it,
// beyond what is explicitly encoded in the react-docgen artifact
// (generatedComponentApiDocs.js)
// Ideally this file should go away.
module.exports = {
linkableTypeAliases: {
NativeColorValue: {
text: 'color',
url: 'colors.md',
},
ViewProps: {
text: 'View Props',
url: 'view.md#props',
},
PressEvent: {
text: 'PressEvent',
url: 'pressevent.md',
},
'RefreshLayoutConsts.SIZE.DEFAULT': {
text: 'RefreshControl.SIZE',
url: 'refreshcontrol.md#refreshlayoutconstssize',
},
StatusBarAnimation: {
text: 'StatusBarAnimation',
url: 'statusbar#statusbaranimation',
},
StatusBarStyle: {
text: 'StatusBarStyle',
url: 'statusbar#statusbarstyle',
},
ReactNode: {
text: 'React.Node',
url: 'react-node.md',
},
TextStyleProps: {
text: 'Text Style Props',
url: 'text-style-props',
},
SectionT: {
text: 'Section',
url: 'sectionlist#section',
},
ViewStyleProps: {
text: 'View Style Props',
url: 'view-style-props',
},
Text: {
text: 'Text',
url: 'text#style',
},
},
};

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) AiCure, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
const {typeOf} = require('tokenize-comment/lib/utils');
const magic = require('./magic');
const {formatMultiplePlatform} = require('./utils');
function formatMethodType(param) {
let text, url;
if (param?.type?.name === 'union') {
if (param?.type?.alias) {
const {alias} = param.type;
if (Object.hasOwnProperty.call(magic.linkableTypeAliases, alias)) {
({url, text} = magic.linkableTypeAliases[alias]);
}
if (url) return `[${text}](${url})`;
else return param.type.alias;
}
return param.type.name;
} else {
if (param?.type?.type) return param.type.type;
else return param.type.name;
}
}
function formatMethodName(param) {
let tag = param.description;
if (tag) {
const isMatch = tag.match(/{@platform [a-z ,]*}/);
if (isMatch) {
const platform = isMatch[0].match(/ [a-z ,]*/);
tag = tag.replace(/{@platform [a-z ,]*}/g, '');
tag = formatMultiplePlatform(platform[0].split(','));
return param.name + tag;
}
}
return param.name;
}
function formatMethodDescription(param) {
let tag = param.description;
const isMatch = tag.match(/{@platform [a-z ,]*}/);
if (isMatch) {
const platform = isMatch[0].match(/ [a-z ,]*/);
// Replaces @platform strings with empty string
// and appends type with formatted platform
tag = tag.replace(/{@platform [a-z ,]*}/g, '');
}
return tag;
}
module.exports = {
formatMethodType,
formatMethodName,
formatMethodDescription,
};

View File

@@ -0,0 +1,18 @@
{
"name": "@react-native-website/sync-api",
"version": "0.0.1",
"private": true,
"scripts": {
"prettier": "prettier --write \"**/*.js\"",
"sync": "node sync-api-docs ../../react-native"
},
"devDependencies": {
"@motiz88/react-native-docgen": "0.0.3",
"fs-extra": "^9.1.0",
"glob": "^7.1.6",
"he": "^1.2.0",
"path": "^0.12.7",
"react-docgen-markdown-renderer": "^2.1.3",
"tokenize-comment": "^3.0.1"
}
}

View File

@@ -0,0 +1,85 @@
/**
* Copyright (c) AiCure, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
// Preprocess the react-docgen artifact before rendering it to Markdown.
// This file may end up in the OpenDBM repo, as part of the
// `generate-api-docs` script.
const tokenizeComment = require('tokenize-comment');
const {typeOf} = require('tokenize-comment/lib/utils');
function joinDescriptionAndExamples(tokenized) {
let sections = [];
if (tokenized.description) {
sections.push(tokenized.description);
}
for (const {raw} of tokenized.examples) {
sections.push(raw);
}
if (tokenized.footer) {
sections.push(tokenized.footer);
}
return sections.join('\n\n');
}
function preprocessTagsInDescription(obj) {
if (obj && obj.description) {
obj.description = obj.description
.split('\n')
.map(line => {
return line.replace(/ /, '');
})
.join('\n');
const descriptionTokenized = tokenizeComment(obj.description);
obj.description = obj.description.replace(
/@platform .*|@default .*|@type .*/g,
''
);
obj.rnTags = {};
const platformTag = descriptionTokenized.tags.find(
({key}) => key === 'platform'
);
const defaultTag = descriptionTokenized.tags.filter(
tag => tag.key === 'default'
);
const typeTag = descriptionTokenized.tags.filter(tag => tag.key === 'type');
if (platformTag) {
obj.rnTags.platform = platformTag.value.split(',');
}
if (defaultTag.length) {
obj.rnTags.default = [];
defaultTag.forEach(tag => {
obj.rnTags.default.push(tag.value);
});
}
if (typeTag.length) {
obj.rnTags.type = [];
typeTag.forEach(tag => {
obj.rnTags.type.push(tag.value);
});
}
}
}
// NOTE: This function mutates `docs`.
function preprocessGeneratedApiDocs(docs) {
for (const {component} of docs) {
if (component.props && component.description) {
for (const prop of Object.values(component.props)) {
preprocessTagsInDescription(prop);
}
for (const prop of component.methods) {
preprocessTagsInDescription(prop);
}
}
}
}
module.exports = preprocessGeneratedApiDocs;

View File

@@ -0,0 +1,246 @@
/**
* Copyright (c) AiCure, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
const {typeOf} = require('tokenize-comment/lib/utils');
const magic = require('./magic');
const {
formatMultiplePlatform,
stringToInlineCodeForTable,
maybeLinkifyType,
maybeLinkifyTypeName,
formatType,
} = require('./utils');
function formatTypeColumn(prop) {
// Checks for @type pragma comment
if (prop.rnTags && prop.rnTags.type) {
let tableRows = '';
const typeTags = prop.rnTags.type;
typeTags.forEach(tag => {
let url, text;
// Checks for @platform pragma in @type string
const isMatch = tag.match(/{@platform [a-z ,]*}/);
if (isMatch) {
// Extracts platforms from matched regex
const platform = isMatch[0].match(/ [a-z ,]*/);
// Replaces @platform strings with empty string
// and appends type with formatted platform
tag = tag.replace(/{@platform [a-z ,]*}/g, '');
if (Object.hasOwnProperty.call(magic.linkableTypeAliases, tag)) {
({url, text} = magic.linkableTypeAliases[tag]);
if (url) tag = `[${text}](${url})`;
}
tag = tag + formatMultiplePlatform(platform[0].split(','));
} else {
// Check if there are multiple comma separated types in a single line
if (tag.match(/, /)) {
let newTag = '';
const tags = tag.split(', ');
tags.forEach(item => {
if (Object.hasOwnProperty.call(magic.linkableTypeAliases, item)) {
({url, text} = magic.linkableTypeAliases[item]);
if (url) newTag += ', ' + `[${text}](${url})`;
} else newTag += ', ' + item;
});
//Trim comma from beginning
tag = newTag.replace(/^, /, '');
}
// If there is no comma separated types in rnTags
else if (Object.hasOwnProperty.call(magic.linkableTypeAliases, tag)) {
({url, text} = magic.linkableTypeAliases[tag]);
if (url) tag = `[${text}](${url})`;
}
}
tableRows = tableRows + tag + '<hr/>';
});
tableRows = tableRows.replace(/<hr\/>$/, '');
return tableRows;
}
// To extract type from prop flowType
else if (prop.flowType && Object.keys(prop.flowType).length >= 1) {
let text, url;
// Handles flowtype name for signatures
if (prop.flowType.name === 'signature') {
// Handles flowtype for function signature
if (prop.flowType.type === 'function') {
// Extracts EventType from the raw value
const isMatch = prop.flowType.raw.match(/: [a-zA-Z]*/);
if (isMatch) {
// Formats EventType
const eventType = isMatch[0].slice(2);
// Checks for aliases in magic and generates md url
if (
Object.hasOwnProperty.call(magic.linkableTypeAliases, eventType)
) {
({url, text} = magic.linkableTypeAliases[eventType]);
if (url) {
return `${prop.flowType.type}([${text}](${url}))`;
}
}
// TODO: Handling unknown function params
return `${prop.flowType.type}`;
} else {
return prop.flowType.type;
}
} else if (prop.flowType.type === 'object') {
return prop.flowType.type;
}
} else if (prop.flowType.name.includes('$ReadOnlyArray')) {
prop?.flowType?.elements[0]?.elements &&
prop?.flowType?.elements[0]?.elements.forEach(elem => {
if (
Object.hasOwnProperty.call(magic.linkableTypeAliases, elem.name)
) {
({url, text} = magic.linkableTypeAliases[elem.name]);
}
});
if (url) return `array of [${text}](${url})`;
else if (prop?.flowType?.elements[0].name === 'union') {
const unionTypes = prop?.flowType?.elements[0]?.elements.reduce(
(acc, curr) => {
acc.push(curr.value);
return acc;
},
[]
);
return `array of enum(${unionTypes.join(', ')})`;
} else if (prop?.flowType?.elements[0]?.name) {
const typeName = prop.flowType.elements[0].name;
//array of number
if (typeName === 'number') return `array of ${typeName}`;
else if (
Object.hasOwnProperty.call(magic.linkableTypeAliases, typeName)
) {
({url, text} = magic.linkableTypeAliases[typeName]);
if (url) return `array of [${text}](${url})`;
}
//default array for all other types
else return 'array';
}
} else if (prop.flowType.name === '$ReadOnly') {
// Special Case: switch#trackcolor
let markdown = '';
if (prop.flowType.elements[0]?.type === 'object') {
prop?.flowType?.elements[0]?.signature?.properties.forEach(
({key, value}) => {
value?.elements?.forEach(elem => {
if (
Object.hasOwnProperty.call(magic.linkableTypeAliases, elem.name)
) {
({url, text} = magic.linkableTypeAliases[elem.name]);
markdown += `${key}: [${text}](${url})` + ', ';
}
});
if (!url) markdown += `${key}: ${value.name}` + ', ';
}
);
if (markdown.match(/, $/)) markdown = markdown.replace(/, $/, '');
return `${prop.flowType.elements[0]?.type}: {${markdown}}`;
}
} else if (prop.flowType.name === 'union') {
let unionTypes = prop.flowType.raw.split('|');
// Trim whitespaces and remove any leftover `|` (to avoid table split)
unionTypes = unionTypes
.map(elem => {
return elem.trim().replace(/|/g, '');
})
.filter(item => {
if (item) return item;
});
// Get text and url from magic aliases
prop?.flowType?.elements?.forEach(elem => {
if (Object.hasOwnProperty.call(magic.linkableTypeAliases, elem.name)) {
({url, text} = magic.linkableTypeAliases[elem.name]);
}
});
if (url) return `[${text}](${url})`;
return `enum(${unionTypes.join(', ')})`;
} else if (prop.flowType.name === 'ReactElement') {
return 'element';
} else {
// Get text and url from magic aliases
prop?.flowType?.elements?.forEach(elem => {
if (Object.hasOwnProperty.call(magic.linkableTypeAliases, elem.name)) {
({url, text} = magic.linkableTypeAliases[elem.name]);
}
});
}
// If no text is found, get raw values as text
if (!text) {
// TO BE FIXED
text =
(prop.flowType.raw && stringToInlineCodeForTable(prop.flowType.raw)) ||
formatType(prop.flowType.name);
}
// If URL is found, return text and link in markdown format
if (url) {
return `[${text}](${url})`;
}
return text;
}
}
// Adds proper markdown formatting to component's default value.
function formatDefaultColumn(prop) {
if (prop?.rnTags?.default) {
// Parse from @default annotation
let tableRows = '';
prop.rnTags.default.forEach(tag => {
const isMatch = tag.match(/{@platform [a-z]*}/);
if (isMatch) {
const platform = isMatch[0].match(/ [a-z]*/);
tag = tag.replace(/{@platform [a-z]*}/g, '');
// Checks component for NativeColorValue in default
let colorBlock = '';
prop?.flowType?.elements.some(elem => {
if (elem.name === 'NativeColorValue' && !tag.includes('null')) {
colorBlock = `<ins style={{background: '${tag.replace(
/'/g,
''
)}'}} className="color-box" />`;
return true;
}
});
tag =
colorBlock +
(!tag.includes('null') ? '`' + tag + '`' : tag) +
formatMultiplePlatform(platform[0].split(','));
} else if (!tag.includes('`')) {
tag = '`' + tag + '`';
}
tableRows = tableRows + tag + '<hr/>';
});
tableRows = tableRows.replace(/<hr\/>$/, '');
return {Default: tableRows};
} else {
// Parse defaultValue if @default annotation not provided
return prop?.defaultValue?.value
? {Default: stringToInlineCodeForTable(prop?.defaultValue?.value)}
: '';
}
}
module.exports = {
formatTypeColumn,
formatDefaultColumn,
};

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) AiCure, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// ***** EXPERIMENTAL *****
// Updates the API docs from the OpenDBM source code.
'use strict';
const process = require('process');
const fs = require('fs-extra');
const path = require('path');
const extractDocsFromRN = require('./extractDocsFromRN');
const preprocessGeneratedApiDocs = require('./preprocessGeneratedApiDocs');
const generateMarkdown = require('./generateMarkdown');
const {titleToId} = require('./utils');
const DOCS_ROOT_DIR = path.resolve(__dirname, '..', 'docs');
async function generateApiDocs(rnPath) {
const apiDocs = await extractDocsFromRN(rnPath);
preprocessGeneratedApiDocs(apiDocs);
await Promise.all(
apiDocs.map(async ({component, file}, index) => {
if (!component.displayName) {
console.log(
`react-docgen data for ${path.basename(file)} was malformed, skipping`
);
return;
}
const id = titleToId(component.displayName);
const componentMarkdown = generateMarkdown(
{title: component.displayName, id: id},
component
);
const outFile = path.join(DOCS_ROOT_DIR, id + '.md');
console.log('Generated ' + outFile);
await fs.writeFile(outFile, componentMarkdown, 'utf8');
})
);
}
async function main(args) {
await generateApiDocs(args[0]);
}
main(process.argv.slice(2));

View File

@@ -0,0 +1,95 @@
/**
* Copyright (c) AiCure, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
const he = require('he');
const magic = require('./magic');
// Adds multiple platform tags for prop name
function formatMultiplePlatform(platforms) {
let platformString = '';
platforms.forEach(platform => {
switch (platform.trim().toLowerCase()) {
case 'ios':
platformString += '<div class="label ios">' + 'iOS' + '</div> ';
break;
case 'android':
platformString += '<div class="label android">' + 'Android' + '</div>';
break;
case 'tv':
platformString += '<div class="label tv">' + 'TV' + '</div>';
break;
//TODO: Add a new CSS class for VR
case 'vr':
platformString += '<div class="label tv">' + 'VR' + '</div>';
}
});
return platformString;
}
// Wraps a string in an inline code block in a way that is safe to include in a
// table cell, by wrapping it as HTML <code> if necessary.
function stringToInlineCodeForTable(str) {
let useHtml = /[`|]/.test(str);
str = str.replace(/\n/g, ' ');
if (useHtml) {
return '<code>' + he.encode(str).replace(/\|/g, '&#124;') + '</code>';
}
return '`' + str + '`';
}
function maybeLinkifyType(flowType) {
let url, text;
flowType.elements?.forEach(elem => {
if (Object.hasOwnProperty.call(magic.linkableTypeAliases, elem.name)) {
({url, text} = magic.linkableTypeAliases[elem.name]);
}
});
if (!text) {
text = stringToInlineCodeForTable(
flowType.raw || formatType(flowType.name)
);
}
if (url) {
return `[${text}](${url})`;
}
return text;
}
function formatType(name) {
if (name.toLowerCase() === 'boolean') return 'bool';
if (name.toLowerCase() === 'stringish') return 'string';
if (name === '$ReadOnlyArray') return 'array';
return name;
}
function maybeLinkifyTypeName(name) {
let url, text;
if (Object.hasOwnProperty.call(magic.linkableTypeAliases, name)) {
({url, text} = magic.linkableTypeAliases[name]);
}
if (!text) {
text = stringToInlineCodeForTable(name);
}
if (url) {
return `[${text}](${url})`;
}
return text;
}
function titleToId(title) {
return title.toLowerCase().replace(/[^a-z]+/g, '-');
}
module.exports = {
formatMultiplePlatform,
stringToInlineCodeForTable,
maybeLinkifyType,
maybeLinkifyTypeName,
formatType,
titleToId,
};

View File

@@ -0,0 +1,15 @@
---
id: api-doc
title: OpendDBM API
---
This section give an overview of OpenDBM APi methods and functions. It is not a requirement to be familiar with all materials to be effective with OpenDBM. You can still benefit from this section as it will give you insights into how we manage things to give added values to this repository.
Each module will have its own raw variables that's too big to be included in this section. You can find the deeper explanation in the Variables section.
## Table of Contents
- [Facial Activity API](facial-activity-api)
- [Movement API](movement-api)
- [Verbal Acoustics API](verbal-acoustics-api)
- [Speech API](speech-api)

View File

@@ -0,0 +1,8 @@
---
title: Test Blog
authors: [rudy-haryanto
]
tags: [announcement, release]
---
# Test Blog

View File

@@ -0,0 +1,10 @@
---
title: Announcing OpenDBM 0.2.1
authors: [rudy-haryanto]
tags: [announcement, release]
---
# Announcing 0.69
We are excited to release a new version of OpenDBM, 0.2.1. This version comes with several improvements for the lot of things. Read on to learn more!

View File

@@ -0,0 +1,6 @@
rudy-haryanto:
name: Rudy Haryanto
title: DevOps TBID
url: https://twitter.com/rudy-haryanto
image_url: https://github.com/rudy-haryanto.png

View File

@@ -0,0 +1,65 @@
---
title: Changelogs in Pull Requests
---
The changelog entry in your pull request serves as a sort of "tl;dr:" for your changes: do they affect Android? are these breaking changes? is something new being added?
Providing a changelog using a standardized format helps release coordinators write release notes. Please include a changelog as part of your pull request description. Your pull request description will be used as the commit message should the pull request get merged.
### Format
A changelog entry has the following format
```
## Changelog:
[Category] [Type] - Message
```
The "Category" field may be one of:
- **Android**, for changes that affect Android.
- **iOS**, for changes that affect iOS.
- **General**, for changes that do not fit any of the other categories.
- **Internal**, for changes that would not be relevant to developers consuming the release notes.
The "Type" field may be one of:
- **Added**, for new features.
- **Changed**, for changes in existing functionality.
- **Deprecated**, for soon-to-be removed features.
- **Removed**, for now removed features.
- **Fixed**, for any bug fixes.
- **Security**, in case of vulnerabilities.
Finally, the "Message" field may answer "what and why" on a feature level. Use this to briefly tell OpenDBM users about notable changes.
For more detail, see [How do I make a good changelog?](https://keepachangelog.com/en/1.0.0/#how) and [Why keep a changelog?](https://keepachangelog.com/en/1.0.0/#why)
### Examples
- `[General] [Added] - Add snapToOffsets prop to ScrollView component`
- `[General] [Fixed] - Fix various issues in snapToInterval on ScrollView component`
- `[iOS] [Fixed] - Fix crash in RCTImagePicker`
### FAQ
#### What if my pull request contains changes to both Android and JavaScript?
Use the Android category.
#### What if my pull request contains changes to both Android and iOS?
Use the General category if the change is made in a single pull request.
#### What if my pull request contains changes to Android, iOS, and JavaScript?
Use the General category if the change is made in a single pull request.
#### What if...?
Any changelog entry is better than none. If you are unsure if you have picked the right category, use the "message" field to succinctly describe your change.
These entries are used by the [`@rnx-kit/rn-changelog-generator`](https://github.com/microsoft/rnx-kit/tree/main/incubator/rn-changelog-generator) script to build a rough draft, which is then edited by a release coordinator.
Your notes will be used to add your change to the correct location in the final release notes.

View File

@@ -0,0 +1,20 @@
---
id: code-of-conduct
title: Code of Conduct
---
Alongside the release of OpenDBM v2.0, we decided to release alongside it a code of conduct as is conventional for community-owned repositories. The code of conduct is [included with the codebase](https://github.com/AiCure/open_dbm/blob/master/CODE_OF_CONDUCT.md) when OpenDBM is downloaded but weve also pasted it below.
## Commitments
In adopting this Code of Conduct, we are committed to strengthening joint efforts, innovation, and the free exchange of ideas for developing, maintaining, and using open-source software. We hope to build an active and inclusive community for development of methods for measurement of health. We want all members of this community to be active participants regardless of race, gender identity, religion, creed, color, marital status, genetic information, age, nationality, or disability.
## Values
We promote values such as being audience centered, staying calm, professional, and respectful during discussions and communicating through active listening, We encourage all members to respectfully accept evaluation and disapproval, to always aim for what is best for the community, and above all respect their fellow members.
We do not promote any kind of harassment, aggression, or intimidation. Personal threats, attacks, stalking, vulgar language, and any kind of insulting statements will not be tolerated. We do not approve of the exchange of disrespectful language, sketches, or cartoons nor the posting of personal information (emails, etc.) without user permission.
## Reporting
To report any complaint or inappropriate behavior, we ask the users to contact the moderators at [opendbm@aicure.com](mailto:opendbm@aicure.com). Moderators of the community have the right to remove offensive content that does not align with the Code of Conduct. If the content explicitly violates the Code of Conduct, it may result in disqualification from upcoming events and being blocked from the [GitHub Organization](https://help.github.com/articles/blocking-a-user-from-your-organization/)

View File

@@ -0,0 +1,14 @@
---
id: community-discussions
title: Community Discussions
---
For any and all discussions related to the use of OpenDBM, we encourage all users to host those conversations using the (as of December of 2020) new Discussions feature on Github i.e. the [Discussions](https://github.com/AiCure/open_dbm/discussions) tab on the OpenDBM Github page. While the [Issues](https://github.com/AiCure/open_dbm/issues) tab is for technical issues, we want the Discussions tab to be for everything else. Below are a couple things we want the Discussions tab to be helpful for.
## Discussions on variables
We want to foster threads in the Discussions tab that users can contribute to regarding digital biomarker variables that are clinically relevant measures in the context of individual patient populations. For example, if a user is wondering which of OpenDBMs outputted variables are relevant to, lets say, Major Depressive Disorder, we want them to be able to turn to a thread in the Discussions tab where they can find that information. Our team will be starting some of these threads ourselves and adding to them as we run into more literature; we encourage other users to also utilize the Discussions tab to create a central repository of such information.
## Experimental design
One of the more common types of questions we get from users of OpenDBM is about experimental design and data collection setups. As is briefly discussed in Chapter 6, the nature of the data collection paradigm and the behaviors that are relevant differ depending on the disease state and the goals of the experiment. Not to mentionwe are not authorities on experimental design whatsoever. If a user has such questions, we encourage them to post them in the [Discussions](https://github.com/AiCure/open_dbm/discussions) tab. We certainly will be monitoring it and responding to any questions to the best of our ability, but we also want to allow other researchers to provide their opinions, which we feel will be just as important to take into consideration.

View File

@@ -0,0 +1,22 @@
---
id: overview
title: Contributing Overview
description: How to contribute to OpenDBM
---
<!-- alex disable simple simply -->
Thank you for your interest in contributing to OpenDBM! From commenting on and triaging issues, to reviewing and sending Pull Requests, all contributions are welcome.
We aim to build a vibrant and inclusive [ecosystem of partners, core contributors, and community](https://github.com/AiCure/open_dbm/blob/main/ECOSYSTEM.md) that goes beyond the main OpenDBM GitHub repository.
The [Open Source Guides](https://opensource.guide/) website has a collection of resources for individuals, communities, and companies who want to learn how to run and contribute to an open source project.
Contributors and people new to open source alike will find the following guides especially useful:
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
- [Building Welcoming Communities](https://opensource.guide/building-community/)
We want to ensure that OpenDBM feels like a community of like-minded researchers and clinicians. Hence, there are a few ways we encourage users to stay involvedand why we [encourage you to join DiME, too!](https://www.dimesociety.org/)
Most importantly, if youre interested in OpenDBM, star the repo and [sign up for our listserv](https://docs.google.com/forms/d/e/1FAIpQLScLrgyM08YIumzJw8dxmRaMQ4byBL3J4A90FpokDhX8-KUNZw/viewform) for all updates.

View File

@@ -0,0 +1,5 @@
---
title: Contribute Guidelines
---
With OpenDBM v2.0, weve also written out a simple version of [contribution guidelines,](https://github.com/AiCure/open_dbm/blob/master/CONTRIBUTING.md) available with the code base. We want to make these more detailed in the future. As we work with the first independent contributors to OpenDBM, we will be coming out with a much more robust packet of instructions for contributors. If you find the current guidelines confusing or have trouble contributing to OpenDBM, please definitely [reach out to us](mailto:opendbm@aicure.com) and were happy to assist.

View File

@@ -0,0 +1,6 @@
---
id: issue-reporting
title: Issue Reporting
---
For any technical issues with using the code i.e. during installation and during use, we ask that youinstead of emailing usbring up the issue using the Issues feature on Github i.e. in the [Issues](https://github.com/AiCure/open_dbm/issues) tab on the OpenDBM github, We have written out an [Issue template](https://github.com/AiCure/open_dbm/blob/master/.github/ISSUE_TEMPLATE/bug_report.md) that will automatically populate when you create a new issue; we suggest you follow that template.

View File

@@ -0,0 +1,12 @@
---
id: license-contact
title: License and Contact
---
## License
OpenDBM is published under the [AGPL v3.0 License.](https://github.com/AiCure/open_dbm/blob/master/license.txt)
## Contact
For technical issues, we ask that you use the [Issues](https://github.com/AiCure/open_dbm/issues) tab to contact us. For any general discussions, we ask that you please use the [Discussions](https://github.com/AiCure/open_dbm/discussions) tab. If youd like to contact us privately and directly, you can email us at [opendbm@aicure.com.](mailto:opendbm@aicure.comopendbm@aicure.com)

View File

@@ -0,0 +1,8 @@
---
id: opendbm-listserv-signup
title: OpenDBM Listserv signup
---
[Use this link to sign up for the OpenDBM Listserv and stay up to date on release and events.](https://docs.google.com/forms/d/e/1FAIpQLScLrgyM08YIumzJw8dxmRaMQ4byBL3J4A90FpokDhX8-KUNZw/viewform)
From time to time, we will be making either small updates to OpenDBM functionalities (additional variables, bug fixes, etc.) or large version releases with major additions to features (e.g., we want a future update to OpenDBM to include analysis of wearable data). We will also be hosting virtual (and hopefully one day in-person) events for the OpenDBM user community that allow for training, introduction to new functionalities, presentations on prominent projects, etc. In either of these cases, wed like to keep the user community up to date so that they dont miss out on these updates or events, so we do encourage you to [sign up for the listserv.](https://docs.google.com/forms/d/e/1FAIpQLScLrgyM08YIumzJw8dxmRaMQ4byBL3J4A90FpokDhX8-KUNZw/viewform)

View File

@@ -0,0 +1,8 @@
---
id: publications-and-referencing
title: Publications and Referencing
---
As the user community grows, we hope and are anticipating scientific publications whose methods depend partially or entirely on methods compiled into OpenDBM. For the authors of those publications, we request that they reference OpenDBM in their work. We are (as of December of 2020) preparing a manuscript that can be referenced. Meanwhile, we ask that authors at the very least reference the Github repository when mentioning OpenDBM.
Additionally, we are hoping to maintain an up to date list of publications that have used methods from OpenDBM when reporting their findings. This live list of publications is linked to from many different places around the Github repository and this documentation, but [here is another link to it](https://docs.google.com/spreadsheets/u/2/d/1pRBWCCFMbEgZNQzm2Litm3RUQ6glwUwswrqGDePMvh0/edit#gid=0). If you are a user of OpenDBM and have used its methods in work that has either been presented at a conference or submitted to a journal, we ask that you please submit a citation of the work to us [using this form](https://forms.gle/Hb6bDL1GJvG1ByUX7) so that we may append the list of publications with your work.

View File

@@ -0,0 +1,52 @@
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
import React, {useState} from 'react';
const DocsRating = ({label}) => {
if (!ExecutionEnvironment.canUseDOM) {
return null;
}
const [haveVoted, setHaveVoted] = useState(false);
const giveFeedback = value => {
if (window.ga) {
window.ga('send', {
hitType: 'event',
eventCategory: 'button',
eventAction: 'feedback',
eventLabel: label,
eventValue: value,
});
}
setHaveVoted(true);
};
return (
<div className="docsRating">
{haveVoted ? (
'Thanks for letting us know!'
) : (
<>
Is this page useful?
<svg
className="i_thumbsup"
alt="Like"
onClick={() => giveFeedback(1)}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 81.13 89.76">
<path d="M22.9 6a18.57 18.57 0 002.67 8.4 25.72 25.72 0 008.65 7.66c3.86 2 8.67 7.13 13.51 11 3.86 3.11 8.57 7.11 11.54 8.45s13.59.26 14.64 1.17c1.88 1.63 1.55 9-.11 15.25-1.61 5.86-5.96 10.55-6.48 16.86-.4 4.83-2.7 4.88-10.93 4.88h-1.35c-3.82 0-8.24 2.93-12.92 3.62a68 68 0 01-9.73.5c-3.57 0-7.86-.08-13.25-.08-3.56 0-4.71-1.83-4.71-4.48h8.42a3.51 3.51 0 000-7H12.28a2.89 2.89 0 01-2.88-2.88 1.91 1.91 0 01.77-1.78h16.46a3.51 3.51 0 000-7H12.29c-3.21 0-4.84-1.83-4.84-4a6.41 6.41 0 011.17-3.78h19.06a3.5 3.5 0 100-7H9.75A3.51 3.51 0 016 42.27a3.45 3.45 0 013.75-3.48h13.11c5.61 0 7.71-3 5.71-5.52-4.43-4.74-10.84-12.62-11-18.71-.15-6.51 2.6-7.83 5.36-8.56m0-6a6.18 6.18 0 00-1.53.2c-6.69 1.77-10 6.65-9.82 14.5.08 5.09 2.99 11.18 8.52 18.09H9.74a9.52 9.52 0 00-6.23 16.9 12.52 12.52 0 00-2.07 6.84 9.64 9.64 0 003.65 7.7 7.85 7.85 0 00-1.7 5.13 8.9 8.9 0 005.3 8.13 6 6 0 00-.26 1.76c0 6.37 4.2 10.48 10.71 10.48h13.25a73.75 73.75 0 0010.6-.56 35.89 35.89 0 007.58-2.18 17.83 17.83 0 014.48-1.34h1.35c4.69 0 7.79 0 10.5-1 3.85-1.44 6-4.59 6.41-9.38.2-2.46 1.42-4.85 2.84-7.62a41.3 41.3 0 003.42-8.13 48 48 0 001.59-10.79c.1-5.13-1-8.48-3.35-10.55-2.16-1.87-4.64-1.87-9.6-1.88a46.86 46.86 0 01-6.64-.29c-1.92-.94-5.72-4-8.51-6.3l-1.58-1.28c-1.6-1.3-3.27-2.79-4.87-4.23-3.33-3-6.47-5.79-9.61-7.45a20.2 20.2 0 01-6.43-5.53 12.44 12.44 0 01-1.72-5.36 6 6 0 00-6-5.86z" />
</svg>
<svg
className="i_thumbsdown"
alt="Dislike"
onClick={() => giveFeedback(0)}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 81.13 89.76">
<path d="M22.9 6a18.57 18.57 0 002.67 8.4 25.72 25.72 0 008.65 7.66c3.86 2 8.67 7.13 13.51 11 3.86 3.11 8.57 7.11 11.54 8.45s13.59.26 14.64 1.17c1.88 1.63 1.55 9-.11 15.25-1.61 5.86-5.96 10.55-6.48 16.86-.4 4.83-2.7 4.88-10.93 4.88h-1.35c-3.82 0-8.24 2.93-12.92 3.62a68 68 0 01-9.73.5c-3.57 0-7.86-.08-13.25-.08-3.56 0-4.71-1.83-4.71-4.48h8.42a3.51 3.51 0 000-7H12.28a2.89 2.89 0 01-2.88-2.88 1.91 1.91 0 01.77-1.78h16.46a3.51 3.51 0 000-7H12.29c-3.21 0-4.84-1.83-4.84-4a6.41 6.41 0 011.17-3.78h19.06a3.5 3.5 0 100-7H9.75A3.51 3.51 0 016 42.27a3.45 3.45 0 013.75-3.48h13.11c5.61 0 7.71-3 5.71-5.52-4.43-4.74-10.84-12.62-11-18.71-.15-6.51 2.6-7.83 5.36-8.56m0-6a6.18 6.18 0 00-1.53.2c-6.69 1.77-10 6.65-9.82 14.5.08 5.09 2.99 11.18 8.52 18.09H9.74a9.52 9.52 0 00-6.23 16.9 12.52 12.52 0 00-2.07 6.84 9.64 9.64 0 003.65 7.7 7.85 7.85 0 00-1.7 5.13 8.9 8.9 0 005.3 8.13 6 6 0 00-.26 1.76c0 6.37 4.2 10.48 10.71 10.48h13.25a73.75 73.75 0 0010.6-.56 35.89 35.89 0 007.58-2.18 17.83 17.83 0 014.48-1.34h1.35c4.69 0 7.79 0 10.5-1 3.85-1.44 6-4.59 6.41-9.38.2-2.46 1.42-4.85 2.84-7.62a41.3 41.3 0 003.42-8.13 48 48 0 001.59-10.79c.1-5.13-1-8.48-3.35-10.55-2.16-1.87-4.64-1.87-9.6-1.88a46.86 46.86 0 01-6.64-.29c-1.92-.94-5.72-4-8.51-6.3l-1.58-1.28c-1.6-1.3-3.27-2.79-4.87-4.23-3.33-3-6.47-5.79-9.61-7.45a20.2 20.2 0 01-6.43-5.53 12.44 12.44 0 01-1.72-5.36 6 6 0 00-6-5.86z" />
</svg>
</>
)}
</div>
);
};
export default DocsRating;

View File

@@ -0,0 +1,119 @@
const theme = {
plain: {
color: '#FFFFFF',
backgroundColor: '#282C34',
},
styles: [
{
types: ['property'],
style: {
color: '#2aa198',
},
},
{
types: ['attr-name', 'comment', 'prolog', 'doctype', 'cdata'],
style: {
color: '#93a1a1',
},
},
{
types: ['punctuation'],
style: {
color: '#657b83',
},
},
{
types: ['namespace'],
style: {
opacity: 0.7,
},
},
{
types: ['selector', 'char', 'builtin', 'url'],
style: {
color: '#2aa198',
},
},
{
types: ['entity'],
style: {
color: '#2aa198',
},
},
{
types: ['atrule', 'inserted'],
style: {
color: '#859900',
},
},
{
types: ['important', 'variable', 'deleted'],
style: {
color: '#cb4b16',
},
},
{
types: ['important', 'bold'],
style: {
fontWeight: 'bold',
},
},
{
types: ['italic'],
style: {
fontStyle: 'italic',
},
},
{
types: ['entity'],
style: {
cursor: 'help',
},
},
// react-native theme
{
types: ['attr-name', 'keyword'],
style: {
color: '#c5a5c5',
},
},
{
types: ['string', 'regex', 'attr-value'],
style: {
color: '#8dc891',
},
},
{
types: ['number', 'constant', 'symbol'],
style: {
color: '#5a9bcf',
},
},
{
types: ['boolean'],
style: {
color: '#ff8b50',
},
},
{
types: ['class-name'],
style: {
color: '#fac863',
},
},
{
types: ['function'],
style: {
color: '#79b6f2',
},
},
{
types: ['operator', 'tag'],
style: {
color: '#fc929e',
},
},
],
};
module.exports = theme;

View File

@@ -0,0 +1,22 @@
import React, {useState} from 'react';
import CodeBlock from '@theme/CodeBlock';
const TableRowWithCodeBlock = ({name, url, code}) => (
<tr>
<td>
{url ? (
<a href={url} target="_blank" rel="noopener noreferrer">
{name}
</a>
) : (
name
)}
</td>
<td>
<CodeBlock>{code}</CodeBlock>
</td>
</tr>
);
export default TableRowWithCodeBlock;

View File

@@ -0,0 +1,95 @@
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
const isMacOS = ExecutionEnvironment.canUseDOM
? navigator.platform.startsWith('Mac')
: false;
const isWindows = ExecutionEnvironment.canUseDOM
? navigator.platform.startsWith('Win')
: false;
const syntax = [
{label: 'Function Component', value: 'functional'},
{label: 'Class Component', value: 'classical'},
];
const defaultSyntax = 'functional';
const packageManagers = [
{label: 'npm', value: 'npm'},
{label: 'Yarn', value: 'yarn'},
];
const defaultPackageManager = 'npm';
const androidLanguages = [
{label: 'Java', value: 'java'},
{label: 'Kotlin', value: 'kotlin'},
];
const defaultAndroidLanguage = 'java';
const javaScriptSpecLanguages = [
{label: 'Flow', value: 'flow'},
{label: 'TypeScript', value: 'typescript'},
];
const defaultJavaScriptSpecLanguages = 'flow';
const guides = [{label: 'Install Dependencies', value: 'dep-install'}];
const defaultGuide = 'dep-install';
const guidesDocker = [{label: 'Installing Docker Images', value: 'docker-install'}];
const defaultGuideDocker = 'docker-install';
const platforms = [
{label: 'Android', value: 'android'},
{label: 'iOS', value: 'ios'},
];
const defaultPlatform = isMacOS ? 'ios' : 'android';
const oses = [
{label: 'macOS', value: 'macos'},
{label: 'Linux', value: 'linux'},
{label: 'Windows', value: 'windows'},
];
const defaultOs = isMacOS ? 'macos' : isWindows ? 'windows' : 'linux';
const getDevNotesTabs = (tabs = ['android', 'ios', 'web', 'windows']) =>
[
tabs.includes('android') ? {label: 'Android', value: 'android'} : undefined,
tabs.includes('ios') ? {label: 'iOS', value: 'ios'} : undefined,
tabs.includes('web') ? {label: 'Web', value: 'web'} : undefined,
tabs.includes('windows') ? {label: 'Windows', value: 'windows'} : undefined,
].filter(Boolean);
const getLibraryNotesTabs = (
tabs = ['researchers', 'data_scientist', 'engineer']
) =>
[
tabs.includes('researchers')
? {label: 'Researchers', value: 'researchers'}
: undefined,
tabs.includes('data_scientist')
? {label: 'Data Scientist', value: 'data_scientist'}
: undefined,
tabs.includes('engineer')
? {label: 'Python Engineer', value: 'engineer'}
: undefined,
].filter(Boolean);
export default {
defaultGuide,
defaultGuideDocker,
defaultOs,
defaultPackageManager,
defaultPlatform,
defaultSyntax,
defaultAndroidLanguage,
javaScriptSpecLanguages,
defaultJavaScriptSpecLanguages,
getDevNotesTabs,
getLibraryNotesTabs,
guides,
guidesDocker,
oses,
packageManagers,
platforms,
syntax,
androidLanguages,
};

View File

@@ -0,0 +1,370 @@
const users = require('./showcase.json');
const versions = require('./versions.json');
const lastVersion = versions[0];
const copyright = `Copyright © ${new Date().getFullYear()} AiCure, Inc.`;
const commonDocsOptions = {
breadcrumbs: false,
showLastUpdateAuthor: false,
showLastUpdateTime: true,
editUrl: 'https://github.com/AiCure/open_dbm/blob/master/docs/',
remarkPlugins: [require('@react-native-website/remark-snackplayer')],
};
/** @type {import('@docusaurus/types').DocusaurusConfig} */
module.exports = {
title: 'OpenDBM',
tagline: 'AiCure Digital Biomaker Tools',
organizationName: 'teebid',
projectName: 'aicure_opendbm',
url: 'https://teebid.github.io',
baseUrl: '/aicure_opendbm/',
clientModules: [require.resolve('./snackPlayerInitializer.js')],
trailingSlash: false, // because trailing slashes can break some existing relative links
scripts: [
{
src: 'https://cdn.jsdelivr.net/npm/focus-visible@5.2.0/dist/focus-visible.min.js',
defer: true,
},
{
src: 'https://widget.surveymonkey.com/collect/website/js/FILLWITHSOMETHING.js',
defer: true,
},
{src: 'https://snack.expo.dev/embed.js', defer: true},
],
favicon: 'img/favicon.ico',
titleDelimiter: '·',
customFields: {
users,
},
i18n: {
defaultLocale: 'en',
locales: ['en'],
},
onBrokenLinks: 'throw',
webpack: {
jsLoader: isServer => ({
loader: require.resolve('esbuild-loader'),
options: {
loader: 'tsx',
format: isServer ? 'cjs' : undefined,
target: isServer ? 'node12' : 'es2017',
},
}),
},
presets: [
[
'@docusaurus/preset-classic',
/** @type {import('@docusaurus/preset-classic').Options} */
({
docs: {
path: '../docs',
sidebarPath: require.resolve('./sidebars.json'),
editCurrentVersion: true,
onlyIncludeVersions:
process.env.PREVIEW_DEPLOY === 'true'
? ['current', ...versions.slice(0, 2)]
: undefined,
versions: {
[lastVersion]: {
badge: false, // Do not show version badge for last RN version
},
},
...commonDocsOptions,
},
blog: {
path: 'blog',
blogSidebarCount: 'ALL',
blogSidebarTitle: 'All Blog Posts',
feedOptions: {
type: 'all',
copyright,
},
},
theme: {
customCss: [
require.resolve('./src/css/customTheme.scss'),
require.resolve('./src/css/index.scss'),
require.resolve('./src/css/showcase.scss'),
require.resolve('./src/css/versions.scss'),
],
},
googleAnalytics: {
trackingID: 'UA-41298772-2',
},
gtag: {
trackingID: 'UA-41298772-2',
},
}),
],
],
plugins: [
'docusaurus-plugin-sass',
[
'content-docs',
/** @type {import('@docusaurus/plugin-content-docs').Options} */
({
id: 'extras',
path: 'extras',
routeBasePath: '/extras',
sidebarPath: require.resolve('./sidebarsExtras.json'),
...commonDocsOptions,
}),
],
[
'content-docs',
/** @type {import('@docusaurus/plugin-content-docs').Options} */
({
id: 'contributing',
path: 'contributing',
routeBasePath: '/contributing',
sidebarPath: require.resolve('./sidebarsContributing.json'),
...commonDocsOptions,
}),
],
[
'content-docs',
/** @type {import('@docusaurus/plugin-content-docs').Options} */
({
id: 'api',
path: 'api',
routeBasePath: '/api',
sidebarPath: require.resolve('./sidebarsAPI.json'),
...commonDocsOptions,
}),
],
[
'@docusaurus/plugin-pwa',
{
debug: true,
offlineModeActivationStrategies: ['appInstalled', 'queryString'],
pwaHead: [
{
tagName: 'link',
rel: 'icon',
href: '/img/pwa/manifest-icon-512.png',
},
{
tagName: 'link',
rel: 'manifest',
href: '/manifest.json',
},
{
tagName: 'meta',
name: 'theme-color',
content: '#20232a',
},
{
tagName: 'meta',
name: 'apple-mobile-web-app-capable',
content: 'yes',
},
{
tagName: 'meta',
name: 'apple-mobile-web-app-status-bar-style',
content: '#20232a',
},
{
tagName: 'link',
rel: 'apple-touch-icon',
href: '/img/pwa/manifest-icon-512.png',
},
{
tagName: 'link',
rel: 'mask-icon',
href: '/img/pwa/manifest-icon-512.png',
color: '#06bcee',
},
{
tagName: 'meta',
name: 'msapplication-TileImage',
href: '/img/pwa/manifest-icon-512.png',
},
{
tagName: 'meta',
name: 'msapplication-TileColor',
content: '#20232a',
},
],
},
],
],
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
prism: {
defaultLanguage: 'jsx',
theme: require('./core/PrismTheme'),
additionalLanguages: [
'java',
'kotlin',
'objectivec',
'swift',
'groovy',
'ruby',
'flow',
],
},
navbar: {
title: 'OpenDBM',
logo: {
src: 'img/header_logo.png',
alt: 'OpenDBM',
},
style: 'dark',
items: [
{
label: 'Getting Started',
type: 'doc',
docId: 'getting-started',
position: 'right',
},
{
label: 'Variables',
type: 'doc',
docId: 'biomaker-variables',
position: 'right',
},
{
label: 'API',
type: 'doc',
docId: 'api-doc',
position: 'right',
docsPluginId: 'api',
},
{
label: 'Resources',
type: 'doc',
docId: 'extras',
position: 'right',
docsPluginId: 'extras',
},
{
to: '/blog',
label: 'Blog',
position: 'right',
},
{
type: 'docsVersionDropdown',
position: 'left',
dropdownActiveClassDisabled: true,
dropdownItemsAfter: [
{
to: '/versions',
label: 'All versions',
},
],
},
{
href: 'https://github.com/AiCure/open-dbm',
'aria-label': 'GitHub repository',
position: 'right',
className: 'navbar-github-link',
},
],
},
image: 'img/logo-og.png',
footer: {
style: 'dark',
links: [
{
title: 'Docs',
items: [
{
label: 'Getting Started',
to: 'docs/getting-started',
},
{
label: 'Tutorial',
to: 'docs/tutorial',
},
{
label: 'OpenDBM Biomaker Variables',
to: 'docs/biomaker-variables',
},
{
label: 'More Resources',
to: 'docs/more-resources',
},
],
},
{
title: 'Community',
items: [
{
label: 'The OpenDBM Community',
to: 'help',
},
{
label: "Who's using OpenDBM?",
to: 'showcase',
},
{
label: 'Ask Questions on Stack Overflow',
href: 'https://stackoverflow.com/questions/tagged/opendbm',
},
],
},
{
title: 'Find us',
items: [
{
label: 'Blog',
to: 'blog',
},
{
label: 'GitHub',
href: 'https://github.com/AiCure/open-dbm',
},
],
},
{
title: 'More',
items: [
{
label: 'AiCure',
href: 'https://aicure.com/',
},
{
label: 'AiCure OpenDBM',
href: 'https://aicure.com/opendbm/',
},
{
label: 'Docs (Word version)',
href: 'https://docs.google.com/document/d/1O6OUSY9FHFCZfpIWu3Kgg0Q_dyp073xjjQ2K3viEffU/edit#heading=h.rxr2y5t62mwa',
},
],
},
],
logo: {
alt: 'OpenDBM Open Source Logo',
src: 'img/oss_logo.png',
href: 'https://aicure.com/opendbm',
},
copyright,
},
algolia: {
appId: '8TDSE0OHGQ',
apiKey: '83cd239c72f9f8b0ed270a04b1185288',
indexName: 'react-native-v2',
contextualSearch: true,
},
metadata: [
{
property: 'og:image',
content:
'https://raw.githubusercontent.com/teebid/aicure_opendbm/master/docs/website/static/img/header_logo.png',
},
{name: 'twitter:card', content: 'summary_large_image'},
{
name: 'twitter:image',
content:
'https://raw.githubusercontent.com/teebid/aicure_opendbm/master/docs/website/static/img/header_logo.png',
},
{name: 'twitter:site', content: '@aicure'},
],
}),
};

View File

@@ -0,0 +1,5 @@
:::caution
This document refers to the architecture of the new renderer, [Fabric](fabric-renderer), that is in active roll-out.
:::

View File

@@ -0,0 +1,65 @@
---
title: Changelogs in Pull Requests
---
The changelog entry in your pull request serves as a sort of "tl;dr:" for your changes: do they affect Android? are these breaking changes? is something new being added?
Providing a changelog using a standardized format helps release coordinators write release notes. Please include a changelog as part of your pull request description. Your pull request description will be used as the commit message should the pull request get merged.
### Format
A changelog entry has the following format
```
## Changelog:
[Category] [Type] - Message
```
The "Category" field may be one of:
- **Android**, for changes that affect Android.
- **iOS**, for changes that affect iOS.
- **General**, for changes that do not fit any of the other categories.
- **Internal**, for changes that would not be relevant to developers consuming the release notes.
The "Type" field may be one of:
- **Added**, for new features.
- **Changed**, for changes in existing functionality.
- **Deprecated**, for soon-to-be removed features.
- **Removed**, for now removed features.
- **Fixed**, for any bug fixes.
- **Security**, in case of vulnerabilities.
Finally, the "Message" field may answer "what and why" on a feature level. Use this to briefly tell OpenDBM users about notable changes.
For more detail, see [How do I make a good changelog?](https://keepachangelog.com/en/1.0.0/#how) and [Why keep a changelog?](https://keepachangelog.com/en/1.0.0/#why)
### Examples
- `[General] [Added] - Add snapToOffsets prop to ScrollView component`
- `[General] [Fixed] - Fix various issues in snapToInterval on ScrollView component`
- `[iOS] [Fixed] - Fix crash in RCTImagePicker`
### FAQ
#### What if my pull request contains changes to both Android and JavaScript?
Use the Android category.
#### What if my pull request contains changes to both Android and iOS?
Use the General category if the change is made in a single pull request.
#### What if my pull request contains changes to Android, iOS, and JavaScript?
Use the General category if the change is made in a single pull request.
#### What if...?
Any changelog entry is better than none. If you are unsure if you have picked the right category, use the "message" field to succinctly describe your change.
These entries are used by the [`@rnx-kit/rn-changelog-generator`](https://github.com/microsoft/rnx-kit/tree/main/incubator/rn-changelog-generator) script to build a rough draft, which is then edited by a release coordinator.
Your notes will be used to add your change to the correct location in the final release notes.

View File

@@ -0,0 +1,98 @@
---
id: ci-cd-pipeline
title: OpenDBM CI CD Pipeline
---
## Summary
OpenDBM CI CD Pipeline is very important as its job is to maintain the quality of the library for each new changes. The OpenDBM CI CD pipeline is divided into multiple sections that serve different purposes. Before OpenDBM dive into those sections, please see below the summary of the entirer pipeline flow.
<figure>
<img src="../docs/assets/odbm_pipeline.png" width="500" alt="OpenDBM Pipeline Flow" />
<figcaption>ODBM Pipeline Flow</figcaption>
</figure>
The git flow started when the community or OpenDBM team create stories to do. You can fork from master branch and named the fork based on the requirement (bug fix / enhancement). For instance if its enhancement, the fork name is feature/[github_name]/[feature_name]. The naming convention is not enforced for now but its a good practise to do so. Then after the feature developed and tested locally, the developer need to commit and push their changes into the their forked repository. But in doing so, there will be pre-commit checking which focused on linting will be executed. Upon success, the code will be pushed to your forked repo.
After it pushed, you need to create a pull request. After the PR is created, Github Actions will automatically trigger Code Checking chain processes, to make sure the code is clean and clear of vulnerabilities. It will also run the series of unit tests and run the test cases on different platform (MacOs, Windows, Linux). Only after the Code Checking success, the Release Manager can merge the PR.
After it merged, again the Github Actions will be executed Code Checking on the master branch. After the Code Checking is successful, the developers need to create the tag which marked as release version. When tag created successfully, it will again run the Code Check, and additionally it will run the automatic update of OpenDBM to PyPi.
<figure>
<img src="../docs/assets/odbm_workflows.png" width="500" alt="OpenDBM Workflows" />
<figcaption>ODBM Workflows</figcaption>
</figure>
> The OpenDBM is also using caching to speed up the pipeline deployment. So the installation and dependencies installation will be done in fast time. The only time needed is to download the cache into the github action container. You can check the cache key in job `- uses: actions/cache@v3` in `.github/workflows/open_dbm-code-checking.yml` file.
## OpenDBM Code Checking
The configuration for this pipelines is located under `.github/workflows/open_dbm-code-checking.yml`. Its the pipeline that will trigger in almost every git events. When you push your changes to your forked repository, it will run using your own github actions. It also only triggers when you change the files of the core library such as under dbm_lib directory. Any non-core files changes will not trigger this pipeline.
### Linting
Linting is a way of detecting errors in code that can be solved with syntax or style changes. People often use linting to find simple errors, such as inconsistent spacing or formatting. This is generally more effective than merely running the code.
A good linter will often flag errors that the compiler would not catch. Often these are frivolous problems such as unused variables, but they serve to remind programmers about best practices and are sometimes harder for humans to catch when scanning through a large body of code. In Python, there are many options for linters including PyLint, black and pylama. In ODBM-G2, OpenDBM will use flake8 as our linter.
### Vulnerabilities Scanning
With a 650% increase in next-gen cyberattacks against open source tools recorded in the last year, it is important to secure openDBM code by scanning the possible vulnerabilities from third party libraries.
ODBM-G2 will be using safety (https://pypi.org/project/safety) to scan the third party vulnerabilities.
<figure>
<img src="../docs/assets/odbm_safety.png" width="500" alt="OpenDBM Safety" />
<figcaption>ODBM Safety</figcaption>
</figure>
### Unit tests
Unit Test is a way to test every unit/component in codes. This unit can be a function, method, procedure, or module. Unit tests must be isolated from the original code to verify its correctness.
OpenDBM is using one of the most popular frameworks for unit testing, Pytest. Pytest framework is easier to read, more straightforward to write, and can be scalable to build complex testing.
In OpenDBM project, the unit test will be stored in Test folder. The Unit Test file structure will mimic OpenDBM File Structure to improve the readability / easier to check. For example, as below.
<figure>
<img src="../docs/assets/odbm_unittest.png" width="500" alt="OpenDBM Unit tests" />
<figcaption>ODBM Unit tests</figcaption>
</figure>
To have test case material, OpenDBM use video from various sources to generate all output from ODBM-G1. This video captures all the functionality that OpenDBM has, such as emotional expressivity, eye blink, head movement, speech, etc. The output that is stored in CSV files then are used as our material to do unit testing
There will be 2 types of expected output:
* Non-numeric. If the expected output is non-numeric, then in order to pass the test case, the new codes must generate precisely the same output as the previous codes.
* Numeric. If the expected output is numeric, then in order to pass the test case, the new codes should generate the same output as the previous codes with a margin of error.
Formula for margin of error will be as follow:
tol=max(rel_tol,abs_tol )
With
tol = Tolerance value between actual output and expected output
rel_tol = relative tolerance based on the magnitude of two numbers
abs_tol= absolute tolerance (constant)
Formula above is a standard for Python [PEP 485](https://peps.python.org/pep-0485/) for testing approximate equality
> Currently the code coverage for OpenDBM is reaching 81%
## OpenDBM Build Checking
This pipeline is to ensure the new changes that will merged to OpenDBM library still supporting one of the great things about OpenDBM, which is supporting all OS platforms. The pipeline not only ensure the installation process run without any problem in all platforms, but also to ensure the functionalities run without problem by executing the test cases in all platforms. If the one of the OS is/are failling due to new changes, it will be notified to the developer and OpenDBM team, and also generate failed badge and display it in the OpenDBM github pages
The pipeline will trigger if there is new Merge Request created.
<figure>
<img src="../docs/assets/odbm_build_checking.png" width="500" alt="OpenDBM Build Checking" />
<figcaption>ODBM Build Checking</figcaption>
</figure>
## OpenDBM Publish Release
This pipeline triggered only if the release manager create a release tag. There is also a condition in the pipeline that the release should not be draft / pre release tag. Only when official release created, it will run the pipeline that lives in `.github/workflows/open_dbm-publish-release.yml`, and publish the new version of OpenDBM library to PyPI
<figure>
<img src="../docs/assets/odbm_pypi_release.png" width="500" alt="OpenDBM PyPI release page" />
<figcaption>ODBM PyPI release page</figcaption>
</figure>
## OpenDBM Web Documentation Deployment
This pipeline is to make deployment of OpenDBM Web documentation. The configuration file is in `.github/workflows/open_dbm-docs-deploy.yml` file. You can find the details about [OpenDBM documentation here](odbm-doc).
> This pipeline only triggered manually. We can go to github actions, select OpenDBM Web Documentation Deployment, the choose the Run WorkFlow
> We need a proper Github Token that can build and push the github pages.
## Github Environment Variables
**GH_TOKEN** : Its to update the badge based on conditions defined in the pipeline. It push the new badge status if there's any status difference after pipeline. Its also used to deploy the Web Documentation to Github pages
**TWINE_PASSWORD** : Pipeline use this variable to push into the PyPI. By default the pipeline use __token__ as the username, indicates that to push to PyPI we will use token instead of credentials

View File

@@ -0,0 +1,20 @@
---
id: code-of-conduct
title: Code of Conduct
---
Alongside the release of OpenDBM v2.0, we decided to release alongside it a code of conduct as is conventional for community-owned repositories. The code of conduct is [included with the codebase](https://github.com/AiCure/open_dbm/blob/master/CODE_OF_CONDUCT.md) when OpenDBM is downloaded but weve also pasted it below.
## Commitments
In adopting this Code of Conduct, we are committed to strengthening joint efforts, innovation, and the free exchange of ideas for developing, maintaining, and using open-source software. We hope to build an active and inclusive community for development of methods for measurement of health. We want all members of this community to be active participants regardless of race, gender identity, religion, creed, color, marital status, genetic information, age, nationality, or disability.
## Values
We promote values such as being audience centered, staying calm, professional, and respectful during discussions and communicating through active listening, We encourage all members to respectfully accept evaluation and disapproval, to always aim for what is best for the community, and above all respect their fellow members.
We do not promote any kind of harassment, aggression, or intimidation. Personal threats, attacks, stalking, vulgar language, and any kind of insulting statements will not be tolerated. We do not approve of the exchange of disrespectful language, sketches, or cartoons nor the posting of personal information (emails, etc.) without user permission.
## Reporting
To report any complaint or inappropriate behavior, we ask the users to contact the moderators at [opendbm@aicure.com](mailto:opendbm@aicure.com). Moderators of the community have the right to remove offensive content that does not align with the Code of Conduct. If the content explicitly violates the Code of Conduct, it may result in disqualification from upcoming events and being blocked from the [GitHub Organization](https://help.github.com/articles/blocking-a-user-from-your-organization/)

View File

@@ -0,0 +1,14 @@
---
id: community-discussions
title: Community Discussions
---
For any and all discussions related to the use of OpenDBM, we encourage all users to host those conversations using the (as of December of 2020) new Discussions feature on Github i.e. the [Discussions](https://github.com/AiCure/open_dbm/discussions) tab on the OpenDBM Github page. While the [Issues](https://github.com/AiCure/open_dbm/issues) tab is for technical issues, we want the Discussions tab to be for everything else. Below are a couple things we want the Discussions tab to be helpful for.
## Discussions on variables
We want to foster threads in the Discussions tab that users can contribute to regarding digital biomarker variables that are clinically relevant measures in the context of individual patient populations. For example, if a user is wondering which of OpenDBMs outputted variables are relevant to, lets say, Major Depressive Disorder, we want them to be able to turn to a thread in the Discussions tab where they can find that information. Our team will be starting some of these threads ourselves and adding to them as we run into more literature; we encourage other users to also utilize the Discussions tab to create a central repository of such information.
## Experimental design
One of the more common types of questions we get from users of OpenDBM is about experimental design and data collection setups. As is briefly discussed in Chapter 6, the nature of the data collection paradigm and the behaviors that are relevant differ depending on the disease state and the goals of the experiment. Not to mentionwe are not authorities on experimental design whatsoever. If a user has such questions, we encourage them to post them in the [Discussions](https://github.com/AiCure/open_dbm/discussions) tab. We certainly will be monitoring it and responding to any questions to the best of our ability, but we also want to allow other researchers to provide their opinions, which we feel will be just as important to take into consideration.

View File

@@ -0,0 +1,21 @@
---
id: overview
title: Contributing Overview
description: How to contribute to OpenDBM
---
<!-- alex disable simple simply -->
Thank you for your interest in contributing to OpenDBM! From commenting on and triaging issues, to reviewing and sending Pull Requests, all contributions are welcome.
We aim to build a vibrant and inclusive [ecosystem of partners, core contributors, and community](https://github.com/AiCure/open_dbm/blob/main/ECOSYSTEM.md) that goes beyond the main OpenDBM GitHub repository.
The [Open Source Guides](https://opensource.guide/) website has a collection of resources for individuals, communities, and companies who want to learn how to run and contribute to an open source project.
Contributors and people new to open source alike will find the following guides especially useful:
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
- [Building Welcoming Communities](https://opensource.guide/building-community/)
We want to ensure that OpenDBM feels like a community of like-minded researchers and clinicians. Hence, there are a few ways we encourage users to stay involvedand why we [encourage you to join DiME, too!](https://www.dimesociety.org/)
Most importantly, if youre interested in OpenDBM, star the repo and [sign up for our listserv](https://docs.google.com/forms/d/e/1FAIpQLScLrgyM08YIumzJw8dxmRaMQ4byBL3J4A90FpokDhX8-KUNZw/viewform) for all updates.

View File

@@ -0,0 +1,16 @@
---
id: extras
title: Resources
---
:::info
Welcome to the Resources section! If you're getting started with OpenDBM, please refer to <a href="/docs/getting-started">Guides</a> section. Continue reading to learn about OpenDBM surrounding components! In this section, weve put together some resources that dont quite fit in any of the other chapters of this documentation but are helpful things to know regardless.
:::
This section is intended to share the surrounding components inside OpenDBM repository. The intended components includes CI/CD pipeline, REST API for demo purpose and how this documentation built. It is not a requirement to be familiar with this material to be effective with OpenDBM. You can still benefit from this section as it will give you insights into how we manage things to give added values to this repository. Feel free to share your feedback on the [discussion group](https://github.com/AiCure/open_dbm/discussions/categories/general) for this section.
## Table of Contents
- [CI CD Pipeline](ci-cd-pipeline)
- [REST API](odbm-rest-api)
- [OpenDBM Documentation](odbm-doc)

View File

@@ -0,0 +1,5 @@
---
title: Contribute Guidelines
---
With OpenDBM v2.0, weve also written out a simple version of [contribution guidelines,](https://github.com/AiCure/open_dbm/blob/master/CONTRIBUTING.md) available with the code base. We want to make these more detailed in the future. As we work with the first independent contributors to OpenDBM, we will be coming out with a much more robust packet of instructions for contributors. If you find the current guidelines confusing or have trouble contributing to OpenDBM, please definitely [reach out to us](mailto:opendbm@aicure.com) and were happy to assist.

View File

@@ -0,0 +1,6 @@
---
id: issue-reporting
title: Issue Reporting
---
For any technical issues with using the code i.e. during installation and during use, we ask that youinstead of emailing usbring up the issue using the Issues feature on Github i.e. in the [Issues](https://github.com/AiCure/open_dbm/issues) tab on the OpenDBM github, We have written out an [Issue template](https://github.com/AiCure/open_dbm/blob/master/.github/ISSUE_TEMPLATE/bug_report.md) that will automatically populate when you create a new issue; we suggest you follow that template.

View File

@@ -0,0 +1,12 @@
---
id: license-contact
title: License and Contact
---
## License
OpenDBM is published under the [AGPL v3.0 License.](https://github.com/AiCure/open_dbm/blob/master/license.txt)
## Contact
For technical issues, we ask that you use the [Issues](https://github.com/AiCure/open_dbm/issues) tab to contact us. For any general discussions, we ask that you please use the [Discussions](https://github.com/AiCure/open_dbm/discussions) tab. If youd like to contact us privately and directly, you can email us at [opendbm@aicure.com.](mailto:opendbm@aicure.comopendbm@aicure.com)

View File

@@ -0,0 +1,123 @@
---
id: odbm-doc
title: OpenDBM Documentation
---
## Summary
As book is the gate to knowledge, OpenDBM documentation is the gate to all the great features provided in this library! The OpenDBM team primary focus is how to make this library easily accessible, installable and usable in a very simple and straightforward as much as possible, and good documentation is the key to our objectives.
## Github documentation
<figure>
<img src="../docs/assets/odbm_github_doc.png" width="500" alt="OpenDBM Github documentation" />
<figcaption>OpenDBM Github documentation</figcaption>
</figure>
We want our github documentation as concise as possible. In here you can find the important information such as:
* The latest OpenDBM version in PyPI
* Unit test results which are coming from OpenDBM Code Checking Github Actions pipeline
* The code coverage that also produced by above process
Along with those informations, you can also see the OS build and test status on our documentation which produced from OpenDBM Build Checking Github Actions pipeline. This way you can check whether the last version of OpenDBM library can support which platforms
### Basic usage and other informations
We also provide a section how to install and use OpenDBM library inside our github page. But of course more detailed explanation are provided in OpenDBM Web documentation which you're probably looking right now
### TOC Table of content
We are using [DocToc](https://github.com/thlorenz/doctoc) in order to generate our table of contents. So dont change the table of content at all. If you want to add anything, just add it like a normal way, and you can use `## My Section` if you feel need to add new section for your information. After that, just generate ```doctoc .``` in the root folder and DocToc will generate the ToC immediately and include your new information seamlessly.
<figure>
<img src="../docs/assets/odbm_doctoc.png" width="500" alt="OpenDBM Github Table of Content Generator" />
<figcaption>OpenDBM Github Table of Content Generator</figcaption>
</figure>
## Web Documentation
By good probability when you read this section, you're currently looking at our web documentation. (Unless you read this from our github blob files which is still normal, no worries!)
> We are using [Docusaurus](https://docusaurus.io/) to generate this documentation as our primary objective is focus on the content and how to deliver the most straightfoward information to the community. So all the neccessary code to build this web documentation are coming from Docusaurus.
## Documentation sections
### Dashboard
When you first arrived to the OpenDBM Web documentation, you will see the concise description about what is OpenDBM and Why to use it. We also provide talks and videos that provide informations about this library. We also put the acknowledgements to those libraries that make everything is possible in this OpenDBM library
### Getting Started
This section provide informations on everything you need to know to install and use the OpenDBM. It explains about the prerequisites before you install the OpenDBM. It makes sure to leave no one behind as we provide the informations to all OS platforms.
<figure>
<img src="../docs/assets/odbm_allOs.png" width="500" alt="OpenDBM Documentation to all platforms" />
<figcaption>OpenDBM Documentation to all platforms</figcaption>
</figure>
### Variables
This section provide deeper informations about OpenDBM. There are a lot of variables you can fetch from OpenDBM, and this section provide you how to use those variables and what is the limitation on each modules. We also make sure to make annotation or reference to acknowledge other people contributions to make all this possible
### API
One of all thing about the OpenDBM generation 2 is how easy it is to use OpenDBM. We put another layer on top of the previous OpenDBM library to make it easier to use OpenDBM. All those APIs informations are provided within this section. This section is automatically generated by [pydoc](https://docs.python.org/3/library/pydoc.html) command line that executed in `.github/workflows/open_dbm-docs-deploy.yml`
<figure>
<img src="../docs/assets/pydoc-pic.png" width="100%" alt="OpenDBM Pydoc Generated Document" />
<figcaption>OpenDBM Pydoc Generated Document</figcaption>
</figure>
### Resources
This section provide all informations surrounding the OpenDBM environment. It gives you information about the pipeline, REST API, Documentation (Hi, there!) and contributing guideines.
### Blog
Within this section, you can read other people perspectives, stories and bunch of interesting stuff about OpenDBM all around the world!
## OpenDBM Web Technical Documentation
Below section will discuss in more detail about the technical aspect of OpenDBM Web documentation structure.
### How to install and run locally
Your node must be set to stable version (as of now version 16) to be able to install and run this documentation
Under the docs directory:
* ```bash yarn``` to install all the dependencies
* Then go to the the `website` directory and run the app by typing:
* Command below is for start the website for the **first time**.
```bash
cd website
pip install pydoc-markdown
pydoc-markdown -I ../opendbm/api_lib/facial_activity -m api --render-toc > website/api/facial-activity-api.md
pydoc-markdown -I ../opendbm/api_lib/movement -m api --render-toc > website/api/movement-api.md
pydoc-markdown -I ../opendbm/api_lib/verbal_acoustics -m api --render-toc > website/api/verbal-acoustics-api.md
pydoc-markdown -I ../opendbm/api_lib/speech -m api --render-toc > website/api/speech-api.md
yarn start
```
* This command is the one you execute if you have already generated dynamic documentation from pydoc-markdown
```bash
cd website && yarn start
```
### Dashboard
The dashboard page is build on top of React framework. You need only a basic React knowledge in order to change stuff in the dashboard.
### Main config file
The main config file of OpenDBM documentation is docusaurus.config.js. You can change things like:
* You can set the footer content
* You can set the `editUrl`. This parameter to define the url when you or other community members want to change this file
<figure>
<img src="../docs/assets/odbm_edit_button.png" width="300" alt="OpenDBM Edit this page button" />
<figcaption>OpenDBM Edit this page button</figcaption>
</figure>
* You can set the url, baseUrl
* in `presets[0][0]` you can set the sidebar file to show in the `getting-started` section. We can also set the limit number of versions that will be displayed in the documentation in this line `['current', ...versions.slice(0, 2)]` We will discuss about versioning deeper below
> Support too many versions will make the web deployment much slower.
* Still in the same above parameter, we can also sync the google analytic tag in `googleAnalytics.trackingID` parameter.
* in `plugins` we also can set the siderbar file to be shown in other section as well
### Versioned pages
This documentation support versioning on the library as it very important that we could provide information and retain it so community can refer to specific version for their usecase. The versions are listed under `docs/website/versions.json`. The latest version must be the first element in that array.
That versions will be referred and used in these two directories `versioned_docs` and `versioned_sidebars`. So you can edit the documentation in specific version within these directories.
Those files and sidebars file also has duplicated contents in `sidebars.json` and files under directories `docs/docs`. These files are meant for future version. So if the community using **Edit This Page** button in the pages, it will create the MR for changes related to this directory. So you can say its a temporary directory for future changes on the documentations. Later, when you create future `version-x.x` version, you can just copy paste all the files under `docs/docs` to the new version directory.
### Non Versioned pages
Right now, the non versioned pages are API, Resources, Blog. It has its own directories under docs/website. If you need new section, you can create the directory and define it in `docusaurus.config.js`.
### Navigation (Sidebar and page ID)
In the `version-xx-sidebars.json`, you can define the sidebar and the IDs of pages that you want to categorize. You can set the id inside the page i.e.
```
id: action-units
title: Actions Units
```

View File

@@ -0,0 +1,206 @@
---
id: odbm-rest-api
title: OpenDBM REST API
---
import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import constants from '@site/core/TabsConstants';
## Summary
Inside the OpenDBM repository, there is a folder named rest_api. This folder means to showcase that you can leverage OpenDBM library in API service or Web application, which this folder will showcase the former.
> The OpenDBM API designed with OpenAPI 2.0 to standardize API definition for programmatic usage, better developer experience and documentation. It will also protected by standard security protocol.
<figure>
<img src="../docs/assets/odbm_api_summary.png" width="500" alt="OpenDBM REST API Summary" />
<figcaption>ODBM REST API Summary</figcaption>
</figure>
## How to use
To use the Rest API is very straighforward
* Go to the `rest_api` directory
* Install the neccessary dependencies
```commandline
pip install -r requirements.txt
docker pull opendbmteam/dbm-openface
```
* Then you can use Make syntax to run it
```commandline
make dev
```
Wait until the rest api ready to serve requests
```bash
INFO: Will watch for changes in these directories: ['/Users/smarty_not_dummy__user/open_dbm/rest_api/app']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [80258] using StatReload
INFO: Started server process [80275]
```
* You can test using the OpenAPI specs like mentioned in [here](ci-cd-pipeline#unit-tests). You can open `http://127.0.0.1:8000/docs` or `http://127.0.0.1:8000/redoc`.
* Before you start using the APIs, you need to create your credentials for authentication. Alternatively, you can try to use username 'aicure' and the password is 'opendbm'.
> You can skip below steps if you want to use 'aicure' credential
* To create your own username, first you generate your own password by following these steps:
```bash
cd rest_api/app
python
Python 3.7.13 (default, Mar 28 2022, 07:24:34)
[Clang 12.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
```
Then you can start type your own password using this python command
```python
from services.auth.auth import get_password_hash
print(get_password_hash('test'))
$2b$12$qYnAxjQd..feDyPBYa/mDuRrH.PkcpNHJk3F.l/c0h3l/1NcZup7O
```
Save the password in the last line and your preferred username in app/services/auth/auth.py
```python
def get_db():
fake_users_db = {
"your_new_username": {
"username": "your_new_username",
"full_name": "New Full name",
"email": "new email",
"hashed_password": "$2b$12$qYnAxjQd..feDyPBYa/mDuRrH.PkcpNHJk3F.l/c0h3l/1NcZup7O ",
"disabled": False,
},
"aicure": {
"username": "aicure",
"full_name": "AiCure OpenDBM",
"email": "opendbm@aicure.com",
"hashed_password": "$2b$12$k4R5SPuHkjFKBsQV5gAHl.e/BlxrX2z1H3vxiB9uGtaDZLFXjggCm",
"disabled": False,
},
"alice": {
"username": "alice",
"full_name": "Alice Wonderson",
"email": "alice@aicure.com",
"hashed_password": "fakehashedsecret2",
"disabled": True,
},
}
return fake_users_db
```
* After that you can login using either of these icons, or using the curl alternative
<Tabs
defaultValue="docs"
values={[
{label: 'API Docs', value: 'docs'},
{label: 'cUrl', value: 'curl'},
]}>
<TabItem value="docs">
<figure>
<img src="../docs/assets/opendbm_auth_symbol.png" width="700" alt="OpenDBM Auth" />
<figcaption>ODBM Auth</figcaption>
</figure>
</TabItem>
<TabItem value="curl">
```bash
curl 'http://127.0.0.1:8000/odbm/v1/login' \
-H 'Accept: application/json, text/plain, */*' \
-H 'Authorization: Basic Og==' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-raw 'grant_type=password&username=aicure&password=opendbm' \
```
</TabItem>
</Tabs>
* Start with uploading the files, put the "local" string value in the `platform` parameter, then save the upload file name.
<Tabs
defaultValue="docs"
values={[
{label: 'API Docs', value: 'docs'},
{label: 'cUrl', value: 'curl'},
]}>
<TabItem value="docs">
<figure>
<img src="../docs/assets/opendbm_upload_file.png" width="700" alt="OpenDBM File Uploading" />
<figcaption>ODBM File Uploading</figcaption>
</figure>
</TabItem>
<TabItem value="curl">
```bash
curl 'http://127.0.0.1:8000/odbm/v1/upload?file_name=facial_speech_verbal_video_test.mp4&file_extension=mp4&platform=local' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaWN1cmUiLCJleHAiOjE2NjI5MDM4MjB9.oYW3z44NdcBLZswxy4Vt1S2r92wYmus59NWnZYDr2CA' \
-H 'Content-Type: multipart/form-data; ' \
-H 'accept: application/json' \
--data-raw $'Content-Disposition: form-data; name="file"; filename="facial_speech_verbal_video_test.mp4" Content-Type: video/mp4 \
--compressed
```
</TabItem>
</Tabs>
The API response will be
```json
{
"info": "file 'facial_speech_verbal_video_test.mp4' saved at 'files/facial_speech_verbal_video_test.mp4'"
}
```
Save the file location returned somewhere `files/facial_speech_verbal_video_test.mp4`
* Then you can use any API you want, insert the previous upload file url into `file_url` parameter. Then you can specify variables you want to get. For example, if you use the `/video/acoustic` API, you can specify the request body like
```json
[
"audio_intensity",
"pitch_frequency"
]
```
<Tabs
defaultValue="docs"
values={[
{label: 'API Docs', value: 'docs'},
{label: 'cUrl', value: 'curl'},
]}>
<TabItem value="docs">
<figure>
<img src="../docs/assets/opendbm_acoustic.png" width="700" alt="OpenDBM Test Acoustic API" />
<figcaption>ODBM Test Acoustic API</figcaption>
</figure>
</TabItem>
<TabItem value="curl">
```bash
curl 'http://127.0.0.1:8000/odbm/v1/video/acoustic?file_url=files%2Ffacial_speech_verbal_video_test.mp4&platform=local' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaWN1cmUiLCJleHAiOjE2NjI5MDkwMzd9.RQgtXhPCcgzctfuNg2GbByR9OMdr1vWTerl1iKPWqfk' \
-H 'Content-Type: application/json' \
-H 'accept: application/json' \
--data-raw $'[\n "audio_intensity",\n "pitch_frequency"\n]' \
--compressed
```
</TabItem>
</Tabs>
After you execute the API, you will get the zip file, with the csv results compressed inside that file
> You can check more details on rest_api usage in `rest_api/README.md`
## Technology Stack
<figure>
<img src="../docs/assets/odbm_tech_stack.png" width="500" alt="OpenDBM REST API Technology Stack" />
<figcaption>ODBM REST API Technology Stack</figcaption>
</figure>
The OpenDBM API will use FastAPI framework. The main reason is because the asynchronous behaviour of FastAPI will works perfectly to complex and high performance methods like data upload, data pre / processing and other possible long operations.
We also use Uvicorn is an ASGI web server implementation for Python. Until recently Python has lacked a minimal low-level server/application interface for async frameworks. The ASGI specification fills this gap, and means we're now able to start building a common set of tooling usable across all async frameworks. Uvicorn currently supports HTTP/1.1 and WebSockets.
## API Documentations
The Rest API documentations can be found when you run the the app. Change the relative path to /docs or /redoc
<figure>
<img src="../docs/assets/odbm_api_docs.png" width="500" alt="OpenDBM REST API Swagger API Docs" />
<figcaption>ODBM REST API Swagger API Docs</figcaption>
<img src="../docs/assets/odbm_redocs.png" width="500" alt="OpenDBM REST API Redocs" />
<figcaption>ODBM REST API Redocs</figcaption>
</figure>

View File

@@ -0,0 +1,8 @@
---
id: opendbm-listserv-signup
title: OpenDBM Listserv signup
---
[Use this link to sign up for the OpenDBM Listserv and stay up to date on release and events.](https://docs.google.com/forms/d/e/1FAIpQLScLrgyM08YIumzJw8dxmRaMQ4byBL3J4A90FpokDhX8-KUNZw/viewform)
From time to time, we will be making either small updates to OpenDBM functionalities (additional variables, bug fixes, etc.) or large version releases with major additions to features (e.g., we want a future update to OpenDBM to include analysis of wearable data). We will also be hosting virtual (and hopefully one day in-person) events for the OpenDBM user community that allow for training, introduction to new functionalities, presentations on prominent projects, etc. In either of these cases, wed like to keep the user community up to date so that they dont miss out on these updates or events, so we do encourage you to [sign up for the listserv.](https://docs.google.com/forms/d/e/1FAIpQLScLrgyM08YIumzJw8dxmRaMQ4byBL3J4A90FpokDhX8-KUNZw/viewform)

View File

@@ -0,0 +1,164 @@
---
id: opendbm-python-api-add-new-variable
title: OpenDBM API - Add New Variable
---
#### Prerequisites read
* [OpenDBM Python API](opendbm-python-api)
* [OpenDBM API - Unit Test](opendbm-python-api-unittest)
<figure>
<img src="../img/extras/api-add-var-5.png" width="70%" alt="OpenDBM Folder" />
<figcaption>Simple Architecure Diagram on How to Add New Module</figcaption>
</figure>
## Scenario
We want to add a pose detector in OpenDBM API. The model file that we will use to do the pose detector is 2 GB in size. Here is the steps on how to do it
### 1. Consider the category of pose detector
We have existing dbm group folders that consist of Facial, Movement, Speech, and Verbal Acoustics.
Compared to other categories, it will make sense if the pose detector is added to the Movement group. So with that in mind,
we will create the core module in the movement folder, to be precise, in **“dbm_lib/dbm_features/raw_features/movement”**.
For the module name, there is no convention on how to create the module name. The only important thing is it needs to be explicit.
In this case, we will name our module **pose_detection.py**
### 2. Create the core module
In general, this module is where we use the model to generate the final output as a pandas dataframe object.
Specifically, The final function of the pose_detection module needs to have two capabilities:
1. Keyword argument for saving the output.
This is essential because you need the module to be used with the Docker command approach. To develop this,
please use the save_output function from the util library so you dont have to create the same function.
2. Should return the final output as pandas dataframe.
In code, the end of the final function should look like this.
<figure>
<img src="../img/extras/api-add-var-1.png" width="100%" alt="OpenDBM Folder" />
<figcaption>End Code of eye_blink Module in Movement dbm group</figcaption>
</figure>
### 3. Store the Model File
Now store your model file in the pkg/local folder, depending on the file size (see pkg folder explanation here). You
also need to create a function where openDBM API can download the model file if the user doesnt have it yet.
The function must be made in the **util.py** module in the api_lib folder.
### 4. Add pose_detection to Python API
Below are the steps to add your newly created pose_detection module to Python API that lives inside api_lib folder.
#### a. Create the private module.
Based on our scenario, we should create “_pose_detection.py” in the movement folder inside api_lib
#### b. Create a class inside the private module
If we look at all the private modules, the code has the same pattern as others. So all we need to do is to modify it a little bit.
Below is the code inside “_eye_blink.py” inside the movement folder.
<figure>
<img src="../img/extras/api-add-var-2.png" width="100%" alt="_eye_blink.py" />
<figcaption>_eye_blink.py</figcaption>
</figure>
As a starter, we copy-paste all codes from “_eye_blink.py” to “_pose_detection.py”. Then in;
* Line 1: change DLIB_SHAPE_MODEL to POSE_DETECTION_MODEL. This object is a path to where the Pose detector model lives.
* Line 2: change the import module from eye_blink to pose_detection, and also change run_eye_blink to your final function name (ex: run_pose_detection)
* Line 5: Change the class name from EyeBlink to PoseDetection
* Line 8: create a list that contains the name of the numerical field of your processed dataframe.
* Line 11: change the _fit_transform method to return the result of your function, in this case, run_pose_detection(args1,args2, …)
The final codes in **_pose_detection.py** should be look like this, for example;
```python
from opendbm.api_lib.model import POSE_DETECTION_MODEL, VideoModel
from opendbm.dbm_lib.dbm_features.raw_features.movement.pose_detection import run_pose_detection
class PoseDetection(VideoModel):
def __init__(self):
super().__init__()
self._params = ["mov_pose_head", "mov_pose_body", "mov_pose_hand", "mov_pose_foot"]
def _fit_transform(self, path):
return run_pose_detection(path, ".", self.r_config, POSE_DETECTION_MODEL, save=False)
```
#### c. Export the newly created module to api.py
Now we go and take a look at codes in module api.py. After that, all we have to do is add pose_detection with the following steps:
#### Import the private pose module
Following the pattern, we just need to add **from ._pose_detection import PoseDetection**
```python
from ._eye_blink import EyeBlink
from ._eye_gaze import EyeGaze
from ._facial_tremor import FacialTremor
from ._head_movement import HeadMovement
from ._vocal_tremor import VocalTremor
from ._pose_detection import PoseDetection # <-- new code added for pose_detection
```
#### Initiate the model class
Following the pattern, create a new variable in a new line **self._pose_detection = PoseDetection()**
```python
class Movement(VideoModel):
def __init__(self):
super().__init__()
self._eye_blink = EyeBlink()
self._eye_gaze = EyeGaze()
self._facial_tremor = FacialTremor()
self._head_movement = HeadMovement()
self._vocal_tremor = VocalTremor()
self._pose_detection = PoseDetection() # <-- new code added for pose_detection
```
#### Add the new pose variable to models dictionary.
```python
self._models = OrderedDict(
{
"eye_blink": self._eye_blink,
"eye_gaze": self._eye_gaze,
"facial_tremor": self._facial_tremor,
"head_movement": self._head_movement,
"vocal_tremor": self._vocal_tremor,
"pose_detection": self._pose_detection # <-- new code added for pose detection
}
)
```
#### Create a new method to get the result of pose_detection.
By following the function pattern in the code below, our function name will be **get_pose_detection**, which will return **self._pose_detection**
```python
def get_vocal_tremor(self):
"""
Get the model object of Vocal Tremor
Returns:
self: object
Model Object
"""
return self._vocal_tremor
### Below is the new code to get the result of pose_detection
def get_pose_detection(self):
"""
Get the model object of Pose Detection
Returns:
self: object
Model Object
"""
return self.pose_detection
```
That is how to add a new component to OpenDBM API. The only leftover is,
### 5. Add pose_detection to unit test (pytest).
Considering you have already read the Unit test design, you need to create a function to test your newly created API.
In our case, in “test_api_movement.py,” we will create a test function named “test_get_pose_detection,” and you should configure your unit testing there.

View File

@@ -0,0 +1,34 @@
---
id: opendbm-python-api-unittest
title: OpenDBM API - Unit Test
---
#### Prerequisites read
* [OpenDBM Python API](opendbm-python-api)
The framework we use to do unit testing is pytest. We use pytest because we want our test design to be very similar to our API design and make the unit test easier to understand. Below is the general architecture of the tests folder.
<figure>
<img src="../img/extras/api-test-docs-1.png" width="100%" alt="Unit Tests Folder" />
<figcaption>Unit Tests Folder</figcaption>
</figure>
The picture above contains the test folder containing the test_data folder that will store many test files.
All the files here will be used as test data during our unit testing.
Next to the test_data folder is conftest.py, the pytest configuration that will be applied to all the test scripts. This file's purpose is to initiate all the API, so we don't have to import it into our test script.
Lastly, we have dbm group folders (facial, movement, speech, verbal acoustics).
Each of these folders consistently has the same structure, which consists of the following:
### 1. Test Script
All test scripts file that has the prefix “test_” is where we create all our unit tests, respective to the folder.
(ex: test script inside the facial folder should only have unit test related to the facial API)
### 2. Configuration Test
This file named conftest.py in each folder will configure how many test data files you want to add to the test script.
If you're going to add another test data, you should create a new function inside this file to feed it into the test script.

View File

@@ -0,0 +1,101 @@
---
id: opendbm-python-api
title: OpenDBM Python API
---
When we use OpenDBM python API, all the source codes come from the opendbm folder in our GitHub repo. opendbm folder is the main
package we use to process input data. opendbm has four folders that consist of api_lib, dbm_lib, pkg, and resources.
To make it clear in the beginning, you will know that our structure in our repo reflects what is already documented in <a href="../docs/biomaker-variables">Variables</a> Section.
<figure>
<img src="../img/extras/api-docs-1.png" width="100%" alt="OpenDBM Folder" />
<figcaption>OpenDBM Folder</figcaption>
</figure>
### 1. dbm_lib
<figure>
<img src="../img/extras/api-docs-2.png" width="100%" alt="dbm_lib Folder" />
<figcaption>dbm_lib folder @ dbm_lib/dbm_features/raw_features</figcaption>
</figure>
When we mention dbm_lib for python API documentation, we refer to the folder “dbm_lib/dbm_features/raw_features.”
dbm_lib is where all core modules are implemented. All the processing components must be stored here because this
folder is where we can use the components either with the Docker command or Python API
Each children's folder (audio, movement, nlp, and video) represents the dbm group. In every children folder,
there will be modules where we perform the processing/inferring of our input data.
For example, below are the modules that live inside the movement folder.
<figure>
<img src="../img/extras/api-docs-3.png" width="100%" alt="movement Folder in dbm_lib" />
<figcaption>movement Folder in dbm_lib</figcaption>
</figure>
The goal of each module here is to perform one task/processing. For instance, facial_tremor.py
will have a functionality to detect the facial tremor of the input data. If we want to add another functionality
in openDBM, for example, motion detection, we should create a new module in this folder (assumed motion detection
is part of movement dbm_group).
The design we explained for the movement package also applied to other dbm groups such as video, nlp, and audio.
Lastly, in the same level of the dbm group folder, there is the util folder, which contains all utility functions used
by all modules that need it. If we want to create/modify the modules in dbm_lib, please check all the utility
functions first so we don't need to reinvent the wheel.
### 2. api_lib
<figure>
<img src="../img/extras/api-docs-4.png" width="100%" alt="api_lib Folder" />
<figcaption>api_lib Folder</figcaption>
</figure>
api_lib is a supporting package created so that we can use openDBM core modules with the python API call.
Looking in the api_lib folder, we can see four folders (facial_activity, movement, speech, verbal_acoustics),
which represent the dbm group, the same as we saw in the dmb_lib folder. In the same level of the dbm group folder,
there are also two modules that have functionality as follows:
#### a. model.py
This module has the functionality to create the base model object that can be used after we execute the fit method
in our Python API. This module also has a class to perform a processing/fit method in a dbm_group level.
#### b. util.py
This module is where all the utility functions are stored, related to the api_lib folder.
<figure>
<img src="../img/extras/api-docs-5.png" width="100%" alt="movement Folder in api_lib" />
<figcaption>movement Folder in api_lib</figcaption>
</figure>
All the dbm group folders inside api\_lib consistently have the same design as other folders.
If we look in the movement folder, we can see all the private modules (with the prefix “\_”) that have
the same name/structure as the one in the dbm_lib folder.
All these private modules will execute the same as their respective modules in the dbm_lib folder. For example, module “_vocal_tremor.py” will execute processing “vocal_tremor.py”. The difference is that in these private modules, we assign the function to the class that will be used for our python API.
The “api.py” module is a module where we implement the python API call tailored to each of the dbm_group.
### 3. pkg
is where the model files are stored. In most of our processing/inferring, we get the weight from this pkg folder.
<b>Note for the developer</b>; if you want to make the user download a new model file, you can direct the download path to the pkg folder. But if the model file is big (ex: 2GB), please consider directing the download path to their local path. The reason is users might try to use OpenDBM in many virtual environments. Their memory storage will be exhausted if we store large model files in the pkg folder in many virtual environments.
### 4. resources
This folder is created initially as a configuration to rename, clean, and structure OpenFace output. To make it consistent, if we need to add other configurations for processing a new model, please create it in this folder.

View File

@@ -0,0 +1,8 @@
---
id: publications-and-referencing
title: Publications and Referencing
---
As the user community grows, we hope and are anticipating scientific publications whose methods depend partially or entirely on methods compiled into OpenDBM. For the authors of those publications, we request that they reference OpenDBM in their work. We are (as of December of 2020) preparing a manuscript that can be referenced. Meanwhile, we ask that authors at the very least reference the Github repository when mentioning OpenDBM.
Additionally, we are hoping to maintain an up to date list of publications that have used methods from OpenDBM when reporting their findings. This live list of publications is linked to from many different places around the Github repository and this documentation, but [here is another link to it](https://docs.google.com/spreadsheets/u/2/d/1pRBWCCFMbEgZNQzm2Litm3RUQ6glwUwswrqGDePMvh0/edit#gid=0). If you are a user of OpenDBM and have used its methods in work that has either been presented at a conference or submitted to a journal, we ask that you please submit a citation of the work to us [using this form](https://forms.gle/Hb6bDL1GJvG1ByUX7) so that we may append the list of publications with your work.

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) AiCure, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const glob = require('glob-promise');
const fs = require('fs-extra');
const path = require('path');
const siteConfig = require('./docusaurus.config.js');
const imageReferenceRegExp = new RegExp(/!\[.*?\]\((.*)\)/g);
let missingAssets = [];
let queue = Promise.resolve();
glob('./docs/**/*.md')
.then(files => {
files.forEach(file => {
queue = queue
.then(() => {
return fs.readFile(file);
})
.then(contents => {
let matches;
while ((matches = imageReferenceRegExp.exec(contents))) {
const pathToFile = path.join(
'./',
matches[1].replace(siteConfig.baseUrl, '')
);
missingAssets.push({imagePath: pathToFile, markdownPath: file});
}
});
});
return queue;
})
.then(() => {
queue = Promise.resolve();
missingAssets.forEach(missingAsset => {
const {imagePath, markdownPath} = missingAsset;
queue = queue
.then(() => {
return fs.stat('./static/' + imagePath);
})
.then(stats => {})
.catch(e => {
console.error(
'Could not find ' +
'static/' +
imagePath +
' which has at least one reference in ' +
markdownPath +
". Did you forget to add the asset to '/static/docs/assets'?"
);
});
});
return queue;
});

63
docs/website/package.json Normal file
View File

@@ -0,0 +1,63 @@
{
"name": "opendbm-website",
"description": "OpenDBM website",
"version": "0.0.1",
"private": true,
"license": "CC-BY-4.0",
"homepage": "https://teebid.github.io/aicure_opendbm/",
"repository": "github:teebid/aicure_opendbm",
"bugs": {
"url": "https://github.com/AiCure/open_dbm/issues"
},
"scripts": {
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"serve": "docusaurus serve",
"clean": "docusaurus clean",
"test": "yarn build",
"version:cut": "docusaurus docs:version",
"format:source": "prettier --write {{core,src}/**/*.js,*.js}",
"format:markdown": "prettier --write ../docs/*.md && prettier --write {{versioned_docs,src}/**/*.md,blog/*.md}",
"format:style": "prettier --write src/**/*.{scss,css}",
"prettier": "yarn format:source && yarn format:markdown && yarn format:style",
"lint": "eslint ../docs/** blog/** core/** src/**/*.{js,md} ./*.js",
"lint:versioned": "eslint versioned_docs/**",
"language:lint": "cd ../ && alex && case-police 'docs/*.md' -p brands,general,products,softwares",
"language:lint:versioned": "cd ../ && alex . && case-police '**/*.md' -p brands,general,products,softwares",
"ci:lint": "yarn lint && yarn lint:versioned && yarn language:lint:versioned",
"pwa:generate": "npx pwa-asset-generator ./static/img/header_logo.png ./static/img/pwa --padding '40px' --background 'rgb(32, 35, 42)' --icon-only --opaque true"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"dependencies": {
"@docusaurus/core": "^2.0.0-beta.21",
"@docusaurus/plugin-pwa": "^2.0.0-beta.21",
"@docusaurus/preset-classic": "^2.0.0-beta.21",
"docusaurus-plugin-sass": "^0.2.2",
"esbuild-loader": "^2.18.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-github-btn": "^1.2.2",
"sass": "^1.49.11"
},
"devDependencies": {
"@docusaurus/types": "^2.0.0-beta.21",
"alex": "^9.1.1",
"fs-extra": "^10.0.1",
"glob": "^7.2.0",
"glob-promise": "^4.2.2",
"path": "^0.12.7"
}
}

View File

@@ -0,0 +1,29 @@
[
{
"name": "OpenFace",
"icon": "openface.png",
"linkAppStore": "https://cmusatyalab.github.io/openface/",
"linkPlayStore": "https://cmusatyalab.github.io/openface/",
"infoLink": "https://cmusatyalab.github.io/openface/",
"infoTitle": "Free and open source face recognition with deep neural networks.",
"pinned": true
},
{
"name": "Parselmouth",
"icon": "parselmouth.png",
"linkAppStore": "https://parselmouth.readthedocs.io/en/stable/",
"linkPlayStore": "https://parselmouth.readthedocs.io/en/stable/",
"infoLink": "https://parselmouth.readthedocs.io/en/stable/",
"infoTitle": "Parselmouth is a Python library for the Praat software.",
"pinned": true
},
{
"name": "DeepSpeech",
"icon": "deepspech.png",
"linkAppStore": "https://deepspeech.readthedocs.io/en/r0.9/",
"linkPlayStore": "https://deepspeech.readthedocs.io/en/r0.9/",
"infoLink": "https://deepspeech.readthedocs.io/en/r0.9/",
"infoTitle": "DeepSpeech is an open source Speech-To-Text engine, using a model trained by machine learning techniques based on Baidus Deep Speech research paper",
"pinned": true
}
]

117
docs/website/sidebars.json Normal file
View File

@@ -0,0 +1,117 @@
{
"docs": [
{
"type": "category",
"label": "The Basics",
"collapsed": false,
"collapsible": true,
"items": [
"getting-started",
"dependencies-installation",
"beginner-installation",
"pro-installation",
"more-resources"
]
},
{
"type": "category",
"label": "Using Docker OpenDBM",
"collapsed": false,
"collapsible": true,
"items": ["opendbm-docker-usage", "windows-usage"]
},
{
"type": "category",
"label": "OpenDBM Output",
"collapsed": false,
"collapsible": true,
"items": ["opendbm-docker-output", "raw-variables"]
}
],
"variable": [
{
"type": "category",
"label": "Biomaker Variables",
"collapsed": false,
"collapsible": true,
"items": [
"biomaker-variables",
{
"type": "category",
"label": "Facial Activity",
"collapsed": false,
"collapsible": true,
"items": [
"facial-activity",
"facial-landmark",
"action-units",
"emotional-expressivity",
"overall-expressivity",
"facial-asymmetry",
"pain-expressivity"
]
},
{
"type": "category",
"label": "Verbal Acoustic",
"collapsed": false,
"collapsible": true,
"items": [
"verbal-acoustic",
"fundamental-frequency",
"formant-frequencies",
"audio-intensity",
"harmonics-to-noise-ratio",
"glottal-to-noise-excitation-ratio",
"jitter",
"shimmer",
"pause-characteristics",
"voice-prevalence"
]
},
{
"type": "category",
"label": "Speech",
"collapsed": false,
"collapsible": true,
"items": [
"speech",
"parts-of-speech",
"sentiment-of-speech",
"lexical-richness",
"rate-of-speech"
]
},
{
"type": "category",
"label": "Movement",
"collapsed": false,
"collapsible": true,
"items": [
"head-movement",
"eye-blink-behavior",
"eye-gaze-directionality",
"facial-tremor",
"vocal-tremor"
]
}
]
}
],
"guidelines": [
{
"type": "category",
"label": "Data Guidelines",
"collapsed": false,
"collapsible": true,
"items": [
"data-guidelines",
"supported-file-types",
"video-guidelines",
"audio-guidelines",
"behavioral-considerations",
"amount-data-needed"
]
}
]
}

View File

@@ -0,0 +1,36 @@
{
"api": [
{
"type": "category",
"label": "OpenDBM API",
"collapsed": false,
"collapsible": true,
"items": [
{
"id": "api-doc",
"type": "doc"
},
{
"id": "facial-activity-api",
"type": "doc",
"label": "Facial Activity"
},
{
"id": "movement-api",
"type": "doc",
"label": "Movement"
},
{
"id": "verbal-acoustics-api",
"type": "doc",
"label": "Verbal Acoustics"
},
{
"id": "speech-api",
"type": "doc",
"label": "Speech"
}
]
}
]
}

View File

@@ -0,0 +1,20 @@
{
"contributing": [
{
"type": "category",
"label": "Contributing to OpenDBM",
"collapsed": false,
"collapsible": true,
"items": [
"overview",
"code-of-conduct",
"how-to-contribute-code",
"issue-reporting",
"community-discussions",
"publications-and-referencing",
"opendbm-listserv-signup",
"license-contact"
]
}
]
}

View File

@@ -0,0 +1,32 @@
{
"extras": [
{
"type": "category",
"label": "Resources",
"collapsed": false,
"items": ["extras",
"ci-cd-pipeline",
"odbm-rest-api",
"odbm-doc",
"opendbm-python-api",
"opendbm-python-api-unittest",
"opendbm-python-api-add-new-variable"]
},
{
"type": "category",
"label": "Contributing",
"collapsed": false,
"collapsible": true,
"items": [
"overview",
"code-of-conduct",
"how-to-contribute-code",
"issue-reporting",
"community-discussions",
"publications-and-referencing",
"opendbm-listserv-signup",
"license-contact"
]
}
]
}

View File

@@ -0,0 +1,87 @@
/**
* Copyright (c) AiCure, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
export default (() => {
if (!ExecutionEnvironment.canUseDOM) {
return null;
}
const updateSnacksTheme = () => {
const theme = document.querySelector('html').dataset.theme;
document.querySelectorAll('.snack-player').forEach(snack => {
snack.dataset.snackTheme = theme;
});
};
const initSnackPlayers = () => {
// console.log('initSnackPlayers');
updateSnacksTheme();
window.ExpoSnack && window.ExpoSnack.initialize();
};
const setupTabPanelsMutationObservers = () => {
const tabPanels = document.querySelectorAll('[role=tabpanel]');
// console.log('setupTabPanelsMutationObservers', {tabPanels});
tabPanels.forEach(tabPanel => {
new MutationObserver((mutations, observer) => {
initSnackPlayers();
// console.log('tabPanel MutationObserver triggered', {tabPanels});
}).observe(tabPanel, {childList: true});
});
};
if (document.readyState === 'complete') {
updateSnacksTheme();
setupTabPanelsMutationObservers();
} else {
document.addEventListener('readystatechange', () => {
if (document.readyState === 'complete') {
updateSnacksTheme();
setupTabPanelsMutationObservers();
}
});
}
// Reset snack iframes on theme changes to sync theme
// Hacky, but no better solution for now
// see https://github.com/expo/snack/blob/main/website/src/client/components/EmbedCode.tsx#L61
const setupThemeSynchronization = () => {
new MutationObserver((mutations, observer) => {
if ('ExpoSnack' in window) {
document.querySelectorAll('.snack-player').forEach(container => {
updateSnacksTheme();
window.ExpoSnack && window.ExpoSnack.remove(container);
window.ExpoSnack && window.ExpoSnack.append(container);
});
}
}).observe(document.getElementsByTagName('html')[0], {
attributeFilter: ['data-theme'],
attributes: true,
childList: false,
subtree: false,
});
};
// Need to set the theme before the snack script (deferred) initialize
updateSnacksTheme();
setupThemeSynchronization();
return {
onRouteUpdate({location}) {
// console.log('onRouteUpdate', {location});
// TODO temporary, because onRouteUpdate fires before the new route renders...
// see https://github.com/facebook/docusaurus/issues/3399#issuecomment-704401189
setTimeout(() => {
initSnackPlayers();
setupTabPanelsMutationObservers();
}, 0);
},
};
})();

View File

@@ -0,0 +1,72 @@
%link-style {
display: initial;
color: var(--ifm-font-color-base);
background-color: rgba(187, 239, 253, 0.3);
line-height: calc(var(--ifm-font-size-base) + 4px);
border-bottom: 1px solid var(--ifm-hr-border-color);
&:hover {
background-color: rgba(187, 239, 253, 0.6);
}
}
%link-style-dark {
background-color: rgba(97, 218, 251, 0.12);
border-bottom-color: rgba(97, 218, 251, 0.3);
&:hover {
background-color: rgba(97, 218, 251, 0.4);
border-bottom-color: var(--brand);
}
}
%hash-link-style {
background-color: transparent;
border-bottom: 0;
color: var(--subtle);
&:hover {
background-color: transparent;
color: var(--brand);
}
}
%button-link-style {
display: inline-block;
padding: 8px 16px;
border: 1px solid var(--ifm-color-emphasis-300);
border-radius: var(--ifm-global-radius);
color: var(--ifm-color-content-secondary);
&:hover {
background: var(--ifm-menu-color-background-hover);
color: var(--ifm-link-color);
}
}
%scrollbar-style {
&::-webkit-scrollbar {
width: 7px;
height: 7px;
}
&::-webkit-scrollbar-thumb {
background: #888;
border-radius: 10px;
}
&::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
}
%scrollbar-style-dark {
&::-webkit-scrollbar-track {
background: #141414;
}
&::-webkit-scrollbar-thumb {
background: var(--ifm-color-emphasis-200);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,802 @@
@import "shared";
.homepage {
width: 100%;
max-width: 100%;
}
/* ActionButton */
.ActionButton {
padding: 0.75rem 1.5rem;
text-align: center;
font-size: 1.25rem;
font-weight: 400;
text-decoration: none !important;
border-bottom: none;
transition: all 0.2s ease-out;
max-width: 50%;
&.primary {
color: var(--dark);
background-color: var(--brand);
&:hover {
color: black;
background-color: white;
}
}
&.secondary {
background: none;
color: var(--brand);
&::after {
content: "";
font-size: 24px;
margin-left: 5px;
}
&:hover {
color: white;
}
}
}
@media only screen and (max-width: 480px) {
.ActionButton {
max-width: 100%;
width: 100%;
display: block;
white-space: nowrap;
}
}
/* AcknowledgementList */
.AcknowledgementList {
display: grid;
padding: 0;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-gap: 16px;
.item {
list-style: none;
a img {
border-radius: 20%;
overflow: hidden;
}
}
}
/* Community */
.Community .content {
max-width: 900px;
margin: 0 auto;
display: flex;
flex-direction: column;
.firstP img {
float: left;
width: 56px;
height: 56px;
margin-right: 20px;
}
}
@media only screen and (max-width: 480px) {
.Community .Heading {
width: 100%;
padding: 0 1rem;
margin-bottom: 1.5rem;
}
}
@media only screen and (min-width: 481px) and (max-width: 960px) {
.Community .Heading {
width: 100%;
padding: 0 4rem;
margin-bottom: 1.5rem;
}
.Community .AcknowledgementList {
width: 100%;
max-width: 500px;
margin: 2rem auto;
}
}
@media only screen and (min-width: 961px) {
.Community .column.first {
border-right: 1px solid var(--ifm-table-border-color);
}
}
/* Cross Platform */
.CrossPlatform {
svg {
max-width: 400px;
margin: -20px 0;
text {
fill: var(--ifm-color-content-secondary);
}
}
}
@media only screen and (max-width: 960px) {
.CrossPlatform .TwoColumns {
grid-gap: 2rem;
}
.CrossPlatform svg {
max-width: 100%;
margin: 0 auto;
}
}
@media only screen and (min-width: 481px) and (max-width: 960px) {
.CrossPlatform .column.last {
width: 86%;
margin: 0 auto;
text-align: center;
}
}
/* Fast Refresh */
/* Make video flush with the bottom */
.FastRefresh {
margin-bottom: -50px;
}
/* Get rid of extra padding at the bottom of the video */
.FastRefresh .column.last {
margin-bottom: -6px;
}
@media only screen and (max-width: 480px) {
.FastRefresh .column.last {
padding: 0;
}
.FastRefresh video {
width: 100%;
}
}
@media only screen and (min-width: 481px) and (max-width: 960px) {
.FastRefresh .TwoColumns {
grid-gap: 2rem;
}
.FastRefresh .column.last {
width: 100%;
padding: 0;
}
.FastRefresh video {
width: 100%;
}
}
@media only screen and (min-width: 961px) {
/* Give video more space than text */
.FastRefresh .TwoColumns {
grid-template-columns: 2fr 1fr;
}
/* Make video flush with top of section */
.FastRefresh .last {
margin-top: -50px;
}
/* Need to set video height so it'll fit */
.FastRefresh video {
height: 340px;
/* width: 100%; */
}
}
/* Get Started */
.GetStarted,
.GetStarted p {
color: white;
}
.GetStarted .Heading {
color: var(--brand);
text-align: center;
}
.GetStarted .content {
max-width: 900px;
margin: 0 auto;
display: flex;
flex-direction: column;
}
.GetStarted .steps {
align-self: center;
}
.GetStarted .steps li {
font-size: 28px;
margin-bottom: 8px;
}
.GetStarted .steps li p {
font-size: 17px;
}
.GetStarted .terminal {
display: flex;
flex-direction: column;
border-left: 1px solid gray;
border-right: 1px solid gray;
border-top: 1px solid gray;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
padding: 30px 30px 0px 30px;
width: 600px;
position: relative;
}
.GetStarted .terminal::before {
content: "○ ○ ○";
color: gray;
font-size: 16px;
position: absolute;
left: 15px;
top: 5px;
}
.GetStarted code {
color: white;
font-size: 18px;
position: relative;
background: none;
border: 0;
}
.GetStarted code:first-child::before {
content: ">";
position: absolute;
left: -13px;
color: gray;
}
@media screen and (max-width: 760px) {
.GetStarted .content {
width: 80%;
}
.GetStarted .steps li {
margin-left: -1rem;
}
.GetStarted .terminal {
width: 100%;
}
}
/* Header Hero */
.HeaderHero {
padding-top: 20px;
}
.HeaderHero .TwoColumns .column {
max-width: initial;
}
.HeaderHero .socialLinks {
display: flex;
justify-content: flex-end;
max-width: 1200px;
margin: -10px auto 0;
.twitter-follow-button,
.github-button {
margin-right: 1rem;
}
}
.HeaderHero .TwoColumns {
align-items: center;
}
.HeaderHero .title {
font-size: 84px;
color: var(--brand);
line-height: 1;
margin-top: 0;
margin-bottom: 20px;
font-weight: 500;
}
.HeaderHero .tagline {
font-size: 36px;
line-height: 1.3;
color: white;
font-weight: 500;
}
.HeaderHero .buttons {
margin-top: 40px;
}
.HeaderHero .image {
display: flex;
align-items: center;
justify-content: center;
}
@media only screen and (min-width: 961px) {
.HeaderHero .TwoColumns {
grid-template-columns: 3fr 1fr;
}
.HeaderHero .TwoColumns .column.left {
padding-right: 0;
}
.HeaderHero .TwoColumns .column.right {
padding-left: 0;
}
}
@media only screen and (min-width: 481px) and (max-width: 960px) {
.HeaderHero .column.first {
display: flex;
justify-content: center;
}
.HeaderHero .column.last {
text-align: center;
}
}
@media only screen and (max-width: 760px) {
.HeaderHero .title {
font-size: 60px;
}
.HeaderHero .tagline {
font-size: 30px;
}
.HeaderHero .socialLinks {
margin-top: -2rem;
}
}
/* Heading */
.Heading {
font-size: 25px;
color: var(--ifm-font-color-base);
line-height: 1.2;
margin-top: 0;
margin-bottom: 20px;
font-weight: 700;
}
/* Home page */
.HomePage {
width: 100%;
overflow-x: hidden;
}
/* Logo Animation */
.LogoAnimation {
width: 350px;
}
@media only screen and (max-width: 760px) {
.LogoAnimation {
width: 100%;
}
}
.LogoAnimation .screen {
transition: all 850ms ease-in-out;
stroke-opacity: 0;
transform: scale(2.25, 1.33) rotate(0);
stroke-width: 5px;
}
.LogoAnimation .background {
fill: var(--dark);
}
.LogoAnimation .logoInner {
transform: scale(1);
transition: all 850ms ease-in-out;
transition-delay: 50ms;
}
.LogoAnimation.mobile .logoInner,
.LogoAnimation.mobile2 .logoInner {
transform: scale(0.4);
}
.LogoAnimation.desktop .logoInner {
transform: scale(0.5);
}
.LogoAnimation.laptop .logoInner {
transform: scale(0.35);
}
.LogoAnimation.full .screen {
stroke-opacity: 0;
transform: scale(2.25, 1.33) rotate(0);
opacity: 1;
transition: none;
}
.LogoAnimation.mobile .screen {
stroke-opacity: 1;
transform: scale(1) rotate(0);
opacity: 1;
stroke-width: 5px;
}
.LogoAnimation.desktop .screen {
stroke-opacity: 1;
transform: scale(1.125, 1.1) rotate(-90deg);
opacity: 1;
stroke-width: 8px;
}
.LogoAnimation.laptop .screen {
stroke-opacity: 1;
transform: scale(0.83) rotate(-90deg);
opacity: 1;
stroke-width: 5px;
}
.LogoAnimation.mobile2 .screen {
stroke-opacity: 1;
opacity: 1;
stroke-width: 5px;
transform: scale(1) rotate(-180deg);
}
.LogoAnimation.full2 .screen {
stroke-opacity: 0;
transform: scale(2.25, 1.33) rotate(-180deg);
}
.LogoAnimation:not(.mobile):not(.mobile2) .speaker {
opacity: 0;
transform: scaleX(0);
}
.LogoAnimation:not(.desktop) .stand {
transform: scaleX(0);
}
.LogoAnimation:not(.laptop) .base {
transform: scaleX(0);
}
.LogoAnimation .speaker,
.LogoAnimation .stand,
.LogoAnimation .base {
transition: all 850ms ease-in-out;
}
/* Native Apps */
.AdvancedBioMaker {
overflow: hidden;
}
@media only screen and (max-width: 960px) {
.AdvancedBioMaker .column.last {
max-height: 300px;
}
}
@media only screen and (min-width: 481px) and (max-width: 960px) {
.AdvancedBioMaker .column.last {
width: 66.7%;
margin: 0 auto;
}
}
@media only screen and (min-width: 961px) {
.AdvancedBioMaker {
max-height: 400px;
}
/* Correct for whitespace in the image of phones */
.AdvancedBioMaker .column.left {
margin-top: -25px;
}
}
/* Native Code */
.WrittenPython .column.last {
margin-bottom: -50px;
}
.WrittenPython pre {
margin: 0;
}
.WrittenPython .prism-code {
border-radius: 0;
font-size: 80%;
background-color: #282c34;
}
@media only screen and (max-width: 480px) {
.WrittenPython .column.last {
width: 100%;
padding: 0;
overflow-x: hidden;
}
.WrittenPython .prism-code {
font-size: 10px;
padding: 1.25rem 1.25rem;
}
}
@media screen and (min-width: 481px) and (max-width: 960px) {
.WrittenPython .TwoColumns {
grid-gap: 2rem;
}
.WrittenPython .column.last {
width: 100%;
padding: 0;
background-color: var(--dark);
height: 28rem;
overflow-y: scroll;
}
.WrittenPython .prism-code {
width: 30rem;
margin: 0 auto;
padding: 1.25rem 0rem;
}
}
@media only screen and (min-width: 961px) {
.WrittenPython .TwoColumns .column.right {
/* Make flush with top and bottom */
margin-top: -50px;
/* Get rid of default left padding */
padding-left: 0;
}
.WrittenPython .column.right .prism-code {
/* Bleed background into the right */
margin-right: -9999px;
padding: 16px 1.5rem;
height: 460px;
}
}
/* Native Development */
.RichFeatures {
overflow-y: hidden;
}
.RichFeatures .dissection {
position: relative;
}
.RichFeatures .dissection img {
display: block;
margin-left: auto;
margin-right: auto;
}
@media only screen and (max-width: 960px) {
.RichFeatures .TwoColumns {
grid-gap: 2rem;
}
}
@media only screen and (max-width: 480px) {
.RichFeatures .dissection {
height: 350px;
}
}
@media only screen and (min-width: 481px) and (max-width: 960px) {
.RichFeatures .dissection {
height: 450px;
}
}
@media only screen and (min-width: 961px) {
.RichFeatures .dissection {
height: 300px;
}
}
/* Section */
.Section {
width: 100%;
padding-top: 50px;
padding-bottom: 50px;
overflow-x: hidden;
}
.Section + .Section {
border-top: 1px solid var(--ifm-table-border-color);
}
.Section.tint {
background-color: var(--ifm-menu-color-background-active);
}
.Section.dark {
background-color: var(--dark);
}
.Section p a {
@extend %link-style;
}
html[data-theme="dark"] .Section p a {
@extend %link-style-dark;
}
/* VideoContent */
.VideoContent {
.twitter-follow-button {
margin-top: 1.5rem;
}
}
@media only screen and (max-width: 960px) {
.VideoContent .TwoColumns {
grid-gap: 2rem;
}
.VideoContent .column.last {
width: 100%;
display: flex;
justify-content: center;
}
/*
* If the full-width video won't fit, make it full-width.
* https://jameshfisher.com/2017/08/30/how-do-i-make-a-full-width-iframe/
*/
.VideoContent .vidWrapper {
position: relative;
width: 100%;
padding-top: 56.25%;
}
.VideoContent iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
@media only screen and (min-width: 961px) {
/* Give more width for the video */
.VideoContent .TwoColumns {
grid-template-columns: 1fr 2fr;
}
.VideoContent iframe {
width: 560px;
height: 315px;
}
}
/* Two columns */
.TwoColumns {
display: grid;
}
.TwoColumns .column {
width: 100%;
}
.TwoColumns .column.first {
grid-area: first;
}
.TwoColumns .column.last {
grid-area: last;
}
@media only screen and (min-width: 961px) {
.TwoColumns {
max-width: 900px;
margin: 0 auto;
grid-template-columns: repeat(2, 1fr);
grid-template-areas: "first last";
}
.TwoColumns.reverse {
grid-template-areas: "last first";
}
.TwoColumns .column {
max-width: 450px;
}
.TwoColumns .column.left {
padding-right: 50px;
}
.TwoColumns .column.right {
padding-left: 50px;
}
}
@media only screen and (max-width: 960px) {
.TwoColumns,
.TwoColumns.reverse {
grid-template-columns: 1fr;
grid-template-areas: "first" "last";
}
.TwoColumns .column {
padding: 0 4rem;
}
}
@media only screen and (max-width: 480px) {
.TwoColumns .column {
padding: 0 1.25rem;
}
}
/* Twitter Follow Button */
.twitter-follow-button {
display: inline-block;
position: relative;
height: 28px;
box-sizing: border-box;
padding: 1px 10px 1px 9px;
background-color: #1b95e0;
color: #fff;
border-radius: 4px;
font-weight: 400;
cursor: pointer;
font-size: 13px;
line-height: 26px;
&:hover {
color: #fff;
background-color: #0c7abf;
}
.icon {
position: relative;
display: inline-block;
top: 4px;
height: 18px;
width: 18px;
margin-right: 4px;
background: transparent 0 0 no-repeat;
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2072%2072%22%3E%3Cpath%20fill%3D%22none%22%20d%3D%22M0%200h72v72H0z%22%2F%3E%3Cpath%20class%3D%22icon%22%20fill%3D%22%23fff%22%20d%3D%22M68.812%2015.14c-2.348%201.04-4.87%201.744-7.52%202.06%202.704-1.62%204.78-4.186%205.757-7.243-2.53%201.5-5.33%202.592-8.314%203.176C56.35%2010.59%2052.948%209%2049.182%209c-7.23%200-13.092%205.86-13.092%2013.093%200%201.026.118%202.02.338%202.98C25.543%2024.527%2015.9%2019.318%209.44%2011.396c-1.125%201.936-1.77%204.184-1.77%206.58%200%204.543%202.312%208.552%205.824%2010.9-2.146-.07-4.165-.658-5.93-1.64-.002.056-.002.11-.002.163%200%206.345%204.513%2011.638%2010.504%2012.84-1.1.298-2.256.457-3.45.457-.845%200-1.666-.078-2.464-.23%201.667%205.2%206.5%208.985%2012.23%209.09-4.482%203.51-10.13%205.605-16.26%205.605-1.055%200-2.096-.06-3.122-.184%205.794%203.717%2012.676%205.882%2020.067%205.882%2024.083%200%2037.25-19.95%2037.25-37.25%200-.565-.013-1.133-.038-1.693%202.558-1.847%204.778-4.15%206.532-6.774z%22%2F%3E%3C%2Fsvg%3E);
}
}

View File

@@ -0,0 +1,139 @@
@import "shared";
.showcaseSection {
max-width: 800px;
margin: 64px auto;
text-align: center;
padding: 0 20px;
.showcase img {
height: 100px;
border-radius: 20px;
}
.logos img {
padding: 0;
}
.info {
font-size: 15px;
}
p a {
@extend %link-style;
}
.form-button {
@extend %button-link-style;
margin-bottom: 36px;
border-width: 2px;
font-weight: 500;
&:hover {
border-color: var(--ifm-color-primary);
color: var(--ifm-font-color-base) !important;
}
}
}
html[data-theme="dark"] .showcaseSection {
p a {
@extend %link-style-dark;
}
.form-button {
color: var(--ifm-color-secondary-dark);
}
}
.home-showcase-section {
max-width: 800px;
margin: 20px auto 20px auto;
text-align: center;
padding-bottom: 40px;
p {
max-width: 560px;
margin: 0 auto;
}
}
.pinned img {
width: 150px;
border-radius: 20px;
}
.footnote {
font-size: 12px;
color: rgba(0, 0, 0, 0.4);
}
.home-showcase-section {
h2 {
font-size: 31px;
line-height: 40px;
margin: 10px 0;
}
.showcase img {
width: 100px;
height: 100px;
border-radius: 20px;
}
}
.showcaseHeader {
padding-bottom: 15px;
padding-top: 15px;
text-align: center;
}
.showcase {
margin: 30px auto 30px auto;
width: 100%;
display: inline-block;
text-align: center;
vertical-align: top;
h3 {
margin-bottom: 0;
line-height: 21px;
padding: 5px 5px 2px;
font-size: 21px;
}
p {
margin-top: 5px;
padding-top: 0 !important;
}
h3,
p {
color: var(--ifm-color-emphasis-800);
}
}
@media only screen and (max-device-width: 840px) {
.showcaseSection {
width: 100%;
}
}
@media only screen and (min-width: 600px) {
.showcase {
width: 50%;
}
}
@media only screen and (min-width: 960px) {
.showcase {
width: 25%;
}
}
@media only screen and (min-device-width: 736px) {
.showcaseSection .showcase img {
width: 100px;
max-height: 100px;
}
}

View File

@@ -0,0 +1,47 @@
@import "shared";
.versions-page {
max-width: 960px;
padding: 28px;
h1 {
font-size: 3rem;
}
h2 {
font-size: 2rem;
}
code {
white-space: pre;
border: 0;
}
p a,
td a {
@extend %link-style;
code {
background: none;
white-space: nowrap;
}
}
table th,
table td {
min-width: 100px;
font-size: 15px;
padding: 8px 20px;
}
.versions {
margin-bottom: 32px;
}
}
html[data-theme="dark"] .versions-page {
p a,
td a {
@extend %link-style-dark;
}
}

View File

@@ -0,0 +1,45 @@
export function setupDissectionAnimation() {
const section = document.querySelector('.RichFeatures');
const dissection = document.querySelector('.RichFeatures .dissection');
const images = dissection.children;
const numImages = images.length;
const fadeDistance = 40;
const navbarHeight = 60;
function clamp(val, min, max) {
return Math.min(max, Math.max(min, val));
}
// Scale the percent so that `min` goes to 0% and `max` goes to 100%
function scalePercent(percent, min, max) {
const scale = max - min;
return clamp((percent - min) / scale, 0, 1);
}
// Get the percentage that the image should be on the screen given
// how much the entire container is scrolled
// so we can fine-tune at what screen % the animation starts and stops
function getImagePercent(index, scrollPercent) {
const start = index / numImages;
return clamp((scrollPercent - start) * numImages, 0, 1);
}
function onScroll() {
const elPos = section.getBoundingClientRect().top - navbarHeight;
const height = window.innerHeight;
const screenPercent = 1 - clamp(elPos / height, 0, 1);
const scaledPercent = scalePercent(screenPercent, 0.2, 0.9);
for (let i = 0; i < numImages; i++) {
const imgPercent = getImagePercent(i, scaledPercent);
images[i].style.opacity = imgPercent;
const translation = fadeDistance * (1 - imgPercent);
images[i].style.left = `${translation}px`;
}
}
window.addEventListener('scroll', onScroll);
return () => window.removeEventListener('scroll', onScroll);
}

View File

@@ -0,0 +1,49 @@
export function setupHeaderAnimations() {
const steps = ['full', 'mobile', 'desktop', 'laptop', 'mobile2', 'full2'];
const intervals = [1250, 1500, 1500, 1500, 1500, 1250];
let i = 0;
const timeouts = [];
const logo = document.querySelector('.LogoAnimation');
function animateStep() {
const prev = steps[i];
logo.classList.remove(prev);
i = (i + 1) % steps.length;
const current = steps[i];
const timeout = intervals[i];
logo.classList.add(current);
timeouts.push(setTimeout(animateStep, timeout));
}
// only start the animation if the document is visible on load
if (!document.hidden) {
timeouts.push(
setTimeout(() => {
logo.classList.remove('init');
animateStep();
}, 2000)
);
}
function onVisibilityChange() {
if (document.hidden) {
timeouts.forEach(timeout => {
clearTimeout(timeout);
});
// clear the timeouts array
timeouts.length = 0;
} else {
// restart the animation when visible
animateStep();
}
}
// https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
document.addEventListener('visibilitychange', onVisibilityChange, false);
return () =>
document.removeEventListener('visibilitychange', onVisibilityChange);
}

View File

@@ -0,0 +1,36 @@
---
title: Community
description: The OpenDBM Community
wrapperClassName: 'community-page'
---
# The OpenDBM Community
There are some developers / researchers around the world using OpenDBM. This is a brief overview of where you can find them.
## Where To Get Help
If you need help with your OpenDBM library, the right place to go depends on the type of help that you need.
### Repository
The core of **OpenDBM** is worked on full-time by AiCure's OpenDBM team. But there are far more people in the community who make key contributions and fix things. If the issue you are facing is code related, you should consider checking the open issues in the [main repository](https://github.com/AiCure/open_dbm/issues). If you cannot find an existing issue, please [use the Bug Report template](https://github.com/AiCure/open_dbm/issues/new?assignees=&labels=&template=bug_report.yaml) to create an issue with a minimal example.
### Stack Overflow
Many members of the community use Stack Overflow to ask questions. Read through the [existing questions](http://stackoverflow.com/questions/tagged/opendbm?sort=frequent) tagged with **opendbm** or [ask your own](http://stackoverflow.com/questions/ask?tags=opendbm)!
## Staying up to date
### Discussion
OpenDBM is still a young library, and its rapid release cycle leaves the door open for discussing how it can evolve at every step of the way. If you want to know the discussions that are talking about, you can read through the [Discussions](https://github.com/AiCure/open_dbm/discussions).
### Conferences
You can check the [OpenDBM Meetups](https://aicure.com/opendbm/) that happen around the world.
## Communities
### Forum-like groups
We want to ensure that OpenDBM feels like a community of like-minded researchers and clinicians. Hence, there are a few ways we encourage users to stay involvedand why we encourage you [to join DiME, too!](https://www.dimesociety.org/)

View File

@@ -0,0 +1,495 @@
import React, {useEffect} from 'react';
import GitHubButton from 'react-github-btn';
import Head from '@docusaurus/Head';
import useBaseUrl from '@docusaurus/useBaseUrl';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import Layout from '@theme/Layout';
import CodeBlock from '@theme/CodeBlock';
import CrossPlatformSVG from '../../static/img/homepage/cross-platform.svg';
import {setupDissectionAnimation} from './animations/_dissectionAnimation';
import {setupHeaderAnimations} from './animations/_headerAnimation';
const textContent = {
intro: `
OpenDBM is a software package that allows for calculation of digital biomarkers from video/audio of a person's
behavior by combining tools to measure behavioral characteristics like facial activity, voice, and movement into a single package for measurement of overall behavior.
<br/><br/>
<strong>Ease of use</strong>. OpenDBM is designed for ease of use, expanding the availability of such tools to the wider scientific community.
`,
writtenPython: `
It using Python language so you can integrate with other biomaker or ML libraries
<br/><br/>
<strong>Many OS platforms</strong>, one code. You can focus on building researching medical purpose
and the single codebase can share code across platforms. With OpenDBM,
one team can maintain multiple OS platforms and share a common researching application.
`,
codeExample: `
from opendbm.facial import FacialActivity
model = FacialActivity()
m.fit()
landmark = model.get_landmark()
landmark.mean()
landmark.std()
`,
forResearchers: `
Through OpenDBM, a user can objectively and sensitively measure behavioral characteristics such as facial activity, vocal acoustics,
characteristics of speech, patterns of movement, and oculomotion. <br/><br/>
From those behavioral characteristics, they can measure clinically meaningful symptomatology such as emotional expressivity, prosody
of voice, valence of speech, and severity of tremoramong many others.
`,
talks: `
Weve recorded some instructional videos (listed and linked to within the documentation) that should help the user get through
common steps such as installation, usage, etc.
`,
};
function Heading({text}) {
return <h2 className="Heading">{text}</h2>;
}
function ActionButton({href, type = 'primary', target, children}) {
return (
<a className={`ActionButton ${type}`} href={href} target={target}>
{children}
</a>
);
}
function TextColumn({title, text, moreContent}) {
return (
<>
<Heading text={title} />
<div dangerouslySetInnerHTML={{__html: text}} />
{moreContent}
</>
);
}
function HomeCallToAction() {
return (
<>
<ActionButton
type="primary"
href={useBaseUrl('docs/getting-started')}
target="_self">
Get started
</ActionButton>
<ActionButton
type="secondary"
href={useBaseUrl('docs/tutorial')}
target="_self">
Learn basics
</ActionButton>
</>
);
}
function GitHubStarButton() {
return (
<div className="github-button">
<GitHubButton
href="https://github.com/AiCure/open_dbm"
data-icon="octicon-star"
data-size="large"
aria-label="Star AiCure/open_dbm on GitHub">
Star
</GitHubButton>
</div>
);
}
function Section({
element = 'section',
children,
className,
background = 'light',
}) {
const El = element;
return <El className={`Section ${className} ${background}`}>{children}</El>;
}
function TwoColumns({columnOne, columnTwo, reverse}) {
return (
<div className={`TwoColumns ${reverse ? 'reverse' : ''}`}>
<div className={`column first ${reverse ? 'right' : 'left'}`}>
{columnOne}
</div>
<div className={`column last ${reverse ? 'left' : 'right'}`}>
{columnTwo}
</div>
</div>
);
}
function ScreenRect({className, fill, stroke}) {
return (
<rect
className={`screen ${className || ''}`}
rx="3%"
width="180"
height="300"
x="-90"
y="-150"
fill={fill}
stroke={stroke}
/>
);
}
function LogoAnimation() {
return (
<svg
className="LogoAnimation init"
width={350}
height={350}
xmlns="http://www.w3.org/2000/svg"
viewBox="-200 -200 400 400">
<title>OpenDBM logo</title>
<clipPath id="screen">
<ScreenRect fill="none" stroke="gray" />
</clipPath>
<rect
x="-25"
y="120"
width="50"
height="25"
rx="2"
fill="white"
stroke="none"
className="stand"
/>
<polygon
points="-125,90 125,90 160,145 -160,145"
fill="white"
stroke="white"
strokeWidth="5"
strokeLinejoin="round"
className="base"
/>
<ScreenRect className="background" stroke="none" />
<g clipPath="url(#screen)" className="logo">
<g className="logoInner">
<circle cx="0" cy="0" r="30" fill="#61dafb" />
<g stroke="#61dafb" strokeWidth="15" fill="none" id="logo">
<ellipse rx="165" ry="64" />
<ellipse rx="165" ry="64" transform="rotate(60)" />
<ellipse rx="165" ry="64" transform="rotate(120)" />
</g>
</g>
<line
x1="-30"
x2="30"
y1="130"
y2="130"
stroke="white"
strokeWidth="8"
strokeLinecap="round"
className="speaker"
/>
</g>
<ScreenRect fill="none" stroke="white" />
</svg>
);
}
function HeaderHero() {
return (
<Section background="dark" className="HeaderHero">
<div className="socialLinks">
<GitHubStarButton />
</div>
<TwoColumns
reverse
columnOne={
<img alt="" src={useBaseUrl('img/homepage/biomaker_logo2.gif')} />
}
columnTwo={
<>
<h1 className="title">OpenDBM</h1>
<p className="tagline">Digital Biomaker&nbsp;Library.</p>
<div className="buttons">
<HomeCallToAction />
</div>
</>
}
/>
</Section>
);
}
function AdvancedBioMaker() {
return (
<Section className="AdvancedBioMaker" background="light">
<TwoColumns
reverse
columnOne={
<TextColumn
title="Advanced Digital Biomaker"
text={textContent.intro}
/>
}
columnTwo={<img alt="" src={useBaseUrl('img/homepage/biomaker.png')} />}
/>
</Section>
);
}
function WrittenPython() {
return (
<Section className="WrittenPython" background="tint">
<TwoColumns
columnOne={
<TextColumn
title="Written in Python—support all OS platforms"
text={textContent.writtenPython}
/>
}
columnTwo={
<CodeBlock language="jsx">{textContent.codeExample}</CodeBlock>
}
/>
</Section>
);
}
function RichFeatures() {
return (
<Section className="RichFeatures" background="light">
<TwoColumns
reverse
columnOne={
<TextColumn
title="Rich Features for Researchers"
text={textContent.forResearchers}
/>
}
columnTwo={
<div className="dissection">
<img alt="" src={useBaseUrl('img/homepage/features.png')} />
</div>
}
/>
</Section>
);
}
function VideoContent() {
return (
<div>
<Section className="VideoContent" background="tint">
<br />
<TwoColumns
columnOne={
<TextColumn title="Talks and Videos" text={textContent.talks} />
}
columnTwo={
<div className="vidWrapper">
<iframe
src="https://www.youtube.com/embed/PNS-TQX5CFc"
title="OpenDBM Virtual Training"
frameBorder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
/>
</div>
}
/>
<br />
<TwoColumns
columnOne={
<>
<p>
The{' '}
<a href="https://github.com/AiCure/open_dbm/graphs/contributors">
OpenDBM Contributor Team
</a>{' '}
has put together a short video where they explained about the
installation in some platforms.
</p>
</>
}
columnTwo={
<div className="vidWrapper">
<iframe
src="https://www.youtube.com/embed/CfNFBcf41u0"
title="OpenDBM Installation on Mac"
frameBorder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
/>
<br></br>
<iframe
src="https://www.youtube.com/embed/TKON5UcxrwQ"
title="OpenDBM How to use on Mac"
frameBorder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
/>
</div>
}
/>
</Section>
</div>
);
}
/* Community */
function AcknowledgementList() {
const {siteConfig} = useDocusaurusContext();
const apps = siteConfig.customFields.users.filter(app => app.pinned);
return (
<ul className="AcknowledgementList">
{apps.map((app, i) => {
const imgSource = !app.icon.startsWith('http')
? useBaseUrl('img/showcase/' + app.icon)
: app.icon;
return (
<li key={i} className="item">
{app.infoLink ? (
<a href={app.infoLink}>
<img src={imgSource} alt={app.name} />
</a>
) : (
<img src={imgSource} alt={app.name} />
)}
</li>
);
})}
</ul>
);
}
function Community() {
return (
<Section className="Community" background="light">
<div className="content">
<Heading text="Community Driven" />
<TwoColumns
columnOne={
<>
<p className="firstP">
<img src={useBaseUrl(`img/header_logo.png`)} alt="" />
<span>
AiCure released OpenDBM in 2020 and has been maintaining it
ever since.
</span>
</p>
<p>
We want to ensure that OpenDBM feels like a community of
like-minded researchers and clinicians. Hence, there are a few
ways we encourage users to stay involvedand why we encourage
you to join DiME, too! Most importantly, if youre interested in
OpenDBM, star the repo and sign up for our listserv for all
updates.
</p>
<p>
If youre thinking about contributing to OpenDBMto which we
say kudosplease{' '}
<span>
<a href="opendbm@aicure.com">reach out to us.</a>
</span>{' '}
Weve written{' '}
<span>
<a href="https://github.com/AiCure/open_dbm/blob/master/CODE_OF_CONDUCT.md">
code of conduct
</a>
</span>{' '}
and{' '}
<span>
<a href="https://github.com/AiCure/open_dbm/blob/master/CONTRIBUTING.md">
contribution guidelines
</a>
</span>{' '}
but also want to do whatever we can to help.
</p>
</>
}
columnTwo={
<>
<p>
<strong>Acknowledgements</strong> <br></br>A point that was
mentioned earlier and cannot be emphasized enough is that
OpenDBM is simply a compilation of existing but disparate
open-source software tools that weve built on top of. All these
tools are of course listed in the OpenDBM dependencies but we
want to acknowledge just a few here: OpenFace, built on OpenCV,
is at the heart of all facial measures and even some of the
movement ones. Parselmouth and its reliance on the Praat
software library lies behind most of the vocal acoustic
measures. DeepSpeech was used for all speech transcription and
NLTK is utilized for a lot of language metrics. OpenDBM would
not be possible without theseand several otheropen source
software packages.
</p>
<AcknowledgementList />
</>
}
/>
</div>
</Section>
);
}
function GetStarted() {
return (
<Section className="GetStarted" background="dark">
<div className="content">
<Heading text="Give it a try" />
<ol className="steps">
<li>
<p>Run this</p>
<div className="terminal">
<code>pip install opendbm</code>
</div>
</li>
<li>
<p>Read these</p>
<HomeCallToAction />
</li>
</ol>
</div>
</Section>
);
}
const useHomePageAnimations = () => {
useEffect(() => setupHeaderAnimations(), []);
useEffect(() => setupDissectionAnimation(), []);
};
const Index = () => {
useHomePageAnimations();
return (
<Layout
description="A digital biomaker reader based on Python"
wrapperClassName="homepage">
<Head>
<title>OpenDBM · Digital Biomaker Library</title>
<meta
property="og:title"
content="OpenDBM · Digital Biomaker Library"
/>
<meta
property="twitter:title"
content="OpenDBM · Digital Biomaker Library"
/>
</Head>
<HeaderHero />
<AdvancedBioMaker />
<WrittenPython />
<RichFeatures />
<VideoContent />
<Community />
<GetStarted />
</Layout>
);
};
export default Index;

View File

@@ -0,0 +1,119 @@
/**
* Copyright (c) AiCure, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import Layout from '@theme/Layout';
import useBaseUrl from '@docusaurus/useBaseUrl';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
const renderApp = (app, i) => {
return (
<div className="showcase" key={i}>
<div>
<a href={app.infoLink}>{renderAppIcon(app)}</a>
<h3>{app.name}</h3>
{renderLinks(app)}
{renderInfo(app.infoTitle, app.infoLink)}
</div>
</div>
);
};
const renderAppIcon = app => {
let imgSource = app.icon;
if (!app.icon.startsWith('http')) {
imgSource = useBaseUrl('img/showcase/' + app.icon);
}
return <img src={imgSource} alt={app.name} />;
};
const renderInfo = (title, uri) => {
return uri ? (
<p className="info">
<a href={uri} target="_blank">
{title}
</a>
</p>
) : null;
};
const renderLinks = app => {
if (!app.linkAppStore && !app.linkPlayStore) {
return;
}
const linkAppStore = app.linkAppStore ? (
<a href={app.linkAppStore} target="_blank">
iOS
</a>
) : null;
const linkPlayStore = app.linkPlayStore ? (
<a href={app.linkPlayStore} target="_blank">
Android
</a>
) : null;
return (
<p>
{linkPlayStore}
{linkPlayStore && linkAppStore ? ' • ' : ''}
{linkAppStore}
</p>
);
};
const Showcase = () => {
const {siteConfig} = useDocusaurusContext();
const showcaseApps = siteConfig.customFields.users;
const pinnedApps = showcaseApps.filter(app => app.pinned);
const featuredApps = showcaseApps
.filter(app => !app.pinned)
.sort((a, b) => a.name.localeCompare(b.name));
const apps = pinnedApps.concat(featuredApps);
return (
<Layout
title="Who's using OpenDBM?"
description="Some already using openDBM. You can check these below">
<div className="showcaseSection">
<div className="prose">
<h1>Who's using OpenDBM?</h1>
<p>
This section attempts to list all peer-reviewed scientific articlces
that have used OpenDBM for measurement of digital biomarkers.
</p>
</div>
<div>
<ul>
<li>
Galatzer-Levy, I., Abbas, A., Koesmahargyo, V., Yadav, V.,
Perez-Rodriguez, M. M., Rosenfield, P., ... & Hansen, B. J.
(2020). Facial and vocal markers of schizophrenia measured using
remote smartphone assessments. medRxiv.
</li>
<li>
Galatzer-Levy, I. R., Abbas, A., Ries, A., Homan, S.,
Koesmahargyo, V., Yadav, V., ... & Scholz, U. (2020). Validation
of Visual and Auditory Digital Markers of Suicidality in Acutely
Suicidal Psychiatric In-Patients. medRxiv.
</li>
</ul>
</div>
<a
className="form-button"
href="https://forms.gle/Hb6bDL1GJvG1ByUX7"
target="_blank">
To add to this list, please simply fill out this form.
</a>
</div>
</Layout>
);
};
export default Showcase;

View File

@@ -0,0 +1,119 @@
/**
* Copyright (c) AiCure, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import Layout from '@theme/Layout';
import useBaseUrl from '@docusaurus/useBaseUrl';
const versions = require('../../versions.json');
const VersionItem = ({version, currentVersion}) => {
const versionName = version === 'next' ? 'Master' : version;
const isCurrentVersion = currentVersion === version;
const isNext = version === 'next';
const isRC = version.toUpperCase().indexOf('-RC') !== -1;
const latestMajorVersion = versions[0].toUpperCase().replace('-RC', '');
const documentationLink = (
<a
href={useBaseUrl(
'docs/' + (isCurrentVersion ? '' : version + '/') + 'getting-started'
)}>
Documentation
</a>
);
let releaseNotesURL = 'https://github.com/AiCure/open_dbm/releases';
let releaseNotesTitle = 'Changelog';
if (isNext) {
releaseNotesURL = `https://github.com/AiCure/open_dbm/compare/${latestMajorVersion}-stable...main`;
releaseNotesTitle = 'Commits since ' + latestMajorVersion;
} else if (!isRC) {
releaseNotesURL = `https://github.com/AiCure/open_dbm/releases/tag/v${version}.0`;
}
const releaseNotesLink = <a href={releaseNotesURL}>{releaseNotesTitle}</a>;
return (
<tr>
<th>{versionName}</th>
<td>{documentationLink}</td>
<td>{releaseNotesLink}</td>
</tr>
);
};
const Versions = () => {
const currentVersion = versions.length > 0 ? versions[0] : null;
const latestVersions = ['next'].concat(
versions.filter(version => version.indexOf('-RC') !== -1)
);
const stableVersions = versions.filter(
version => version.indexOf('-RC') === -1 && version !== currentVersion
);
return (
<Layout title="Versions" wrapperClassName="versions-page">
<h1>OpenDBM versions</h1>
<p>
When we want to plan for a release, a new release candidate is created
off the <code>master</code> branch of{' '}
<a href={'https://github.com/AiCure/open_dbm'}>
<code>AiCure/open_dbm</code>
</a>
. The release candidate will be online for some time to allow
contributors like yourself to{' '}
<a href={useBaseUrl('docs/upgrading')}>verify the changes</a> and to
identify any issues by{' '}
<a href="https://github.com/AiCure/open_dbm/issues">
writing clear, actionable bug reports
</a>
. Eventually, the release candidate will be promoted to{' '}
<code>master</code> .
</p>
<h2>Latest version</h2>
<p>
The most recent stable version will be publish automatically whenever a
new release tag is created using the{' '}
<code>github git tag [new version]</code> command.
</p>
<table className="versions">
<tbody>
<VersionItem
key={'version_' + currentVersion}
version={currentVersion}
currentVersion={currentVersion}
/>
</tbody>
</table>
<h2>Previous versions</h2>
<table className="versions">
<tbody>
{stableVersions.map(version => (
<VersionItem
key={'version_' + version}
version={version}
currentVersion={currentVersion}
/>
))}
</tbody>
</table>
<h2>Archived versions</h2>
<p>
The documentation for versions below <code>0.2.0</code> can be found on
the separate docs word{' '}
<a href="https://docs.google.com/document/d/1O6OUSY9FHFCZfpIWu3Kgg0Q_dyp073xjjQ2K3viEffU/edit#heading=h.ttfc0bhy0aif">
in here
</a>
.
</p>
</Layout>
);
};
export default Versions;

View File

@@ -0,0 +1,3 @@
/// <reference types="react" />
import type { Props } from "@theme/BlogSidebar/Desktop";
export default function BlogSidebarDesktop({ sidebar }: Props): JSX.Element;

View File

@@ -0,0 +1,47 @@
import React from 'react';
import clsx from 'clsx';
import Link from '@docusaurus/Link';
import {translate} from '@docusaurus/Translate';
import styles from './styles.module.css';
export default function BlogSidebarDesktop({sidebar}) {
let cachedYear = null;
return (
<aside className="col col--3">
<nav
className={clsx(styles.sidebar, 'thin-scrollbar')}
aria-label={translate({
id: 'theme.blog.sidebar.navAriaLabel',
message: 'Blog recent posts navigation',
description: 'The ARIA label for recent posts in the blog sidebar',
})}>
<div className={clsx(styles.sidebarHeader, 'margin-bottom--md')}>
{sidebar.title}
</div>
<ul className={clsx(styles.sidebarItemList, 'clean-list')}>
{sidebar.items.map(item => {
const postYear = item.permalink.split('/')[2];
const yearHeader = cachedYear !== postYear && (
<h5 className={styles.sidebarItemTitle}>{postYear}</h5>
);
cachedYear = postYear;
return (
<>
{yearHeader}
<li key={item.permalink} className={styles.sidebarItem}>
<Link
isNavLink
to={item.permalink}
className={styles.sidebarItemLink}
activeClassName={styles.sidebarItemLinkActive}>
{item.title}
</Link>
</li>
</>
);
})}
</ul>
</nav>
</aside>
);
}

View File

@@ -0,0 +1,60 @@
.sidebar {
max-height: calc(100vh - (var(--ifm-navbar-height) + 2rem));
overflow-y: auto;
position: sticky;
top: var(--ifm-navbar-height);
padding: 20px 12px 0 0;
margin-left: -20px;
}
.sidebarHeader {
font-size: var(--ifm-h4-font-size);
font-weight: var(--ifm-font-weight-bold);
padding-left: 12px;
display: block;
}
.sidebarItemTitle {
margin: 0.75rem 0 0.5rem;
color: var(--subtle);
padding-left: 12px;
border-bottom: 0.01rem solid var(--ifm-table-border-color);
padding-bottom: 4px;
}
.sidebarItemList {
font-size: 13px;
}
.sidebarItem {
margin-top: 0.1rem;
line-height: 18px;
}
.sidebarItemLink {
color: var(--ifm-font-color-base);
padding: 4px 8px;
display: block;
border-left: 4px solid transparent;
border-radius: 0.25rem;
line-height: 18px;
}
.sidebarItemLink:hover {
background: var(--ifm-menu-color-background-active);
color: var(--ifm-font-color-base);
text-decoration: none;
}
.sidebarItemLinkActive {
color: var(--ifm-font-color-base);
background: var(--ifm-menu-color-background-active);
border-left-color: var(--ifm-menu-color-active);
font-weight: 700;
}
@media (max-width: 996px) {
.sidebar {
display: none;
}
}

View File

@@ -0,0 +1,3 @@
/// <reference types="react" />
import type { Props } from "@theme/BlogSidebar/Mobile";
export default function BlogSidebarMobile(props: Props): JSX.Element;

View File

@@ -0,0 +1,41 @@
import React from 'react';
import Link from '@docusaurus/Link';
import {NavbarSecondaryMenuFiller} from '@docusaurus/theme-common';
import styles from './styles.module.css';
function BlogSidebarMobileSecondaryMenu({sidebar}) {
let cachedYear = null;
return (
<ul className="menu__list blog-menu__list">
{sidebar.items.map(item => {
const postYear = item.permalink.split('/')[2];
const yearHeader = cachedYear !== postYear && (
<h5 className={styles.sidebarItemTitle}>{postYear}</h5>
);
cachedYear = postYear;
return (
<>
{yearHeader}
<li key={item.permalink} className="menu__list-item">
<Link
isNavLink
to={item.permalink}
className="menu__link"
activeClassName="menu__link--active">
{item.title}
</Link>
</li>
</>
);
})}
</ul>
);
}
export default function BlogSidebarMobile(props) {
return (
<NavbarSecondaryMenuFiller
component={BlogSidebarMobileSecondaryMenu}
props={props}
/>
);
}

View File

@@ -0,0 +1,7 @@
.sidebarItemTitle {
margin: 0.75rem 0 0.5rem;
color: var(--subtle);
padding-left: 12px;
border-bottom: 0.01rem solid var(--ifm-table-border-color);
padding-bottom: 4px;
}

View File

@@ -0,0 +1,18 @@
/**
* Copyright (c) AiCure, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import DocItemFooterOriginal from '@theme-original/DocItemFooter';
import DocsRating from '../../../core/DocsRating';
export default function DocItemFooter(props) {
return (
<>
<DocsRating label={props.content.metadata.unversionedId} />
<DocItemFooterOriginal {...props} />
</>
);
}

View File

@@ -0,0 +1,103 @@
/**
* Copyright (c) AiCure, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem';
import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem';
import {
useVersions,
useLatestVersion,
useActiveDocContext,
} from '@docusaurus/plugin-content-docs/client';
import {useDocsPreferredVersion} from '@docusaurus/theme-common';
import {translate} from '@docusaurus/Translate';
const getVersionMainDoc = version =>
version.docs.find(doc => doc.id === version.mainDocId);
export default function DocsVersionDropdownNavbarItem({
mobile,
docsPluginId,
dropdownActiveClassDisabled,
dropdownItemsBefore,
dropdownItemsAfter,
...props
}) {
const activeDocContext = useActiveDocContext(docsPluginId);
const versions = useVersions(docsPluginId);
const latestVersion = useLatestVersion(docsPluginId);
const {preferredVersion, savePreferredVersionName} =
useDocsPreferredVersion(docsPluginId);
// (CUSTOM) Hide version dropdown on non-versioned pages
if (!activeDocContext.activeDoc) {
return null;
}
// (CUSTOM) Show only `next` and last 5 versions in the dropdown
const reducedVersions = versions.slice(0, 6);
function getItems() {
const versionLinks = reducedVersions.map(version => {
// We try to link to the same doc, in another version
// When not possible, fallback to the "main doc" of the version
const versionDoc =
activeDocContext?.alternateDocVersions[version.name] ||
getVersionMainDoc(version);
return {
isNavLink: true,
label: version.label,
to: versionDoc.path,
isActive: () => version === activeDocContext?.activeVersion,
onClick: () => {
savePreferredVersionName(version.name);
},
};
});
return [...dropdownItemsBefore, ...versionLinks, ...dropdownItemsAfter];
}
const items = getItems();
const dropdownVersion =
activeDocContext.activeVersion ?? preferredVersion ?? latestVersion; // Mobile dropdown is handled a bit differently
const dropdownLabel =
mobile && items
? translate({
id: 'theme.navbar.mobileVersionsDropdown.label',
message: 'Versions',
description:
'The label for the navbar versions dropdown on mobile view',
})
: dropdownVersion.label;
const dropdownTo =
mobile && items ? undefined : getVersionMainDoc(dropdownVersion).path; // We don't want to render a version dropdown with 0 or 1 item
// If we build the site with a single docs version (onlyIncludeVersions: ['1.0.0'])
// We'd rather render a button instead of a dropdown
if (items.length <= 1) {
return (
<DefaultNavbarItem
{...props}
mobile={mobile}
label={dropdownLabel}
to={dropdownTo}
isActive={dropdownActiveClassDisabled ? () => false : undefined}
/>
);
}
return (
<DropdownNavbarItem
{...props}
mobile={mobile}
label={dropdownLabel}
to={dropdownTo}
items={items}
isActive={dropdownActiveClassDisabled ? () => false : undefined}
/>
);
}

View File

@@ -0,0 +1,40 @@
# Redirects from what the browser requests to what we serve
# Renamed pages
/docs/android-setup /docs/getting-started
/docs/building-for-apple-tv /docs/building-for-tv
/docs/building-from-source /contributing/how-to-build-from-source
/docs/contributing /contributing/overview
/docs/publishing-forks /contributing/how-to-build-from-source#publish-your-own-version-of-react-native
/docs/testing /contributing/how-to-run-and-write-tests
/docs/understanding-cli https://github.com/react-native-community/cli#react-native-cli
/contributing/how-to-contribute /contributing/overview
# Changed blog post dates
/blog/2021/03/11/version-0.64 /blog/2021/03/12/version-0.64
# Architecture pages move to unversioned docs
/docs/architecture-overview /architecture/overview
/docs/fabric-renderer /architecture/fabric-renderer
/docs/render-pipeline /architecture/render-pipeline
/docs/xplat-implementation /architecture/xplat-implementation
/docs/view-flattening /architecture/view-flattening
/docs/threading-model /architecture/threading-model
/docs/architecture-glossary /architecture/glossary
/docs/next/architecture-overview /architecture/overview
/docs/next/fabric-renderer /architecture/fabric-renderer
/docs/next/render-pipeline /architecture/render-pipeline
/docs/next/xplat-implementation /architecture/xplat-implementation
/docs/next/view-flattening /architecture/view-flattening
/docs/next/threading-model /architecture/threading-model
/docs/next/architecture-glossary /architecture/glossary
/docs/0.66/architecture-overview /architecture/overview
/docs/0.66/fabric-renderer /architecture/fabric-renderer
/docs/0.66/render-pipeline /architecture/render-pipeline
/docs/0.66/xplat-implementation /architecture/xplat-implementation
/docs/0.66/view-flattening /architecture/view-flattening
/docs/0.66/threading-model /architecture/threading-model
/docs/0.66/architecture-glossary /architecture/glossary

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2732 2048"><defs><style>.cls-2{fill:#374d9c}.cls-3{fill:#97aad8}.cls-4{fill:#d5e2f5}.cls-5{fill:#5971b5}.brandText,.cls-7{font-family:-apple-system,BlinkMacSystemFont,&apos;Segoe UI&apos;,Helvetica,Arial,sans-serif,&apos;Apple Color Emoji&apos;,&apos;Segoe UI Emoji&apos;,&apos;Segoe UI Symbol&apos;}.cls-7{font-size:120px;fill:#fff}.cls-13{fill:none;stroke:#010101;stroke-linecap:round;stroke-miterlimit:10;stroke-width:8px}</style></defs><g id="diagram"><path fill="#338846" d="M1462.68 1211.08l29.08 10.88-63.27 139.51-89.19-43.63 25.27-16.95 50.96 18.44 47.15-108.25z"/><path class="cls-2" d="M2671.28 816.45c-90.42 231.91-180.62 250.32-395.16 250.32S1904.4 961 1887.66 759.87c-20.32-244 173.92-306.9 388.46-306.9s502.34 88.59 395.16 363.48z"/><g id="cloud"><ellipse class="cls-2" cx="712.42" cy="515.05" rx="176.85" ry="170.79"/><circle class="cls-2" cx="940.36" cy="600.03" r="135.21"/><circle class="cls-2" cx="892.22" cy="461.47" r="65.3"/><circle class="cls-2" cx="997.7" cy="404.54" r="22.6"/><circle class="cls-2" cx="862.08" cy="357.66" r="20.09"/><circle class="cls-2" cx="656.33" cy="730.43" r="14.44"/></g><path class="cls-2" d="M620.33 1220.47c0 109.31-118.07 214.68-263.72 214.68s-263.72-56.93-263.72-214.68c0-109.32 118.07-172.11 263.72-172.11s263.72 16.74 263.72 172.11z"/><path class="cls-3" d="M2118.73 602.03v153.63l117.2 53.58 229.4-83.72V571.47l-204.28-25.18-142.32 55.74z"/><path class="cls-4" d="M2212.35 660.73l252.98-86.18v-3.08l-204.28-25.18-142.32 55.74V645l93.62 15.73z"/><path class="cls-5" d="M2118.73 602.05v153.61l117.2 53.58V652.45l-117.2-50.4z"/><path d="M1124 498.06L955.86 682.42c372.31 200.53 332.89 379.34 332.89 379.34l-94 25.31c129.06 33.79 157.31 125.52 157.31 125.52 50.79-96 152.12-156.24 152.12-156.24l-99.94-.43C1523.86 598.42 1124 498.06 1124 498.06zm-8.43 870.04l192.86 11.9c-378.57 633.33-914.84 156.31-914.84 156.31l-99.45 79.4c71.43-145.23 4.76-276.19 4.76-276.19 111.91 31 225.51-27.14 225.51-27.14l-30.27 105.72c378.57 233.33 621.43-50 621.43-50zM139.38 961c52.38-176.19 342.86-333.33 342.86-333.33l-57.14-38.1c140.47 26.19 195.23 0 195.23 0-50 85.72 0 216.67 0 216.67l-69-73.81C377.48 853.81 379.86 1111 379.86 1111zm1309.52 440.43l261.1-33.19c326 183 469.82-257.29 469.82-257.29h-73.81c152.38-78.57 164.28-150 164.28-150 23.81 109.53 140.48 150 140.48 150h-90.48C2068 2048 1448.9 1401.43 1448.9 1401.43z" fill="#71c9e4"/><text class="cls-7" transform="translate(660.81 547.82)">test</text><text class="cls-7" transform="translate(198.9 1263.3)">fix</text><text transform="translate(1131.05 1311.01)" fill="#7b1619" font-size="120" class="brandText">fail</text><text transform="translate(1491.76 1309.01)" fill="#1a4423" font-size="120" class="brandText">pass</text><text class="cls-7" transform="translate(2044.14 922.82)"><tspan letter-spacing="-.02em">r</tspan><tspan x="37.8" y="0">elease</tspan></text><path class="cls-3" d="M735.44 816.45h7.13c11.81 0 21.38-8.74 21.38-19.53v-169.4h-49.88v169.4c0 10.79 9.57 19.53 21.37 19.53z"/><path class="cls-5" d="M736.86 801.1h4.3c7.13 0 12.92-5.53 12.92-12.35V681.66h-30.14v107.09c0 6.82 5.78 12.35 12.92 12.35z"/><path class="cls-3" d="M808.28 816.45h7.13c11.81 0 21.38-8.74 21.38-19.53v-169.4H786.9v169.4c0 10.79 9.57 19.53 21.38 19.53z"/><path class="cls-4" d="M809.69 801.1H814c7.13 0 12.91-6.1 12.91-13.62v-118.1h-30.13v118.1c0 7.52 5.78 13.62 12.91 13.62z"/><path class="cls-3" d="M885.71 816.45h7.13c11.8 0 21.38-8.74 21.38-19.53v-169.4h-49.89v169.4c0 10.79 9.57 19.53 21.38 19.53z"/><path class="cls-5" d="M887.12 801.1h4.31c7.13 0 12.91-3.94 12.91-8.8V716H874.2v76.3c0 4.86 5.8 8.8 12.92 8.8z"/><path d="M414.77 1204.73l-47.48 94.79c8.37 18.42 41.86 21.77 41.86 21.77L447.51 1219c-5-20.1 10.92-36.51 29.92-11.39l-3.51 6.31 22.31 10.29 20.42-40.58-17.08-14.69c-17.33 8.71-17.33-18.42-17.33-18.42-63.05-38.51-114.95 13.4-114.95 13.4 75.35-3.35 61.95 18.42 61.95 18.42-5.02 25.08-14.47 22.39-14.47 22.39z" fill="#fff"/><path fill="#a21732" d="M1087.51 1211.08h43.54l-56.93 69.35 45.21 41.86-33.49 20.09-38.52-42.4-45.21 30.15h-34.74l64.89-53.4-47.72-48.21 40.18-8.37 24.49 39.35 38.3-48.42z"/><g id="tie"><path class="cls-13" d="M2364.91 762.17l-15.1-148.27-140.5-47.35"/><path class="cls-13" d="M2168.32 778.33l9.01-151.08 142.75-73.69"/><path class="cls-13" d="M2194.07 669.51c26.64-16.39 46.89-47.69 55.81-77 1.7-5.56 5.68-10.78 11.28-12.34.93 4.18-3.44 7.54-7.38 9.23a60 60 0 01-48.73-1 12.74 12.74 0 01-4.91-3.53c-1.68-2.24-1.88-5.21-2-8-.46-9.21-.2-20 7.26-25.45 6.73-4.88 16.2-2.86 23.72.65 9.28 4.33 17.53 10.54 25.7 16.7 5.16 3.89 10.95 9.69 8.74 15.76-4-7.79 1-19.4 8.48-24s16.74-4.92 25.48-4.4c5.26.31 10.64.93 15.29 3.4s8.44 7.17 8.42 12.43c0 7-6.4 12.25-12.73 15.17a54.18 54.18 0 01-28.95 4.61c-6.24-.73-13.7-4.28-13.43-10.56 3.78-1.48 8.14 1 10.26 4.46s2.6 7.63 3.27 11.63a103.12 103.12 0 0051.07 72.13"/></g></g></svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 KiB

Some files were not shown because too many files have changed in this diff Show More