init commit
|
@ -0,0 +1,16 @@
|
|||
# EditorConfig is awesome: https://editorconfig.org
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
|
||||
# Tab indentation (no size specified)
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.md]
|
||||
indent_size = 4
|
|
@ -0,0 +1,19 @@
|
|||
name: Publish docs via GitHub Pages
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Deploy docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout main
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Deploy docs
|
||||
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
EXTRA_PACKAGES: mkdocs-include-markdown-plugin
|
|
@ -1,5 +1,9 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/.svelte-kit
|
||||
/build
|
||||
/functions
|
||||
.venv
|
||||
/site
|
||||
/exports/**
|
||||
!/exports/.gitkeep
|
||||
|
||||
apps/web-bootstrap/public/model/**
|
||||
|
||||
|
|
1
.npmrc
|
@ -1 +0,0 @@
|
|||
engine-strict=true
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"ms-azuretools.vscode-azurefunctions"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach to Node Functions",
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"port": 9229,
|
||||
"preLaunchTask": "func: host start"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
{
|
||||
"azureFunctions.deploySubpath": "api",
|
||||
"azureFunctions.postDeployTask": "npm install",
|
||||
"azureFunctions.projectLanguage": "JavaScript",
|
||||
"azureFunctions.projectRuntime": "~3",
|
||||
"debug.internalConsoleOptions": "neverOpen",
|
||||
"azureFunctions.preDeployTask": "npm prune"
|
||||
}
|
|
@ -2,30 +2,20 @@
|
|||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "func",
|
||||
"command": "host start",
|
||||
"problemMatcher": "$func-node-watch",
|
||||
"isBackground": true,
|
||||
"dependsOn": "npm install",
|
||||
"type": "shell",
|
||||
"label": "yarn install",
|
||||
"command": "yarn install",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/api"
|
||||
"cwd": "${workspaceFolder}/apps/web-bootstrap"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"label": "npm install",
|
||||
"command": "npm install",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/api"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"label": "npm prune",
|
||||
"command": "npm prune --production",
|
||||
"label": "yarn start",
|
||||
"command": "yarn start",
|
||||
"problemMatcher": [],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/api"
|
||||
"cwd": "${workspaceFolder}/apps/web-bootstrap"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
87
README.md
|
@ -1,78 +1,81 @@
|
|||
# Scenario : The Loved Mutt
|
||||
# Scenario: Getting started with Lobe
|
||||
|
||||
According to the American Society for the Prevention of Cruelty to Animals (ASPCA), animal shelters receive about 3 million dogs annually - about 6 dogs per minute! While euthanasia rates have dropped, over 500,000 dogs are euthanized because they could not be matched with their original owners or an adoptive family.
|
||||
Ever wish you could be alerted when your package has arrived at your door without relying on less-than-accurate delivery-tracking apps? What about needing someone else count your reps during your workout?
|
||||
|
||||
Your team will aid a fictional adoption agency with deploying their website to the cloud. The application is designed to raise awareness of different dog breeds. A potential adopter can use the app to flip through pictures of various dogs and come up with potential names for each dog. This can be a great way to get a family excited about the possibility of adopting a new pet!
|
||||
[Lobe] is here to the rescue! [Lobe] is a free, private desktop application that has everything you need to take your machine learning ideas from prototype to production.
|
||||
|
||||
The agency has already created the website using [Svelte](https://svelte.dev/) and [Svelte Kit](https://kit.svelte.dev/) for the front end, [Azure Functions](https://docs.microsoft.com/azure/azure-functions/functions-overview?WT.mc_id=academic-28005-chrhar) and a [MongoDB API database](https://docs.mongodb.com/drivers/node/current/) for the back-end. They have provided documentation your team can review to gain an understanding of how it works, and run it locally for testing purposes.
|
||||
In this lab, you'll build an [image classification](https://www.lobe.ai/docs/welcome/welcome#what-is-image-classification) model to solve a problem that you and your team come up with together.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Each team member will need an Azure account. With [Azure for Students](https://aka.ms/a4s?WT.mc_id=academic-28005-chrhar), you can access $100 in free credit, and a large suite of free services!
|
||||
|
||||
### Skills
|
||||
Your team should be familiar with the following:
|
||||
|
||||
- Git and GitHub
|
||||
- [Forking](https://docs.github.com/github/getting-started-with-github/quickstart/fork-a-repo) and [cloning](https://docs.github.com/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github/cloning-a-repository) repositories
|
||||
- [Creating and managing branches](https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/making-changes-in-a-branch/managing-branches)
|
||||
- HTML, CSS and JavaScript
|
||||
- [Forking](https://docs.github.com/github/getting-started-with-github/quickstart/fork-a-repo) and [cloning](https://docs.github.com/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github/cloning-a-repository) repositories
|
||||
- [Creating and managing branches](https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/making-changes-in-a-branch/managing-branches)
|
||||
- Using a terminal (e.g., Bash, PowerShell, etc.)
|
||||
|
||||
### Hardware
|
||||
- A computer capable of running arbitrary code and on which you have administrative rights
|
||||
!!! Danger
|
||||
Lobe is not currently supported on Apple computers with the M1 chip
|
||||
- A stable internet connection (for setup and data download only)
|
||||
|
||||
### Software
|
||||
Each member of your team will also need the following software installed:
|
||||
|
||||
- [Lobe](https://www.lobe.ai/)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
- [Install git on macOS](https://git-scm.com/download/mac)
|
||||
- [Install git on Windows](https://git-scm.com/download/win)
|
||||
- [Install git on Linux](https://git-scm.com/download/linux)
|
||||
- [Visual Studio Code](https://code.visualstudio.com/)
|
||||
- [Install git on macOS](https://git-scm.com/download/mac)
|
||||
- [Install git on Windows](https://git-scm.com/download/win)
|
||||
- [Install git on Linux](https://git-scm.com/download/linux)
|
||||
- [Visual Studio Code](https://code.visualstudio.com/) or any another text editor or IDE (e.g., WebStorm, Notepad++, Atom, Brackets, etc.)
|
||||
- [Node.js](https://nodejs.org/)
|
||||
- [Install Node.js on Windows](https://docs.microsoft.com/windows/dev-environment/javascript/nodejs-on-windows?WT.mc_id=academic-28005-chrhar)
|
||||
- [Install Node.js on Linux or MacOS](https://github.com/nvm-sh/nvm#installing-and-updating)
|
||||
- [Azure Functions Core Tools](https://www.npmjs.com/package/azure-functions-core-tools), which can be installed by running the following command (after Node.js and npm are installed)
|
||||
- [Install Node.js on Windows](https://docs.microsoft.com/windows/dev-environment/javascript/nodejs-on-windows)
|
||||
- [Install Node.js on Linux or MacOS](https://github.com/nvm-sh/nvm#installing-and-updating)
|
||||
- [Yarn](https://yarnpkg.com/getting-started/install#per-project-install)
|
||||
|
||||
```bash
|
||||
npm i -g azure-functions-core-tools@3 --unsafe-perm true
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
A series of resources will be provided to help your team determine the appropriate steps for completion. The resources provided should provide your team with enough information to achieve each goal. If you get stuck, you can always ask a mentor for additional help.
|
||||
|
||||
A [sample of the site](https://calm-glacier-0b7804d10.azurestaticapps.net/) has been deployed so you can see how it looks in action.
|
||||
|
||||
## Exploring the source code
|
||||
|
||||
There are two key folders for the application: [src/routes](../src/routes), which contains the Svelte files used for the front-end, and [api](../api), which contains the Azure Functions used for the back-end. The flow of the application is as follows:
|
||||
The key folder for the application, [apps](./apps), contains a [starter web application](./apps/web-bootstrap) in which to drop an exported model from Lobe. The flow of the application is as follows:
|
||||
|
||||
1. A user navigates to the page and is presented with the option to login (and sees a picture of a cute dog)
|
||||
1. User authenticates using their GitHub credentials
|
||||
1. After authenticating, the user can now name dogs. Each time they name a dog, it's saved to a Mongo API database.
|
||||
1. The user can click on the "named dogs" link to review the list of dogs they've named
|
||||
1. A user navigates to the page and is presented with the option to take a photo or upload a photo
|
||||
1. User takes a photo using their device's camera or uploads a photo from their device.
|
||||
1. After submitting the photo, the model will attempt to classify and tag the photo.
|
||||
|
||||
> **Note**: No updates to the application code will be made during this workshop. Your team will be able to successfully complete the workshop without any experience with Svelte. The only file your team will modify is *staticwebapp.config.json*.
|
||||
|
||||
![Flow of the app](./goals/media/app-flow.svg)
|
||||
!!! Info
|
||||
No updates to the application code will be made during this workshop. Your team will be able to successfully complete the workshop without any experience with React. The only files your team will add are generated by the Lobe app exporting function.
|
||||
|
||||
## Goals
|
||||
|
||||
Your team will obtain the starter, deploy the application to the cloud, enable authentication, and create and configure the database.
|
||||
Your team will obtain the starter, train the model, and use the model in locally-run web application.
|
||||
|
||||
1. [Obtain the source code](./goals/0-obtain-source.md):
|
||||
The first step when working with any codebase is to download it. Your team's first goal will be to obtain the code from GitHub.
|
||||
1. [Deploy to the cloud](./goals/1-deploy.md):
|
||||
Because the Loved Mutt wants the application to be publicly available, your team will need to deploy the application. For this workshop, your team will use Azure Static Web Apps, which is able to host the application and run the Azure Functions.
|
||||
1. [Enable authentication](./goals/2-authentication.md):
|
||||
A key component of the app is allowing users to name dogs and have the names saved to a list. This requires authentication, which is built-in to Azure Static Web Apps. To achieve this goal, your team will configure the application so *only* GitHub authentication is enabled.
|
||||
1. [Add a database](./goals/3-database.md):
|
||||
Saving information typically requires a database, and this application is no different. The code has already been added to use a Mongo API database, which is available through Cosmos DB on Azure. Your team will create a Cosmos DB account, and then update the application on Azure Static Web Apps to use your newly created database.
|
||||
|
||||
## Validation
|
||||
|
||||
This workshop is designed to be a goal-oriented self-exploration of Azure and related technologies. Your team can use the [validation tool](https://ashy-mushroom-0609d7c10.azurestaticapps.net/) to confirm the *Deploy to the cloud* and *Enable authentication* goals have been met. Validating the final goal of *Add a database* will be manual - you'll use the application your team deployed and confirm it works as expected.
|
||||
2. [Train the model](./goals/1-train.md):
|
||||
Because we need a model to classify photos, your team will first need to train a model. For this workshop, your team will Lobe, which will train the model for you based on photos and tags that your team selects.
|
||||
3. [Test the model](./goals/2-test.md):
|
||||
A key aspect of training a model is testing and improving it. Here you will take or upload photos to improve the model by fine-tuning it's training.
|
||||
4. [Export the model](./goals/3-export.md):
|
||||
Our webapp will only work if it has a model to run! You will use Lobe to export your model as a TensorFlow.js script and model files.
|
||||
5. [Run the app](./goals/4-run.md):
|
||||
Run your app! For this goal, you will run your web application on your local computer and try to classify images with it.
|
||||
|
||||
## Where do we go from here?
|
||||
|
||||
This project is designed as a potential seed for future development. If you were to continue with this idea, your team could potentially:
|
||||
|
||||
- Use the [Petfinder API](https://www.petfinder.com/developers/) to create an application to match potential adopters with dogs
|
||||
- Use the [Bing Maps API](https://docs.microsoft.com/bingmaps/getting-started/?WT.mc_id=academic-28005-chrhar) to search based on location
|
||||
- Use [Custom Vision](https://azure.microsoft.com/services/cognitive-services/custom-vision-service/?WT.mc_id=academic-28005-chrhar) to identify dog breeds
|
||||
- Deploy and [run your model on a Raspberry Pi](https://learn.adafruit.com/machine-learning-101-lobe-braincraft) and use a camera module to take photos!
|
||||
- Build a [Rock, Paper, Scissors game](https://learn.adafruit.com/lobe-rock-paper-scissors) and play against your model!
|
||||
- Build a [Package Detector](https://learn.adafruit.com/build-an-ml-package-detector) and get notifications when a package is left at your door!
|
||||
|
||||
|
||||
<!-- References -->
|
||||
[Lobe]: https://www.lobe.ai/
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
*.js.map
|
||||
*.ts
|
||||
.git*
|
||||
.vscode
|
||||
local.settings.json
|
||||
test
|
||||
tsconfig.json
|
|
@ -1,94 +0,0 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TypeScript output
|
||||
dist
|
||||
out
|
||||
|
||||
# Azure Functions artifacts
|
||||
bin
|
||||
obj
|
||||
appsettings.json
|
||||
local.settings.json
|
|
@ -1,19 +0,0 @@
|
|||
module.exports = {
|
||||
getUserId(req) {
|
||||
if (!req) throw 'req parameter is required';
|
||||
const header = req.headers['x-ms-client-principal'];
|
||||
const encoded = Buffer.from(header, 'base64');
|
||||
const decoded = encoded.toString('ascii');
|
||||
const clientPrincipal = JSON.parse(decoded);
|
||||
return clientPrincipal.userId;
|
||||
},
|
||||
getMongoClient() {
|
||||
const { MongoClient } = require("mongodb");
|
||||
const uri = process.env.MONGO_CONNECTION_STRING;
|
||||
const client = new MongoClient(uri, {
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
});
|
||||
return client;
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"version": "2.0",
|
||||
"logging": {
|
||||
"applicationInsights": {
|
||||
"samplingSettings": {
|
||||
"isEnabled": true,
|
||||
"excludedTypes": "Request"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extensionBundle": {
|
||||
"id": "Microsoft.Azure.Functions.ExtensionBundle",
|
||||
"version": "[1.*, 2.0.0)"
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"bindings": [
|
||||
{
|
||||
"authLevel": "anonymous",
|
||||
"type": "httpTrigger",
|
||||
"direction": "in",
|
||||
"name": "req",
|
||||
"methods": [
|
||||
"get",
|
||||
"post"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "http",
|
||||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
module.exports = async function (context, req) {
|
||||
const {getUserId, getMongoClient} = require('../helpers.js')
|
||||
|
||||
const userId = getUserId(req);
|
||||
const client = getMongoClient();
|
||||
|
||||
try {
|
||||
client.connect();
|
||||
const database = client.db('loved-mutt');
|
||||
const collection = database.collection('dogs');
|
||||
const namedDogs = await collection.find({userId}).toArray();
|
||||
context.res.body = {namedDogs};
|
||||
} catch (ex) {
|
||||
context.res = {
|
||||
body: ex,
|
||||
status: 500
|
||||
}
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"name": "Azure"
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
{
|
||||
"name": "api",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"bl": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
|
||||
"integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
|
||||
"requires": {
|
||||
"readable-stream": "^2.3.5",
|
||||
"safe-buffer": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"bson": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
|
||||
"integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg=="
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||
},
|
||||
"denque": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz",
|
||||
"integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ=="
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"memory-pager": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
|
||||
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
|
||||
"optional": true
|
||||
},
|
||||
"mongodb": {
|
||||
"version": "3.6.6",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.6.tgz",
|
||||
"integrity": "sha512-WlirMiuV1UPbej5JeCMqE93JRfZ/ZzqE7nJTwP85XzjAF4rRSeq2bGCb1cjfoHLOF06+HxADaPGqT0g3SbVT1w==",
|
||||
"requires": {
|
||||
"bl": "^2.2.1",
|
||||
"bson": "^1.1.4",
|
||||
"denque": "^1.4.1",
|
||||
"optional-require": "^1.0.2",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"saslprep": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"optional-require": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz",
|
||||
"integrity": "sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA=="
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||
},
|
||||
"saslprep": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
||||
"integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"sparse-bitfield": "^3.0.3"
|
||||
}
|
||||
},
|
||||
"sparse-bitfield": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
|
||||
"integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"memory-pager": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"name": "api",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"start": "func start",
|
||||
"test": "echo \"No tests yet...\""
|
||||
},
|
||||
"dependencies": {
|
||||
"mongodb": "^3.6.6"
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/proxies",
|
||||
"proxies": {}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"bindings": [
|
||||
{
|
||||
"authLevel": "anonymous",
|
||||
"type": "httpTrigger",
|
||||
"direction": "in",
|
||||
"name": "req",
|
||||
"methods": [
|
||||
"get",
|
||||
"post"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "http",
|
||||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
module.exports = async function (context, req) {
|
||||
const {getUserId, getMongoClient} = require('../helpers')
|
||||
const client = getMongoClient();
|
||||
|
||||
try {
|
||||
await client.connect();
|
||||
const database = client.db('loved-mutt');
|
||||
const collection = database.collection('dogs');
|
||||
const doc = {
|
||||
imageUrl: req.body.imageUrl,
|
||||
dogType: req.body.dogType,
|
||||
name: req.body.name,
|
||||
userId: getUserId(req)
|
||||
};
|
||||
const result = await collection.insertOne(doc);
|
||||
context.res = {
|
||||
body: result
|
||||
};
|
||||
} catch (ex) {
|
||||
context.res = {
|
||||
body: ex,
|
||||
status: 500
|
||||
}
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"name": "Azure"
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"IsEncrypted": false,
|
||||
"Values": {
|
||||
"AzureWebJobsStorage": "",
|
||||
"FUNCTIONS_WORKER_RUNTIME": "node",
|
||||
"MONGO_CONNECTION_STRING": "mongodb://localhost"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +1,23 @@
|
|||
# Goal 0: Obtain the source code
|
||||
|
||||
Whenever working on any project, step 0 is typically to fork and clone the repository, and install the necessary tooling. To streamline this process, The Loved Mutt have provided your team with step-by-step instructions.
|
||||
Whenever working on any project, step 0 is typically to fork and clone the repository, and install the necessary tooling. To streamline this process, this project has provided your team with step-by-step instructions.
|
||||
|
||||
## About the application
|
||||
|
||||
The front-end was created using [Svelte](https://svelte.dev) and [SvelteKit](https://kit.svelte.dev). No experience with Svelte is required.
|
||||
|
||||
The back-end uses [Azure Functions](https://docs.microsoft.com/azure/azure-functions/functions-overview?WT.mc_id=academic-28005-chrhar), the Azure serverless platform. Azure Functions are supported natively by Azure Static Web Apps, which is where your team will deploy the application. As your team gets to each goal, relevant notes about the application will be provided.
|
||||
|
||||
> Note: Your team does not have to run the application locally to complete this workshop. If you wish to configure your system for local development, [setup steps have been provided](./4-optional-run-locally).
|
||||
The [web application](./apps/web-bootstrap) is from the Lobe team's [starter for web apps](https://github.com/lobe/web-bootstrap). The Lobe Web Bootstrap project takes the machine learning model created in Lobe, and adds it to a project that runs in the browser using [React](https://reactjs.org), [Create React App](https://github.com/facebook/create-react-app), [TypeScript](https://www.typescriptlang.org/), and [TensorFlow.js](https://www.tensorflow.org/js).
|
||||
|
||||
## Generate the repository and obtain the source code
|
||||
|
||||
The starter repository (or repo) has been created as a [GitHub template](https://docs.github.com/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/creating-a-repository-from-a-template). One member of your team will generate a repository from this template, while the remaining team members will fork it. All team members will clone the repository locally.
|
||||
|
||||
1. One team member: [generate the repository](https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fgeektrainer%2Floved-mutt%2Fgenerate) by using the template. Your team can give the repository whatever name you like.
|
||||
1. All other team members will [fork](https://docs.github.com/github/getting-started-with-github/quickstart/fork-a-repo) the new repository.
|
||||
1. All team members will [clone the repository](https://docs.github.com/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github/cloning-a-repository), which can be accomplished by running the following command.
|
||||
1. For all team members, after cloning the repository, open the local folder in Visual Studio Code
|
||||
1. One team member: [generate the repository](https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fmicrosoft%2Fhack-workshop-lobe%2Fgenerate) by using the template. Your team can give the repository whatever name you like.
|
||||
|
||||
## Congratulations
|
||||
2. All other team members will [fork](https://docs.github.com/github/getting-started-with-github/quickstart/fork-a-repo) the new repository.
|
||||
|
||||
Your team has successfully obtained the starter code and explored the app! Now it's time to [deploy the project to the cloud](1-deploy.md).
|
||||
3. All team members will [clone the repository](https://docs.github.com/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github/cloning-a-repository).
|
||||
|
||||
4. For all team members, after cloning the repository, open the local folder in your favorite code editor
|
||||
|
||||
## Congratulations!
|
||||
|
||||
Your team has successfully obtained the starter code and explored the codebase!
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
# Goal 1: Deploy to the cloud
|
||||
|
||||
The Loved Mutt application is a full-stack application (or sometimes called a static web app). All the necessary code for both the client and server are contained inside one project, and the client uses a framework (Svelte in this case). The application also requires access to a database which uses Mongo DB APIs, and the ability to enable authentication.
|
||||
|
||||
For this goal, your team will deploy the application to Azure. Future goals will focus on authentication and the database.
|
||||
|
||||
## The Azure Service
|
||||
|
||||
[Azure Static Web Apps](https://docs.microsoft.com/azure/static-web-apps/overview) is a service which builds and deploys full stack web apps to Azure from a code repository. It provides hosting for static files (HTML, CSS, JavaScript, etc.) and server-side code through serverless Azure Functions. Azure Static Web Apps can host applications written with any front-end framework, including React, Angular, Vue.js and Svelte. Azure Functions can be used to access server resources such as databases or other APIs. A site deployed to Azure Static Web Apps automatically scales, and can even have custom domains.
|
||||
|
||||
Deployment is managed through [GitHub actions](https://github.com/features/actions). When you create a static web app, Azure will automatically create a workflow file on your GitHub repository. This allows you to use GitHub just as you normally would, and updates to *main* or whatever branch you specify are automatically deployed to Azure.
|
||||
|
||||
## Application notes
|
||||
|
||||
When deploying to Azure Static Web Apps, you will need to configure three key folders. These folder names may change based on the application or framework you're using. Defaults are provided when using common frameworks. You can consult the table below for a quick overview of these folders, and the setting necessary for this application.
|
||||
|
||||
Folder name | Description | Setting
|
||||
----------------|------------------------------------------------------------------------------------|-----------
|
||||
App location | Root of the application | **/**
|
||||
Output location | Name of any folders generated by the build process (like *build*, *lib* or *dist*) | **build**
|
||||
API location | Location of the API when using Azure Functions for server-side code | **api**
|
||||
|
||||
## Success criteria
|
||||
|
||||
Your team will work together to deploy the application to Azure using Azure Static Web Apps. Your team will have achieved this goal when the following success criteria are met:
|
||||
|
||||
- The shelter's site has been successfully deployed to Azure Static Web Apps
|
||||
- You can open the index page on the newly created Azure Static Web App
|
||||
|
||||
## Validation
|
||||
|
||||
To validate your team's work with the [automated tool](https://ashy-mushroom-0609d7c10.azurestaticapps.net/), enter the newly created URL on Azure Static Web Apps into the tool.
|
||||
|
||||
## Resources
|
||||
|
||||
Your team might find these resources helpful:
|
||||
|
||||
- [What is Azure Static Web Apps](https://docs.microsoft.com/azure/static-web-apps/overview?WT.mc_id=academic-28005-chrhar)
|
||||
- [Quickstart: Create a static web app - Azure Portal](https://docs.microsoft.com/azure/static-web-apps/get-started-portal?tabs=vanilla-javascript#create-a-static-web-app?WT.mc_id=academic-28005-chrhar)
|
||||
- [Quickstart: Building your first static site - Visual Studio Code](https://docs.microsoft.com/azure/static-web-apps/getting-started?tabs=vanilla-javascript#create-a-static-web-app?WT.mc_id=academic-28005-chrhar)
|
||||
|
||||
## Tips
|
||||
|
||||
- After successfully deploying to Azure, run `git pull` to pull down the newly generated workflow file.
|
||||
- Your team may find the [Azure Static Web Apps Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurestaticwebapps) helpful
|
||||
- The login page **will not** work yet; your team will enable authentication for the next goal
|
||||
|
||||
## Final result
|
||||
|
||||
After you deploy and navigate to the site, you should see a page which looks like the following:
|
||||
|
||||
![Screenshot of the starting page, showing a link to login, a picture of a dog with the heading Isn't this a cute Pekinese dog?](./media/starter-page.png)
|
||||
|
||||
## Next challenge
|
||||
|
||||
Once you've deployed your application, it's time to [add authentication](./2-authentication.md).
|
|
@ -0,0 +1,73 @@
|
|||
# Goal 1: Create and train your model
|
||||
|
||||
[Lobe] is a free, private desktop application that has everything you need to take your machine learning ideas from prototype to production.
|
||||
|
||||
For this goal, your team will train a model using images either captured with your computer camera or imported into the desktop application. Future goals will focus on testing and running your trained model in a web application.
|
||||
|
||||
## What is machine learning?
|
||||
Machine learning is software that learns to perform a task from a collection of examples rather than through a person explicitly defining rules and formulas. This learning software is called a `model`. Teaching a model through examples is called `training`.
|
||||
|
||||
### What is image classification?
|
||||
Image classification is categorizing an image into a single label to represent its content. Apps using image classification could:
|
||||
|
||||
1. Identify a plant's species in the wild.
|
||||
1. Send you photos of a new bird that just started showing up at your bird feeder
|
||||
1. Count the number of push-ups you’ve done in a workout
|
||||
1. Alert you when a shelf is empty
|
||||
1. Read signs in your environment
|
||||
|
||||
### Lobe
|
||||
|
||||
[Lobe] is not doing any reasoning or understanding of the content in your images. Image classification learns to find any patterns from your images - things like textures, colors, and shapes - that can be used to separate your labels.
|
||||
|
||||
Here are some helper questions to answer to make choosing your labels easier:
|
||||
|
||||
1. What information would
|
||||
1. How complex is the object?
|
||||
1. How similar are the objects with the same label? How different are the objects that have different labels?
|
||||
|
||||
!!! Important "The `None` label"
|
||||
Make sure to use a catch-all label like `None` to show unrelated images.
|
||||
|
||||
Lobe will always predict one of your labels even if your image does not contain any related content. If you expect your model to see these types of images, create a `None` label and add variations of these images as examples. You can use this `None` label as a placeholder when waiting for relevant predictions.
|
||||
|
||||
|
||||
## Success criteria
|
||||
|
||||
Your team will work together to create and train a model using [Lobe]. Your team will have achieved this goal when the following success criteria are met:
|
||||
|
||||
- The model has 10-20 images for each label
|
||||
- The model has at least 3-4 labels, not including the `None` label
|
||||
|
||||
## Resources
|
||||
|
||||
Your team might find these resources helpful:
|
||||
|
||||
- [What is labeling?](https://www.lobe.ai/docs/label/label)
|
||||
- [What types of images should I collect?](https://www.lobe.ai/docs/label/label#accordion-what-types-of-images-should-i-collect)
|
||||
- [Example image datasets from TensorFlow](https://www.tensorflow.org/datasets/catalog/overview#image_classification)
|
||||
|
||||
|
||||
## Final result
|
||||
|
||||
After you finish training your model, you should see a page which looks like the following:
|
||||
|
||||
![Screenshot of the starting page, showing a link to login, a picture of a dog with the heading Isn't this a cute Pekinese dog?](./media/lobe_label-complete.png){style="width:100%"}
|
||||
|
||||
## Tips
|
||||
|
||||
💡 Be mindful of the capabilities of your team's computers! Here are some things that can impact the amount of compute power and/or time it will take to train your model:
|
||||
|
||||
- The number of images in your dataset
|
||||
- The number of labels
|
||||
- The average file size of the images in your dataset
|
||||
|
||||
💡 The label progress bars fill up as you add images. The number of photos needed will vary:
|
||||
![Screenshot of the Lobe application user interface, which shows progress bars underneath labels with the number of photos for each label](./media/lobe-ui_label-progress-bars.png){style="width:30%"}
|
||||
|
||||
## Next challenge
|
||||
|
||||
Once you've trained your model, it's time to [fine tune it](./2-test.md).
|
||||
|
||||
<!-- References -->
|
||||
[Lobe]: https://www.lobe.ai
|
|
@ -1,101 +0,0 @@
|
|||
# Goal 2 - Authentication
|
||||
|
||||
Your team should be proud! The application is now deployed an application to the cloud!
|
||||
|
||||
You may remember the primary workflow for the application is to allow users to come up with names for different dogs. Because users will want to see the names they create, your team will need a way to enable authentication.
|
||||
|
||||
## The Azure Service
|
||||
|
||||
[Authentication and authorization](https://docs.microsoft.com/azure/static-web-apps/authentication-authorization) are built-in to Azure Static Web Apps. No additional configuration is required to allow users to login with Azure Active Directory, Twitter or GitHub authentication. You can add a link to the login page, and then add the necessary code to determine the name of the user and any additional actions you may wish to take.
|
||||
|
||||
## Application notes
|
||||
|
||||
Open *src/routes/index.svelte*, and notice the following JavaScript function:
|
||||
|
||||
```javascript
|
||||
async function getUserInfo() {
|
||||
// retrieve the current user from Azure Static Web Apps
|
||||
const response = await fetch("/.auth/me");
|
||||
// get the JSON
|
||||
const payload = await response.json();
|
||||
// clientPrincipal is the property with the information
|
||||
if (payload.clientPrincipal) {
|
||||
// user is authenticated
|
||||
// userDetails contains the username
|
||||
return payload.clientPrincipal.userDetails;
|
||||
} else {
|
||||
// if clientPrincipal is null, the session is anonymous
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This helper function is used to retrieve the current user. The key property is `clientPrincipal`. If `clientPrincipal` is `null`, it means the user is anonymous; if it contains a value the user has authenticated. The path */.auth/me* is built-in to Azure Static Web Apps to provide access to the current user.
|
||||
|
||||
Further down in *index.svelte*, you will notice the following code to display the user or a link to the login page:
|
||||
|
||||
```html
|
||||
{#await getUserInfo()}
|
||||
Getting user info...
|
||||
{:then username}
|
||||
{#if username}
|
||||
<div class="center">
|
||||
Welcome, {username}! See your
|
||||
<a href="named-dogs">named dogs</a>!
|
||||
</div>
|
||||
|
||||
<!-- snipped code to display buttons -->
|
||||
{:else}
|
||||
<div class="center">
|
||||
<a href="/login" class="center">Login to name the dogs!</a>
|
||||
</div>
|
||||
<!-- snipped code to display generic message -->
|
||||
{/if}
|
||||
{/await}
|
||||
```
|
||||
|
||||
The Svelte `await` is used to call `getUserInfo()`. After it's loaded, we look at the `username`. If a username exists, we display it and the buttons for naming dogs. If there is no `username`, the user is anonymous.
|
||||
|
||||
Notice the link to */login*. This will become **the key to this goal**. Your team will make the appropriate application updates to ensure */login* displays a login page.
|
||||
|
||||
> Note: The [await](https://svelte.dev/tutorial/await-blocks) and [if/else](https://svelte.dev/tutorial/if-blocks) blocks are part of [Svelte](https://svelte.dev). `await` allows you to display a loading message when making a potentially lengthy call (like accessing a server resource). `if/else` allows you to integrate logic into your HTML.
|
||||
|
||||
## Success criteria
|
||||
|
||||
Your team will make the necessary updates to the application to enable authentication. Your team will have achieved this goal when the following success criteria are met:
|
||||
|
||||
- The necessary updates have been applied to enable GitHub authentication via the path **/login**
|
||||
- After the updated site is deployed, a user can complete the following flow:
|
||||
- Navigate to the index page
|
||||
- Select the *Login to name the dogs!* link
|
||||
- Be redirected to the GitHub authentication page
|
||||
- Be redirected to the index page
|
||||
|
||||
## Validation
|
||||
|
||||
To validate your lab with the automated tool, enter the contents of *staticwebapp.config.json* into the [validation tool](https://ashy-mushroom-0609d7c10.azurestaticapps.net/).
|
||||
|
||||
## Resources resources
|
||||
|
||||
Your team might find these resources helpful:
|
||||
|
||||
- [Azure Static Web Apps - Login](https://docs.microsoft.com/azure/static-web-apps/authentication-authorization#login?WT.mc_id=academic-28005-chrhar)
|
||||
- [Configure Azure Static Web Apps](https://docs.microsoft.com/azure/static-web-apps/configuration?WT.mc_id=academic-28005-chrhar)
|
||||
- [Access user information in Azure Static Web Apps](https://docs.microsoft.com/azure/static-web-apps/user-information?tabs=javascript&WT.mc_id=academic-28005-chrhar)
|
||||
- [Troubleshooting deployment and runtime errors](https://docs.microsoft.com/azure/static-web-apps/troubleshooting?WT.mc_id=academic-28005-chrhar)
|
||||
|
||||
## Tips
|
||||
|
||||
- The only file which needs to be modified is *staticwebapp.config.json*
|
||||
- Remember to push updates to the main branch to redeploy to Azure Static Web Apps
|
||||
- Naming dogs **will not** work yet; your team will enable the database for the next goal
|
||||
|
||||
## Final result
|
||||
|
||||
After enabling authentication and logging in via GitHub, the page should look like this:
|
||||
|
||||
![Screenshot of the starting page, showing a textbox where you can name the dog, and the header What would you call this cute Pyrenees dog?](./media/authentication.png)
|
||||
|
||||
## Next challenge
|
||||
|
||||
Once your team has configured authentication and authorization, you can [add a database](./3-database.md).
|
|
@ -0,0 +1,59 @@
|
|||
# Goal 2: Test and refine
|
||||
|
||||
Your team should be proud! You've created your model!
|
||||
|
||||
But how do you know if it's working correctly? You have to test it! Fortunately, testing in Lobe will also improve your model as you test.
|
||||
|
||||
## Testing the model
|
||||
|
||||
The way you test and improve your model is by *using* it - that's where the "learning" in machine learning comes from. With Lobe, this is a simple task and let's you see how and why the model is classifying some of your images better than others.
|
||||
|
||||
Here are some things to keep in mind as you refine your model:
|
||||
|
||||
- Understand your problem - start simple, expand over time. Break your scenario down into smaller experiments to prototype and then expand over time.
|
||||
|
||||
- Lobe only learns from the examples you import. Try to collect examples that cover the different types of images your model will see and make predictions on in the future.
|
||||
|
||||
- More images always help - new and unique images are better. The more unique and different the images are, the better your model will learn to generalize.
|
||||
|
||||
- If you can’t classify the label from looking at an image, it will also be difficult for Lobe. Make the image content as large and relevant to your label as possible.
|
||||
|
||||
!!! Danger "Biases in data sets"
|
||||
Data is collected by humans and humans have biases.
|
||||
|
||||
Bias in machine learning models can be difficult to identify because a model just replicates and reinforces pre-existing biases in the data set.
|
||||
|
||||
Representation in your model often should not replicate representation in real life. For accurate classification, you need to have equal representation for each of your labels.
|
||||
|
||||
## Application notes
|
||||
|
||||
- Testing your model happens in the `Use` tab. Like the previous steps, you can import images or use your device camera.
|
||||
- Tune your model by accepting or rejecting the label that the model suggests for the image(s)
|
||||
- When you have filled the label bar completely, your model has been fine-tuned to achieve 100% classification accuracy
|
||||
<figure>
|
||||
![Screenshot of Lobe user interface, showing the prediction accuracy rate for all labels and in aggregate](./media/lobe-ui_improve-label.png){style="width:50%"}
|
||||
<figcaption>That's not a moth! Reject!</figcaption>
|
||||
</figure>
|
||||
## Success criteria
|
||||
|
||||
Your team will make the necessary updates to the model via testing. Your team will have achieved this goal when the following success criteria are met:
|
||||
|
||||
- Your test images equal or outnumber the training images
|
||||
- The model accurately classifies an image 98% of the time or higher
|
||||
|
||||
## Resources
|
||||
|
||||
Your team might find these resources helpful:
|
||||
|
||||
- [Lobe - Improving the model](https://www.lobe.ai/docs/improving/improving)
|
||||
- [Lobe - Working with your results](https://www.lobe.ai/docs/results/results)
|
||||
|
||||
## Final result
|
||||
|
||||
After improving your model, you should see something like the following for your labels:
|
||||
|
||||
![Screenshot of Lobe user interface, showing the prediction accuracy rate for all labels and in aggregate](./media/lobe-ui_label-prediction-rates.png){style="width:30%"}
|
||||
|
||||
## Next challenge
|
||||
|
||||
Once your team has improved the model, you can [export it](./3-export.md)!
|
|
@ -1,63 +0,0 @@
|
|||
# Goal 3: Create and configure a cloud-based database
|
||||
|
||||
Congratulations on enabling authentication for the application! Authentication commonly a key component to any web project.
|
||||
|
||||
Similarly, almost every web application will need some form of a database. If you remember the primary workflow again, the goal is to have users create names for the dogs, which will then be saved to a database. The time has come for your team to create and configure the database.
|
||||
|
||||
## The Azure Service
|
||||
|
||||
[Azure Cosmos DB](https://docs.microsoft.com/azure/cosmos-db/introduction?WT.mc_id=academic-28005-chrhar) is a document or [NoSQL](https://en.wikipedia.org/wiki/NoSQL) database service. Cosmos DB provides different APIs you can use to access it, including an [API for MongoDB](https://docs.microsoft.com/azure/cosmos-db/introduction?WT.mc_id=academic-28005-chrhar), which allows the Node.js MongoDB client to interact with it. Most recently, [Cosmos DB serverless](https://docs.microsoft.com/azure/cosmos-db/serverless?WT.mc_id=academic-28005-chrhar) was released, which similar to other serverless products charges are based on usage. Your team will create a Cosmos DB account and configure the application to use your new Cosmos DB deployment.
|
||||
|
||||
## Application notes
|
||||
|
||||
Open */api/helpers.js*. This file contains a couple of functions to aid the development of the server-side portion of this project. The key function is `getMongoClient`.
|
||||
|
||||
```javascript
|
||||
getMongoClient() {
|
||||
const { MongoClient } = require("mongodb");
|
||||
const uri = process.env.MONGO_CONNECTION_STRING;
|
||||
const client = new MongoClient(uri, {
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
});
|
||||
return client;
|
||||
}
|
||||
```
|
||||
|
||||
`getMongoClient` contains the code to connect to the database. Notice `process.env.MONGO_CONNECTION_STRING`, which is using [process.env](https://nodejs.org/dist/latest-v14.x/docs/api/process.html#process_process_env) to read an environment variable. After creating the database on Azure, your team will need to ensure the environment variable is set correctly on the static web app in Azure.
|
||||
|
||||
When creating your team's instance of Cosmos DB, use the Serverless option. This will allow you to pay for usage rather than a fix fee. For this sample application, the costs will be extremely cheap (under $1 US). Fortunately, since you will be using [Azure for Students](https://aka.ms/a4s), you have free credit!
|
||||
|
||||
## Success criteria
|
||||
|
||||
Your team will create the necessary resources and make the necessary updates in Azure to enable the application to access a Cosmos DB database. Your team will have achieved this goal when the following success criteria are met:
|
||||
|
||||
- A Cosmos DB Account has been created
|
||||
- The static web app configuration has been updated in Azure
|
||||
- In the website, you are able to perform the following tasks:
|
||||
- Login to the site
|
||||
- Name a dog
|
||||
- Navigate to the favorites page by selecting *review the list* to see the dogs you named
|
||||
|
||||
## Validation
|
||||
|
||||
Have a mentor check your site to ensure everything has been completed!
|
||||
|
||||
## Resources
|
||||
|
||||
Your team might find these resources helpful:
|
||||
|
||||
- [Create an Azure Cosmos DB account](https://docs.microsoft.com/azure/cosmos-db/create-cosmosdb-resources-portal#create-an-azure-cosmos-db-account?WT.mc_id=academic-28005-chrhar)
|
||||
- [Get the MongoDB connection string by using the quick start](https://docs.microsoft.com/azure/cosmos-db/connect-mongodb-account#get-the-mongodb-connection-string-by-using-the-quick-start?WT.mc_id=academic-28005-chrhar)
|
||||
- [Configure application settings for Azure Static Web Apps](https://docs.microsoft.com/azure/static-web-apps/application-settings?WT.mc_id=academic-28005-chrhar)
|
||||
- [Troubleshooting deployment and runtime errors](https://docs.microsoft.com/azure/static-web-apps/troubleshooting?WT.mc_id=academic-28005-chrhar)
|
||||
|
||||
## Tips
|
||||
|
||||
- The application will automatically create a database and collection to store data; the only resource your team needs to [create is an Azure Cosmos DB account](https://docs.microsoft.com/azure/cosmos-db/create-cosmosdb-resources-portal#create-an-azure-cosmos-db-accoun?WT.mc_id=academic-28005-chrhart)
|
||||
|
||||
## Final result
|
||||
|
||||
After creating database and configuring the application, you should now be able to login, name a dog, and see the list of all dogs you've named.
|
||||
|
||||
![Screenshot of the named dogs page, showing the list of named dogs with a Pyrenees named as Steve](./media/named-dogs.png)
|
|
@ -0,0 +1,58 @@
|
|||
# Goal 3: Export the model
|
||||
|
||||
Congratulations on successfully training and fine-tuning your model! Now it's time to actually use it.
|
||||
|
||||
To use the model in the [sample web application], you first need to *export* the model.
|
||||
|
||||
## Application notes
|
||||
|
||||
### How to export the model
|
||||
1. In the `Use` tab, select the `Export` button
|
||||
2. Select `TensorFlow.js`
|
||||
3. Choose the [/exports](../exports) folder in the project as the destination folder
|
||||
|
||||
### Exported model folder structure
|
||||
There are a few key elements in the main export directory. Your model in json format (model.json), its weights as binary shard files (.bin), and signature.json which contains information about your Lobe project.
|
||||
|
||||
This is what the folder structure of your exported model should look like:
|
||||
```
|
||||
exports/
|
||||
<Project Name> TensorFlowJS/
|
||||
example/
|
||||
package.json
|
||||
tfjsExample.ts
|
||||
README.md
|
||||
groupN-shardXofY.bin
|
||||
labels.txt
|
||||
model.json
|
||||
signature.json
|
||||
```
|
||||
|
||||
### Move model into web application
|
||||
|
||||
Copy the contents from your exported model folder into `apps/web-bootstrap/public/model` folder in the [sample web application].
|
||||
|
||||
|
||||
## Success criteria
|
||||
|
||||
Your team will have achieved this goal when the following success criteria are met:
|
||||
|
||||
- Your team has exported TensorFlow.js model
|
||||
- The model files have been moved into the `public/model` directory of the [sample web application]
|
||||
|
||||
## Resources
|
||||
|
||||
Your team might find these resources helpful:
|
||||
|
||||
- [Lobe - Exporting your model](https://www.lobe.ai/docs/export/export)
|
||||
|
||||
## Tips
|
||||
|
||||
- While you don't need the `example/` folder or the `labels.txt` file, moving these into the web application will not cause any problems.
|
||||
|
||||
## Final result
|
||||
|
||||
After exporting the model and copying it into the web application, you can now move to the final step: [run the web app and use your model](./4-run.md)!
|
||||
|
||||
<!-- References -->
|
||||
[sample web application]: ../apps/web-bootstrap
|
|
@ -1,47 +0,0 @@
|
|||
# Running the application locally
|
||||
|
||||
As highlighted, your team does not have to run the application locally to successfully complete the workshop. However, if you want to setup your system, you'll need to perform one local install, and make a note about
|
||||
|
||||
## Install MongoDB
|
||||
|
||||
The workshop application uses MongoDB API. To perform local development, you will need MongoDB installed.
|
||||
|
||||
- [MongoDB Community Server](https://www.mongodb.com/try/download/community)
|
||||
- [Install MongoDB Community Server on Windows](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/)
|
||||
- [Install MongoDB Community Server on macOS with Brew](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/)
|
||||
- [Install MongoDB Community Server on Linux](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/)
|
||||
- Note: If you are using Windows Subsystem for Linux (WSL) with Ubuntu, you can install MongoDB using the following commands
|
||||
|
||||
```bash
|
||||
sudo apt-get install mongodb
|
||||
service mongodb start # Start mongodb
|
||||
```
|
||||
|
||||
> **IMPORTANT**: Make sure MongoDB is started after you install the service.
|
||||
|
||||
## Starting the application
|
||||
|
||||
After you have downloaded the source code locally, you are all set to run it! You'll rename one settings file, and then start the server!
|
||||
|
||||
1. Inside Visual Studio Code, locate the file named *starter-settings.json*. Rename the file to *local.settings.json*.
|
||||
|
||||
*local.settings.json* contains environmental variables used by Azure Functions. For the starter project, it contains the connection string to the database, MongoDB running locally on your machine.
|
||||
|
||||
1. Inside Visual Studio Code, open a new terminal by selecting *Terminal* > *New Terminal*.
|
||||
1. Run the following commands to change to install the packages for both the api and client, and start the development server:
|
||||
|
||||
```bash
|
||||
cd api
|
||||
npm install
|
||||
cd ..
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
After the scripts have run, your dev server will be started
|
||||
|
||||
1. Open the website by navigating to [http://localhost:4280](http://localhost:4280)
|
||||
|
||||
![Screenshot of the starting page, showing a link to login, a picture of a dog with the heading Isn't this a cute Pekinese dog?](./media/starter-page.png)
|
||||
|
||||
> A note about authentication: The project uses the [Azure Static Web Apps CLI](https://github.com/Azure/static-web-apps-cli#azure-static-web-apps-cli-preview) to host the application. When you click "login", you will be presented with a page where you can provide your own username and ID. This allows you to simulate authentication without having to actually login to GitHub or another provider.
|
|
@ -0,0 +1,51 @@
|
|||
# Goal 4: Running the application locally
|
||||
|
||||
Congratulations on exporting your model! In this step, you will run the web application locally. You do not need to modify any of the application code to get this working.
|
||||
|
||||
## Install the application dependencies
|
||||
|
||||
The web application uses Node.js Yarn. To perform local development, you will need Node.js and Yarn installed.
|
||||
|
||||
- [Node.js](https://nodejs.org/)
|
||||
- [Install Node.js on Windows](https://docs.microsoft.com/windows/dev-environment/javascript/nodejs-on-windows)
|
||||
- [Install Node.js on Linux or MacOS](https://github.com/nvm-sh/nvm#installing-and-updating)
|
||||
- [Yarn](https://yarnpkg.com/getting-started/install#per-project-install)
|
||||
|
||||
|
||||
Once you have all of the packages installed and the application built:
|
||||
|
||||
1. Open a terminal and navigate to the [sample web application] folder
|
||||
1. Run the following command to install the project dependencies
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
## Starting the application
|
||||
|
||||
After you have installed all of the project dependencies, you are all set to run it!
|
||||
|
||||
1. Open a terminal and navigate to the [sample web application] folder
|
||||
1. Run the following commands to start the development server:
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
1. Open the website by navigating to [http://localhost:3000](http://localhost:3000).
|
||||
<figure>
|
||||
![Screenshot of the application landing page](./media/sample-web-app-ui.jpeg){style="width:100%"}
|
||||
<figcaption>That's a sharpie, not a bird! The model will always attempt to label an image with the best fitting label it knows - even if it doesn't make sense.</figcaption>
|
||||
</figure>
|
||||
|
||||
!!! Caution "Browser permissions"
|
||||
Your browser may ask you accept opening the insecure site "localhost:3000" or ask if the page can access your device cameras. Please refer to your browser documentation on how to do this.
|
||||
|
||||
2. It's ready to use! You can either upload an image from your device or use your device camera.
|
||||
|
||||
## Resources
|
||||
|
||||
Your team might find these resources helpful:
|
||||
|
||||
- [Lobe - Running the sample web app](../apps/web-bootstrap/README.md)
|
||||
|
||||
<!-- References -->
|
||||
[sample web application]: ../apps/web-bootstrap
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{%
|
||||
include-markdown "../README.md"
|
||||
%}
|
После Ширина: | Высота: | Размер: 115 KiB |
После Ширина: | Высота: | Размер: 425 KiB |
После Ширина: | Высота: | Размер: 352 KiB |
После Ширина: | Высота: | Размер: 402 KiB |
После Ширина: | Высота: | Размер: 399 KiB |
После Ширина: | Высота: | Размер: 330 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
После Ширина: | Высота: | Размер: 11 KiB |
После Ширина: | Высота: | Размер: 1.5 MiB |
После Ширина: | Высота: | Размер: 43 KiB |
После Ширина: | Высота: | Размер: 16 KiB |
После Ширина: | Высота: | Размер: 5.4 MiB |
После Ширина: | Высота: | Размер: 477 KiB |
|
@ -0,0 +1,10 @@
|
|||
img {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
figcaption {
|
||||
max-width: 80% !important;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"$lib/*": ["src/lib/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
site_name: Lobe Workshop
|
||||
docs_dir: goals
|
||||
extra_css:
|
||||
- stylesheets/extra.css
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- attr_list
|
||||
- pymdownx.highlight
|
||||
- pymdownx.superfences
|
||||
- pymdownx.snippets
|
||||
- smarty
|
||||
- toc:
|
||||
permalink: True
|
||||
plugins:
|
||||
- include-markdown
|
||||
theme:
|
||||
name: material
|
||||
features:
|
||||
- navigation.indexes
|
||||
- navigation.instant
|
||||
- navigation.expand
|
||||
- navigation.top
|
||||
- toc.integrate
|
||||
nav:
|
||||
- Overview: 'index.md'
|
||||
- 'Goals':
|
||||
- '0-obtain-source.md'
|
||||
- '1-train.md'
|
||||
- '2-test.md'
|
||||
- '3-export.md'
|
||||
- '4-run.md'
|
16
package.json
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"name": "the-loved-mutt",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "svelte-kit dev & swa start http://localhost:3000 --api ./api",
|
||||
"build": "svelte-kit build",
|
||||
"preview": "svelte-kit preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@azure/static-web-apps-cli": "^0.4.0",
|
||||
"@sveltejs/adapter-static": "^1.0.0-next.13",
|
||||
"@sveltejs/kit": "next",
|
||||
"svelte": "^3.34.0"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
14
src/app.html
|
@ -1,14 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<script src="https://kit.fontawesome.com/adcb63a956.js" crossorigin="anonymous"></script>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
%svelte.head%
|
||||
</head>
|
||||
<body>
|
||||
<div id="svelte">%svelte.body%</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1 +0,0 @@
|
|||
/// <reference types="@sveltejs/kit" />
|
|
@ -1,157 +0,0 @@
|
|||
<script>
|
||||
let imageUrl = '';
|
||||
let dogType = '';
|
||||
let message = '';
|
||||
let name = '';
|
||||
|
||||
import { onMount } from "svelte";
|
||||
|
||||
// Load first dog for display
|
||||
onMount(loadDog);
|
||||
|
||||
async function loadDog() {
|
||||
const result = await fetch(
|
||||
"https://dog.ceo/api/breeds/image/random/1/alt"
|
||||
);
|
||||
const imageInfo = (await result.json()).message[0];
|
||||
imageUrl = imageInfo.url;
|
||||
dogType = imageInfo.altText;
|
||||
if (dogType.endsWith("dog dog")) // removes dog dog
|
||||
dogType = imageInfo.altText.substring(
|
||||
0, imageInfo.altText.lastIndexOf(" ")
|
||||
);
|
||||
}
|
||||
|
||||
async function nameDog() {
|
||||
message = 'Saving dog...';
|
||||
const result = await fetch("/api/save-dog", {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify({ imageUrl, dogType, name }),
|
||||
});
|
||||
const json = await result.json();
|
||||
message = `Saved ${dogType} as ${name}`;
|
||||
name = '';
|
||||
await loadDog();
|
||||
}
|
||||
|
||||
async function nextDog() {
|
||||
message = "They're all good dogs";
|
||||
await loadDog();
|
||||
}
|
||||
|
||||
async function getUserInfo() {
|
||||
// retrieve the current user from Azure Static Web Apps
|
||||
const response = await fetch("/.auth/me");
|
||||
// get the JSON
|
||||
const payload = await response.json();
|
||||
// clientPrincipal is the property with the information
|
||||
if (payload.clientPrincipal) {
|
||||
// user is authenticated
|
||||
// userDetails contains the username
|
||||
return payload.clientPrincipal.userDetails;
|
||||
} else {
|
||||
// if clientPrincipal is null, the session is anonymous
|
||||
return null;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Dog shelter sample site</title>
|
||||
</svelte:head>
|
||||
<article class="index">
|
||||
<h2>Name dogs!</h2>
|
||||
<p>
|
||||
One way families can begin to get excited about adopting a dog is to
|
||||
begin thinking of possible names. After logging in, you can come up with
|
||||
names you think would work! Later on, you can <a href="named-dogs"
|
||||
>review the list</a
|
||||
> of names you came up with.
|
||||
</p>
|
||||
|
||||
{#if message}
|
||||
<div class="message">{message}</div>
|
||||
{/if}
|
||||
|
||||
{#await getUserInfo()}
|
||||
Getting user info...
|
||||
{:then username}
|
||||
{#if username}
|
||||
<div class="center">
|
||||
Welcome, {username}! See your
|
||||
<a href="named-dogs">named dogs</a>!
|
||||
</div>
|
||||
|
||||
<div class="header center">
|
||||
What would you call this cute {dogType}?
|
||||
</div>
|
||||
|
||||
<div class="vote-button-container center">
|
||||
<input class="name-input" type="text" bind:value={name} />
|
||||
<button class="vote-button" on:click={nameDog}>
|
||||
<span class="fas fa-vote-yea icon" />
|
||||
Save name!
|
||||
</button>
|
||||
<button class="vote-button" on:click={nextDog}>
|
||||
<span class="fas fa-forward icon" />
|
||||
Cute dog! But but best for someone else.
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="center">
|
||||
<a href="/.auth/login/github" class="center">Login to name the dogs!</a>
|
||||
</div>
|
||||
<div class="header center">
|
||||
Isn't this a cute {dogType}?
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
||||
<div>
|
||||
<img src={imageUrl} alt={dogType} />
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<style>
|
||||
img {
|
||||
width: 90%;
|
||||
margin: auto;
|
||||
}
|
||||
.message {
|
||||
border-radius: 5px;
|
||||
border-style: solid;
|
||||
border-color: black;
|
||||
border-width: 2px;
|
||||
margin-bottom: 10px;
|
||||
padding: 5px;
|
||||
background-color: lightgreen;
|
||||
text-align: center;
|
||||
}
|
||||
.icon {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.vote-button {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
display: flexbox;
|
||||
vertical-align: top;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.vote-button-container {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.header {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.name-input {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
</style>
|
|
@ -1,23 +0,0 @@
|
|||
<svelte:head>
|
||||
<title>Dog shelter sample site</title>
|
||||
</svelte:head>
|
||||
|
||||
<script>
|
||||
async function loadNamedDogs() {
|
||||
const response = await fetch('/api/named-dogs');
|
||||
return (await response.json()).namedDogs;
|
||||
}
|
||||
</script>
|
||||
|
||||
<h1>Your named dogs</h1>
|
||||
<div>Here's the list of all the dog names you've created</div>
|
||||
|
||||
{#await loadNamedDogs()}
|
||||
...loading named dogs
|
||||
{:then namedDogs}
|
||||
<ul>
|
||||
{#each namedDogs as dog}
|
||||
<li><a href={dog.imageUrl}>{dog.dogType} as {dog.name}</a></li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/await}
|
Двоичные данные
static/favicon.ico
До Ширина: | Высота: | Размер: 1.1 KiB |
|
@ -1,5 +0,0 @@
|
|||
body {
|
||||
font-family: "Lucida Sans", "Lucida Sans Regular", "Lucida Grande",
|
||||
"Lucida Sans Unicode", Geneva, Verdana, sans-serif;
|
||||
margin: 0 5%;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
{
|
||||
"routes": [
|
||||
{
|
||||
"route": "/logout",
|
||||
"redirect": "/.auth/logout"
|
||||
},
|
||||
{
|
||||
"route": "/named-dogs",
|
||||
"allowedRoles": [
|
||||
"authenticated"
|
||||
]
|
||||
},
|
||||
{
|
||||
"route": "/api/named-dogs",
|
||||
"allowedRoles": [
|
||||
"authenticated"
|
||||
]
|
||||
}
|
||||
],
|
||||
"responseOverrides": {
|
||||
"401": {
|
||||
"statusCode": 302,
|
||||
"redirect": "/login"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
// svelte.config.js
|
||||
import adapter from '@sveltejs/adapter-static';
|
||||
|
||||
export default {
|
||||
kit: {
|
||||
adapter: adapter({
|
||||
// default options are shown
|
||||
pages: 'build',
|
||||
assets: 'build',
|
||||
fallback: null
|
||||
}),
|
||||
files: {
|
||||
assets: 'static'
|
||||
},
|
||||
router: false,
|
||||
}
|
||||
};
|