From 6b5b0b08a96cbe9d367c2894775870366572528b Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Thu, 25 Apr 2019 22:46:13 -0700 Subject: [PATCH 01/17] WIP timers --- Frontend/package.json | 1 + Frontend/src/assets/bell.mp3 | Bin 0 -> 26271 bytes Frontend/src/assets/silence-5s.mp3 | Bin 0 -> 41239 bytes .../recipe-components/recipe/recipe.html | 7 +- .../recipe-components/recipe/recipe.scss | 4 + .../pages/recipe-components/recipe/recipe.ts | 98 +++++++++++++++++- 6 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 Frontend/src/assets/bell.mp3 create mode 100644 Frontend/src/assets/silence-5s.mp3 diff --git a/Frontend/package.json b/Frontend/package.json index 5ca1842f3..0e126bb3d 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -45,6 +45,7 @@ "dayjs": "^1.7.8", "firebase": "^5.7.1", "fraction.js": "^4.0.11", + "howler": "^2.1.2", "ionic-angular": "3.9.4", "ionicons": "4.5.5", "linkifyjs": "^2.1.7", diff --git a/Frontend/src/assets/bell.mp3 b/Frontend/src/assets/bell.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..dabcb266512c2a8da4fa98d37d87497b847f719e GIT binary patch literal 26271 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%0D#_SQCp~-HsLS4y0S##8ZN-D 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 Cl0aPm literal 0 HcmV?d00001 diff --git a/Frontend/src/assets/silence-5s.mp3 b/Frontend/src/assets/silence-5s.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..866abc7eef8a62ffda9e3171af1b2fb35e891504 GIT binary patch literal 41239 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
noIXPo^`xC) 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 WZQNaW_nfwK^7x-$`EUG*@;?B9P27(F literal 0 HcmV?d00001 diff --git a/Frontend/src/pages/recipe-components/recipe/recipe.html b/Frontend/src/pages/recipe-components/recipe/recipe.html index 24e18de63..a37ddea43 100644 --- a/Frontend/src/pages/recipe-components/recipe/recipe.html +++ b/Frontend/src/pages/recipe-components/recipe/recipe.html @@ -101,7 +101,12 @@

{{ recipe.title }}

Instructions

-

+

+ {{instruction.content}} + + {{instruction.count}}   + +

No Instructions

diff --git a/Frontend/src/pages/recipe-components/recipe/recipe.scss b/Frontend/src/pages/recipe-components/recipe/recipe.scss index 65e3bf708..7fd79dd70 100644 --- a/Frontend/src/pages/recipe-components/recipe/recipe.scss +++ b/Frontend/src/pages/recipe-components/recipe/recipe.scss @@ -77,6 +77,10 @@ page-recipe { .instruction { margin-bottom: 15px; color: #3f3f3f; + + .timer { + font-weight: bold; + } } } diff --git a/Frontend/src/pages/recipe-components/recipe/recipe.ts b/Frontend/src/pages/recipe-components/recipe/recipe.ts index efc236f89..826abdecd 100644 --- a/Frontend/src/pages/recipe-components/recipe/recipe.ts +++ b/Frontend/src/pages/recipe-components/recipe/recipe.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { IonicPage, NavController, NavParams, AlertController, ToastController, ModalController } from 'ionic-angular'; +import { Howl, Howler } from 'howler'; import { RecipeServiceProvider, Recipe } from '../../../providers/recipe-service/recipe-service'; import { LabelServiceProvider } from '../../../providers/label-service/label-service'; @@ -20,7 +21,7 @@ export class RecipePage { recipe: Recipe; recipeId: string; ingredients: any; - instructions: string[]; + instructions: { content: string, isHeader: boolean, count: number }[]; scale: number = 1; @@ -30,6 +31,11 @@ export class RecipePage { pendingLabel: string = ''; showAutocomplete: boolean = false; + timeRoller; + audioWakeLock: any; + timer: any; + timeRemaining: number; + constructor( public navCtrl: NavController, public alertCtrl: AlertController, @@ -87,6 +93,10 @@ export class RecipePage { if (this.recipe.instructions && this.recipe.instructions.length > 0) { // Starts with [, anything inbetween, ends with ] var headerRegexp = /^\[.*\]$/; + // Contains timer identifier + // + var timerRegexp = /([0-9]+ *([0-9]*(\/|\.)[0-9]*)? ?(to|-)? ?)* *(seconds|minutes|hours|second|minute|hour)/g; + let stepCount = 1; this.instructions = this.recipe.instructions.split(/\r?\n/).map(instruction => { let line = instruction.trim(); @@ -98,9 +108,26 @@ export class RecipePage { stepCount = 1; - return `${headerContent}` + return { + content: headerContent, + isHeader: true, + count: 0, + isDone: false + } } else { - return `${ stepCount++ }   ${instruction}` + var timerMatches = line.match(timerRegexp); + if (timerMatches && timerMatches.length > 0) { + for (var i = 0; i < timerMatches.length; i++) { + line = line.replace(timerMatches[i], `${timerMatches[i]}`); + } + } + + return { + content: line, + isHeader: false, + count: stepCount++, + isDone: false + } } }); } @@ -184,6 +211,71 @@ export class RecipePage { ionViewDidLoad() {} + setTimer(time, unit) { + let multiplier = 1; + switch(unit) { + case 'second': + case 'seconds': + multiplier = 1000; + break; + case 'minute': + case 'minutes': + multiplier = 1000 * 60; + break; + case 'hour': + case 'hours': + multiplier = 1000 * 60 * 60; + break; + } + + this.audioWakeLock = new Howl({ + src: ['assets/silence-5s.mp3'], + loop: true, + onplayerror: () => { + this.audioWakeLock.once('unlock', () => { + this.audioWakeLock.play(); + }); + } + }); + + this.audioWakeLock.play(); + + var start = Date.now(); + this.timer = setInterval(() => { + let timeElapsed = Date.now() - start; + let hours = Math.floor(timeElapsed / 3600); + let minutes = Math.floor((timeElapsed - (hours * 3600)) / 60); + let seconds = timeElapsed - (hours * 3600) - (minutes * 60); + + let hour = hours, minute = minutes, second = seconds; + if (hours < 10) hour = `0${hours}`; + if (minutes < 10) minute = `0${minutes}`; + if (seconds < 10) second = `0${seconds}`; + this.timeRoller = `${hour}:${minute}:${second}`; + + if (timeElapsed > time * multiplier) { + clearInterval(this.timer); + this.timer = null; + + this.audioWakeLock.stop(); + + let alertSound = new Howl({ + src: ['assets/bell.mp3'] + }); + + alertSound.play(); + } + }, 1000); + } + + instructionClicked(event, instruction) { + if (event.target.className.indexOf('timer') > -1) { + alert("timer set for " + event.target.innerText); + let tokens = event.target.innerText.split(" "); + this.setTimer(parseInt(tokens[0]), tokens[1]); + } + } + changeScale() { this.recipeService.scaleIngredientsPrompt(this.scale, scale => { this.scale = scale; From 2d322a9a91d662dfe8605d8e641845fcf1caba11 Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Fri, 26 Apr 2019 17:08:24 -0700 Subject: [PATCH 02/17] Add space at bottom of welcome screen --- Frontend/src/pages/info-components/welcome/welcome.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Frontend/src/pages/info-components/welcome/welcome.html b/Frontend/src/pages/info-components/welcome/welcome.html index d644c0429..fdbc1d51c 100644 --- a/Frontend/src/pages/info-components/welcome/welcome.html +++ b/Frontend/src/pages/info-components/welcome/welcome.html @@ -79,6 +79,8 @@

Welcome to RecipeSage!

Log In + +



From 88bf3740d0049a2a114a414bf0f0c3d5e06dec3a Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Fri, 26 Apr 2019 22:35:02 -0700 Subject: [PATCH 03/17] Added ability to check off instructions --- Frontend/src/pages/recipe-components/recipe/recipe.html | 7 +++++-- Frontend/src/pages/recipe-components/recipe/recipe.scss | 4 ++++ Frontend/src/pages/recipe-components/recipe/recipe.ts | 3 ++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Frontend/src/pages/recipe-components/recipe/recipe.html b/Frontend/src/pages/recipe-components/recipe/recipe.html index a37ddea43..5491f435d 100644 --- a/Frontend/src/pages/recipe-components/recipe/recipe.html +++ b/Frontend/src/pages/recipe-components/recipe/recipe.html @@ -103,8 +103,11 @@

{{ recipe.title }}

{{instruction.content}} - - {{instruction.count}}   + + {{instruction.count}}  

No Instructions

diff --git a/Frontend/src/pages/recipe-components/recipe/recipe.scss b/Frontend/src/pages/recipe-components/recipe/recipe.scss index 7fd79dd70..1a0eb0cb8 100644 --- a/Frontend/src/pages/recipe-components/recipe/recipe.scss +++ b/Frontend/src/pages/recipe-components/recipe/recipe.scss @@ -78,6 +78,10 @@ page-recipe { margin-bottom: 15px; color: #3f3f3f; + span.complete .instructionContent { + text-decoration: line-through; + } + .timer { font-weight: bold; } diff --git a/Frontend/src/pages/recipe-components/recipe/recipe.ts b/Frontend/src/pages/recipe-components/recipe/recipe.ts index 826abdecd..a09605905 100644 --- a/Frontend/src/pages/recipe-components/recipe/recipe.ts +++ b/Frontend/src/pages/recipe-components/recipe/recipe.ts @@ -21,7 +21,7 @@ export class RecipePage { recipe: Recipe; recipeId: string; ingredients: any; - instructions: { content: string, isHeader: boolean, count: number }[]; + instructions: { content: string, isHeader: boolean, count: number, complete: boolean }[]; scale: number = 1; @@ -274,6 +274,7 @@ export class RecipePage { let tokens = event.target.innerText.split(" "); this.setTimer(parseInt(tokens[0]), tokens[1]); } + instruction.complete = !instruction.complete; } changeScale() { From 92aec0aad4c86fbe46be83f99d250ba3447ce0d3 Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Sat, 27 Apr 2019 21:40:22 -0700 Subject: [PATCH 04/17] Remove graphicsmagick from circle --- .circleci/config.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a523623ce..7d99d034c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,9 +6,6 @@ jobs: # a collection of steps - image: circleci/node:8.15.0 # ...with this image as the primary container; this is where all `steps` will run steps: # a collection of executable commands - checkout # special step to check out source code to working directory - - run: - name: install-graphicsmagick - command: 'sudo apt install graphicsmagick -y' - run: name: update-npm command: 'sudo npm install -g npm@latest' From 2f8fc6aab549cb693900eeddaf6f4050f1feb1ed Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Sun, 28 Apr 2019 10:37:08 -0700 Subject: [PATCH 05/17] Added marking ingredients complete --- .../select-ingredients.html | 2 +- .../select-ingredients/select-ingredients.ts | 14 ++++----- .../recipe-components/recipe/recipe.html | 6 +++- .../recipe-components/recipe/recipe.scss | 14 +++++++++ .../pages/recipe-components/recipe/recipe.ts | 15 ++++++--- .../recipe-service/recipe-service.ts | 31 +++++++++++++++---- 6 files changed, 62 insertions(+), 20 deletions(-) diff --git a/Frontend/src/components/select-ingredients/select-ingredients.html b/Frontend/src/components/select-ingredients/select-ingredients.html index 864583e97..60a93d7a4 100644 --- a/Frontend/src/components/select-ingredients/select-ingredients.html +++ b/Frontend/src/components/select-ingredients/select-ingredients.html @@ -9,7 +9,7 @@
- {{ ingredient }} + {{ ingredient.content }}

diff --git a/Frontend/src/components/select-ingredients/select-ingredients.ts b/Frontend/src/components/select-ingredients/select-ingredients.ts index c1e49e64c..fb1aea8e7 100644 --- a/Frontend/src/components/select-ingredients/select-ingredients.ts +++ b/Frontend/src/components/select-ingredients/select-ingredients.ts @@ -1,5 +1,5 @@ import { Component, Input, Output, EventEmitter } from '@angular/core'; -import { RecipeServiceProvider } from '../../providers/recipe-service/recipe-service'; +import { RecipeServiceProvider, Ingredient } from '../../providers/recipe-service/recipe-service'; @Component({ selector: 'select-ingredients', @@ -8,17 +8,17 @@ import { RecipeServiceProvider } from '../../providers/recipe-service/recipe-ser export class SelectIngredientsComponent { allSelected: boolean = true; - ingredientBinders: any = {}; - scaledIngredients: any = []; - scale: any = 1; + ingredientBinders: { [index: number]: boolean } = {}; + scaledIngredients: Ingredient[] = []; + scale: number = 1; - _ingredients: any; + _ingredients: string; @Input() get ingredients() { return this._ingredients; } - set ingredients(val) { + set ingredients(val: string) { this._ingredients = val; this.selectedIngredients = []; @@ -27,7 +27,7 @@ export class SelectIngredientsComponent { this.applyScale(true); } - selectedIngredients: any; + selectedIngredients: Ingredient[]; @Output() selectedIngredientsChange = new EventEmitter(); diff --git a/Frontend/src/pages/recipe-components/recipe/recipe.html b/Frontend/src/pages/recipe-components/recipe/recipe.html index 5491f435d..830146e68 100644 --- a/Frontend/src/pages/recipe-components/recipe/recipe.html +++ b/Frontend/src/pages/recipe-components/recipe/recipe.html @@ -88,7 +88,11 @@

{{ recipe.title }}

Ingredients

-

+

No Ingredients

{{ scale }}x scale (click to change) diff --git a/Frontend/src/pages/recipe-components/recipe/recipe.scss b/Frontend/src/pages/recipe-components/recipe/recipe.scss index 1a0eb0cb8..1570a80f5 100644 --- a/Frontend/src/pages/recipe-components/recipe/recipe.scss +++ b/Frontend/src/pages/recipe-components/recipe/recipe.scss @@ -62,6 +62,15 @@ page-recipe { margin-top: 0; } } + + &.complete { + text-decoration: line-through; + opacity: 0.7; + } + + &.ingredientContent { + cursor: pointer; + } } } @@ -78,8 +87,13 @@ page-recipe { margin-bottom: 15px; color: #3f3f3f; + .instructionContent { + cursor: pointer; + } + span.complete .instructionContent { text-decoration: line-through; + opacity: 0.7; } .timer { diff --git a/Frontend/src/pages/recipe-components/recipe/recipe.ts b/Frontend/src/pages/recipe-components/recipe/recipe.ts index a09605905..c68f6eb1b 100644 --- a/Frontend/src/pages/recipe-components/recipe/recipe.ts +++ b/Frontend/src/pages/recipe-components/recipe/recipe.ts @@ -2,7 +2,7 @@ import { Component } from '@angular/core'; import { IonicPage, NavController, NavParams, AlertController, ToastController, ModalController } from 'ionic-angular'; import { Howl, Howler } from 'howler'; -import { RecipeServiceProvider, Recipe } from '../../../providers/recipe-service/recipe-service'; +import { RecipeServiceProvider, Recipe, Instruction, Ingredient } from '../../../providers/recipe-service/recipe-service'; import { LabelServiceProvider } from '../../../providers/label-service/label-service'; import { LoadingServiceProvider } from '../../../providers/loading-service/loading-service'; import { UtilServiceProvider } from '../../../providers/util-service/util-service'; @@ -20,8 +20,8 @@ export class RecipePage { recipe: Recipe; recipeId: string; - ingredients: any; - instructions: { content: string, isHeader: boolean, count: number, complete: boolean }[]; + ingredients: Ingredient[]; + instructions: Instruction[]; scale: number = 1; @@ -112,7 +112,7 @@ export class RecipePage { content: headerContent, isHeader: true, count: 0, - isDone: false + complete: false } } else { var timerMatches = line.match(timerRegexp); @@ -126,7 +126,7 @@ export class RecipePage { content: line, isHeader: false, count: stepCount++, - isDone: false + complete: false } } }); @@ -277,6 +277,11 @@ export class RecipePage { instruction.complete = !instruction.complete; } + ingredientClicked(event, ingredient) { + if (ingredient.isHeader) return; + ingredient.complete = !ingredient.complete; + } + changeScale() { this.recipeService.scaleIngredientsPrompt(this.scale, scale => { this.scale = scale; diff --git a/Frontend/src/providers/recipe-service/recipe-service.ts b/Frontend/src/providers/recipe-service/recipe-service.ts index 0c845a7be..6df19b583 100644 --- a/Frontend/src/providers/recipe-service/recipe-service.ts +++ b/Frontend/src/providers/recipe-service/recipe-service.ts @@ -32,6 +32,19 @@ export interface Recipe { score: number; } +export interface Ingredient { + content: string; + isHeader: boolean; + complete: boolean; +} + +export interface Instruction { + content: string; + isHeader: boolean; + complete: boolean; + count: number; +} + @Injectable() export class RecipeServiceProvider { @@ -324,10 +337,14 @@ export class RecipeServiceProvider { } } - scaleIngredients(ingredients, scale, boldify?) { + scaleIngredients(ingredients: string, scale: number, boldify?: boolean): Ingredient[] { if (!ingredients) return []; - var lines = ingredients.match(/[^\r\n]+/g); + let lines: Ingredient[] = ingredients.match(/[^\r\n]+/g).map(match => ({ + content: match, + complete: false, + isHeader: false + })); // var measurementRegexp = /\d+(.\d+(.\d+)?)?/; var measurementRegexp = /((\d+ )?\d+([\/\.]\d+)?((-)|( to )|( - ))(\d+ )?\d+([\/\.]\d+)?)|((\d+ )?\d+[\/\.]\d+)|\d+/; @@ -335,7 +352,7 @@ export class RecipeServiceProvider { var headerRegexp = /^\[.*\]$/; for (var i = 0; i < lines.length; i++) { - var line = lines[i].trim(); // Trim only spaces (no newlines) + var line = lines[i].content.trim(); // Trim only spaces (no newlines) var measurementMatches = line.match(measurementRegexp); var headerMatches = line.match(headerRegexp); @@ -345,7 +362,8 @@ export class RecipeServiceProvider { var headerContent = header.substring(1, header.length - 1); // Chop off brackets if (boldify) headerContent = `${headerContent}`; - lines[i] = headerContent; + lines[i].content = headerContent; + lines[i].isHeader = true; } else if (measurementMatches && measurementMatches.length > 0) { var measurement = measurementMatches[0]; @@ -361,11 +379,12 @@ export class RecipeServiceProvider { scaledMeasurement = scaledMeasurement.toFraction(true); } - if (boldify) measurementParts[j] = '' + scaledMeasurement + ''; + if (boldify) measurementParts[j] = '' + scaledMeasurement + ''; else measurementParts[j] = scaledMeasurement; } - lines[i] = lines[i].replace(measurementRegexp, measurementParts.join(' to ')); + lines[i].content = lines[i].content.replace(measurementRegexp, measurementParts.join(' to ')); + lines[i].isHeader = false; } catch (e) { console.log("failed to parse", e) } From 62b9bae04334b3489e50be3331de8de497e0e2c3 Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Sun, 28 Apr 2019 10:39:58 -0700 Subject: [PATCH 06/17] Add frontend lint and build to circleci tests --- .circleci/config.yml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7d99d034c..6f14fff89 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,6 +12,7 @@ jobs: # a collection of steps - run: name: install-deps command: 'sudo npm install -g sequelize' + # -------------- Setup Backend ----------------- - restore_cache: # special step to restore the dependency cache # Read about caching dependencies: https://circleci.com/docs/2.0/caching/ key: backend-dependency-cache-{{ checksum "Backend/package.json" }} @@ -28,9 +29,27 @@ jobs: # a collection of steps - run: name: extract-config command: cd Backend/config && unzip -j -P $credentialskey config.json.zip - - run: # run tests - name: test + # ------------- Setup Frontend ---------------- + - restore_cache: # special step to restore the dependency cache + # Read about caching dependencies: https://circleci.com/docs/2.0/caching/ + key: frontend-dependency-cache-{{ checksum "Frontend/package.json" }} + - run: + name: install-npm-frontend + command: 'cd Frontend && npm install' + - save_cache: # special step to save the dependency cache + key: frontend-dependency-cache-{{ checksum "Frontend/package.json" }} + paths: + - ./Frontend/node_modules + # -------------- Tests ----------------- + - run: # run backend tests + name: test-backend command: cd Backend && npm run test:ci + - run: # lint frontend + name: lint-frontend + command: cd Frontend && npm run lint + - run: # build frontend + name: build-frontend + command: cd Frontend && npm run build # - run: # run coverage report # name: code-coverage # command: './node_modules/.bin/nyc report --reporter=text-lcov' From 442dc9c9866798721e2e78065d8b1d28201762d3 Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Sun, 28 Apr 2019 11:04:04 -0700 Subject: [PATCH 07/17] Remove timer code --- Frontend/package.json | 1 - .../recipe-components/recipe/recipe.scss | 4 - .../pages/recipe-components/recipe/recipe.ts | 83 +------------------ 3 files changed, 3 insertions(+), 85 deletions(-) diff --git a/Frontend/package.json b/Frontend/package.json index 0e126bb3d..5ca1842f3 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -45,7 +45,6 @@ "dayjs": "^1.7.8", "firebase": "^5.7.1", "fraction.js": "^4.0.11", - "howler": "^2.1.2", "ionic-angular": "3.9.4", "ionicons": "4.5.5", "linkifyjs": "^2.1.7", diff --git a/Frontend/src/pages/recipe-components/recipe/recipe.scss b/Frontend/src/pages/recipe-components/recipe/recipe.scss index 1570a80f5..1acb47397 100644 --- a/Frontend/src/pages/recipe-components/recipe/recipe.scss +++ b/Frontend/src/pages/recipe-components/recipe/recipe.scss @@ -95,10 +95,6 @@ page-recipe { text-decoration: line-through; opacity: 0.7; } - - .timer { - font-weight: bold; - } } } diff --git a/Frontend/src/pages/recipe-components/recipe/recipe.ts b/Frontend/src/pages/recipe-components/recipe/recipe.ts index c68f6eb1b..4e8d6c49a 100644 --- a/Frontend/src/pages/recipe-components/recipe/recipe.ts +++ b/Frontend/src/pages/recipe-components/recipe/recipe.ts @@ -1,6 +1,5 @@ import { Component } from '@angular/core'; import { IonicPage, NavController, NavParams, AlertController, ToastController, ModalController } from 'ionic-angular'; -import { Howl, Howler } from 'howler'; import { RecipeServiceProvider, Recipe, Instruction, Ingredient } from '../../../providers/recipe-service/recipe-service'; import { LabelServiceProvider } from '../../../providers/label-service/label-service'; @@ -31,11 +30,6 @@ export class RecipePage { pendingLabel: string = ''; showAutocomplete: boolean = false; - timeRoller; - audioWakeLock: any; - timer: any; - timeRemaining: number; - constructor( public navCtrl: NavController, public alertCtrl: AlertController, @@ -93,9 +87,6 @@ export class RecipePage { if (this.recipe.instructions && this.recipe.instructions.length > 0) { // Starts with [, anything inbetween, ends with ] var headerRegexp = /^\[.*\]$/; - // Contains timer identifier - // - var timerRegexp = /([0-9]+ *([0-9]*(\/|\.)[0-9]*)? ?(to|-)? ?)* *(seconds|minutes|hours|second|minute|hour)/g; let stepCount = 1; this.instructions = this.recipe.instructions.split(/\r?\n/).map(instruction => { @@ -115,13 +106,6 @@ export class RecipePage { complete: false } } else { - var timerMatches = line.match(timerRegexp); - if (timerMatches && timerMatches.length > 0) { - for (var i = 0; i < timerMatches.length; i++) { - line = line.replace(timerMatches[i], `${timerMatches[i]}`); - } - } - return { content: line, isHeader: false, @@ -211,73 +195,12 @@ export class RecipePage { ionViewDidLoad() {} - setTimer(time, unit) { - let multiplier = 1; - switch(unit) { - case 'second': - case 'seconds': - multiplier = 1000; - break; - case 'minute': - case 'minutes': - multiplier = 1000 * 60; - break; - case 'hour': - case 'hours': - multiplier = 1000 * 60 * 60; - break; - } - - this.audioWakeLock = new Howl({ - src: ['assets/silence-5s.mp3'], - loop: true, - onplayerror: () => { - this.audioWakeLock.once('unlock', () => { - this.audioWakeLock.play(); - }); - } - }); - - this.audioWakeLock.play(); - - var start = Date.now(); - this.timer = setInterval(() => { - let timeElapsed = Date.now() - start; - let hours = Math.floor(timeElapsed / 3600); - let minutes = Math.floor((timeElapsed - (hours * 3600)) / 60); - let seconds = timeElapsed - (hours * 3600) - (minutes * 60); - - let hour = hours, minute = minutes, second = seconds; - if (hours < 10) hour = `0${hours}`; - if (minutes < 10) minute = `0${minutes}`; - if (seconds < 10) second = `0${seconds}`; - this.timeRoller = `${hour}:${minute}:${second}`; - - if (timeElapsed > time * multiplier) { - clearInterval(this.timer); - this.timer = null; - - this.audioWakeLock.stop(); - - let alertSound = new Howl({ - src: ['assets/bell.mp3'] - }); - - alertSound.play(); - } - }, 1000); - } - - instructionClicked(event, instruction) { - if (event.target.className.indexOf('timer') > -1) { - alert("timer set for " + event.target.innerText); - let tokens = event.target.innerText.split(" "); - this.setTimer(parseInt(tokens[0]), tokens[1]); - } + instructionClicked(event, instruction: Instruction) { + if (instruction.isHeader) return; instruction.complete = !instruction.complete; } - ingredientClicked(event, ingredient) { + ingredientClicked(event, ingredient: Instruction) { if (ingredient.isHeader) return; ingredient.complete = !ingredient.complete; } From 4af387d353f1d811a20cb7f8b3d6d2c853ff91ef Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Sun, 28 Apr 2019 11:22:44 -0700 Subject: [PATCH 08/17] Update reqs --- reqs.sh | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/reqs.sh b/reqs.sh index 0073a071d..4784ffa7e 100755 --- a/reqs.sh +++ b/reqs.sh @@ -1,13 +1,9 @@ #!/bin/bash -# NightmareJS Scraper Engine Reqs +# Puppeteer Reqs sudo apt install libnss3 libasound2 libxss1 -y -sudo npm install -g electron --unsafe-perm=true --allow-root - -# multer-imager reqs -sudo apt install graphicsmagick -y - +# PushPin sudo apt-get install apt-transport-https \ software-properties-common echo deb https://dl.bintray.com/fanout/debian fanout-$(lsb_release -csu 2> /dev/null || lsb_release -cs) main \ From 8dc97f41c3401204815c3df64583111cdf58a3bc Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Sun, 28 Apr 2019 11:23:03 -0700 Subject: [PATCH 09/17] Add precommit lint hooks --- .githooks/pre-commit | 5 +++++ dev-setup.sh | 3 +++ 2 files changed, 8 insertions(+) create mode 100755 .githooks/pre-commit create mode 100755 dev-setup.sh diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 000000000..640fc5fe5 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,5 @@ +#!/bin/bash + +cd Frontend; +npm run lint; +# npm run build; diff --git a/dev-setup.sh b/dev-setup.sh new file mode 100755 index 000000000..8caf140b6 --- /dev/null +++ b/dev-setup.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +git config --local core.hooksPath .githooks/ From fef439e5496471d58dc6ec0a4db098b1252b7551 Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Sun, 28 Apr 2019 11:53:58 -0700 Subject: [PATCH 10/17] Remove timer sound assets --- Frontend/src/assets/bell.mp3 | Bin 26271 -> 0 bytes Frontend/src/assets/silence-5s.mp3 | Bin 41239 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Frontend/src/assets/bell.mp3 delete mode 100644 Frontend/src/assets/silence-5s.mp3 diff --git a/Frontend/src/assets/bell.mp3 b/Frontend/src/assets/bell.mp3 deleted file mode 100644 index dabcb266512c2a8da4fa98d37d87497b847f719e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26271 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%0D#_SQCp~-HsLS4y0S##8ZN-D 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 Cl0aPm diff --git a/Frontend/src/assets/silence-5s.mp3 b/Frontend/src/assets/silence-5s.mp3 deleted file mode 100644 index 866abc7eef8a62ffda9e3171af1b2fb35e891504..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41239 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
noIXPo^`xC) zgc`tIi5>oA*u*7v=@R??{r8zk4<&`!CRT}y&D15<^HX=#avcrH)fmd4<`oFpfxFbh z%=6_`;fw09of?pp3K3F@Q;fHWZI+C=@xDIG+K|_{rM_?D(MG?9PdZn{#Kn^f$#5vR z#0tiD9aYcC=&KBl`}4;vs?+i3mvtb?rt>iJqm~wCVF@kYu(0?Pe_`14?uMi5MQrOXsA7BDXJnE|>gu|UCNz!h z+qcKqEDuU%+l>F$y{~T8<+HBU{g@sw(Brj z{RzcwtJUsg4C2p5!gc^fKew+!5(PyeTi5DOC?1Owf`dQQKBU_jfA+Ba3+@yrh5mEP z|K4bIYX1K?M+sZEIRWfedjbC6hC-FU6yEn)797uG0s^=4^767=ma;Upv|hb<@#Qfk zMFDmT#UB9|7Z-G{XQF&h*PiQm6UV9=%3dAMe15N#ISO02%#Sm$V&%bu2fn<9itE;` zyNd>nEQ{XFdn?Z^%mR`K9Lv9#qyj;!;P*(=_ACXQ+UO~ups?dPA<=fK>&-4k##n4_23==2n!S-IU6A>u21)Q}_ z*u0DVb^vm{Fb$JH^;^p#JS2hDbx{`6EbW{If7%ddA(-}2Q21P?p%qHb$<3wPwM+Z; zbVcylS?(kaq$tDfNZv5TBGAYqk5fm#x|}!vD)+Ct-kA^FeX~5;+uLiEL8(O!@9>(m z2|RlANXfT7$6gM)vC(TUzwe6|;m@9J>;3jpJkz501rp1<4L`47<_OSLgb1sU zb8IXTwWT*-yxjT^h|eKY;-rf1&yvEyh|6BJpq?1xMS3WRA3Sno7^qI0FT&2wF2!$iIg8<19H6=(L9JES9ybHl?x_ND)e?vVlZ}<91OO9&L`n@haLxx6shg z#l=Nm0Qth~*f3T*{rf{T5pHO5j)DafaKQ~cHx@*h{{F)V{2Pw)b*s?j)|9&u zaJpNAOGQ@}wFF&QmwyX7c2H5=yGQlsByxHkded}I378BH6Q-u7hmIVvq!6W|EX9c;9^)3C zCHM)7aSLvb9^J)GA&H{GzNn}u8spA#n2v22bAUI|fUP9&BRqJ1!%J{qdE^cpY`deV zh=@eET?Yfu?q{qnxKA}*BhLC}S^C=uwtDpNquw=-6G>C!rL4zKo_vOb3%74k z@7yAESVTk}Zly%3)D)U&rpH`YS_i?gcI?<;)%TqNwGdl_n(#-Ad`7PgQoHr`cL{)p zw@<)T3pDFF`Wz%ByO$6d2xHzmU)BaXGX#mcs*5g7y=_5p_g0dG@2A+E_*Rz z%r3FRL5Hr=2kH=V2wI-%ykKLK@Hqv1nxicPGlVBaBc9K+_g5A6gn??fdw9H@3Y+^1 z&tnQiRwe$>&ucqh)f;SS5p0yhE3W9niDW!^n_|OSvaoiH0E#M;xyLknRntD8N5C~! zBR@~^!>v1$yj2$p%!eB}vs-Tis+eBO+~-jG^uyU8*LF;u*XYN|hJvdvFo}T)doK*0 zmwFD<`Yzf+Z8ZF`AoBfWkJ@~^?+bbZy3V)6ymv2HnereS4F?0p@HhYh8_sur0 z-(9(u3G4t}uIWa8iKuU?4KJZ2rOea5mZIPa7{K1{o}N*^(vp%aD|wV(dft24L2nYV=R0PwD$F$4BRP@ z4OWT#r6(y0z79O3`m;|$LSm>jJrSpCc|Cq^8lC3;!;@*=^O%v}Dl~WCLT{W_-jyAaX*Rq{Cql7m}QtmU_iL;rv z;e&R)MI7p!TC+A);17@8qJT8~2KZV+2F(0MKf=C#y^?%)Et8B7X7u$^u(Ehq!bh!A zT1;rA=IS}bgo_1~cG2(N9rR!s-}LVtI+C>9044qAo%yUTe?J>BO38r%@K+Sdz@h+E`n-C*Yn{wK|vp|odTB+ci9PQm-&7hnu5it{dEGq zNo-NkrMgbSvomNZH$1yvd!yJHnmy80Wv}y^lL6vX-e4>CQ&}Q{svMXZTIg}bDD+*c zAyJn->m6%J9bQrUKeMA9hPv~ar4VfDz-1}W1sWC_0Vs`;nqh~LAE)Z_$wZ|+W~1G_ zxfZ;ITU+2a@_zuil!8Ll?H<+Fb2??d=JUcRBe;<-6i`amRkD)au%{-1_c;sAHlZ5u z5vi5Pr)72ff#x?8RFjlv(4V6Zu~Zg(HeIFZXU)Bb#5vFKJ`qUqRRH_SdR`c9Ni_g3 zW{3Fv{CvXErt^V^?8q|88D?*Z2$$~%sds1=f=f}HhWLk*9KC%90QU2<96qGi*kdP``FJ-S>dE(^kjH=3ef<5r`AmKb< zjq>AAhtI>NM=Xt(2qQs(L&!l*OH0cdn9y4l+Br%oj*pTg|MBBVx?}QJpx5dVB)gcI z<6)rcL-n;}+a|tv@uF=xGg~^Bh;Gn`N2_T7(zz7!jbla&6 z$jydCjZOn4FyARy2zMubeN#@+f3756m0*F_B;F8c24ws7*u>e%N$w?&oL6R;`HgTu(l%lo2S z`W`#T-su?)6685X`46{b@7%cq3ke5)>LbLr>U?`q{5H0MgqKq3d!Nm)28p0f(h|>3 zmBOm0Kmm{*dA`tpS|;I^$4CY8l?9ziUPDF2?9z$EQb%W|{q2RAu*1}K9OIS`J4YFp zcoe;SgPmwIs|ERrfRZQ2V+67P?JKgaQmmR3%)|mgy)>QzXtf{doTvvVjjE%-HWn#3n}nv?(bvcfNrDpOtlZU z{RX4BiE0pW5J3thafqq`)J`>x7BBWm#igr&QZ8e7c=$(e3+R%tNY}=#TbpoxE*5@I zi_r`@*Kzd)NtGa^Lx{fsZar^w8>|d~hthIJ;pvQmR?(ky%KNG_PaQiZNeD{32FJpD zELLggM^Xe1CC~&;K6(1IrmrH9P^zfP-IW`uspVmvHoTMAS!d&*if}t5@#>hu4g8D_ zGz8m1za`}^^qO?j#Fky`VMKCZ#Z%9GQh}06hiF0*}L#{t|kP zYz%^7aE0s-o)ey*gEtp`{^9zgJUsQ`LI^#G7)2PA&|R-!qS@Qqzq#-n&>lz3He_X- zr%QbO`nA0O8#Zy;y1HDRm2<6`F`H;N!r`D?tn<;n4oYBqy|5EivsR3bOCJMvPBNjbFDuD zHXJdrh`s=yPsM!6`}>0cM^$nky6Tt0XaPI(fE1b=ZBh7QBUzot2WN zg`J2)o%`hOjYq?{l&kP(I%C0(X-)}#_~1c(yb3>d&NbYj1{5?%Jr6HI3S)o=WzS0~ z068Q%8Ki{g_=Wqfqv4Fa2JBFLFWy0z-9>|mLG!2gNPO>rj23q`0v}F(LhE=e8b3|m z%wlSBN>~Wf6OEJmB)3*X9otIfEv{1$pn&(3#d`&*+rZs?Ov@Y#cg%30`Ymn#7}R*= zvAB+oj>zkunIZak>?gFru&a$6V9UY<2!~x8=q1l40cA(z1#*RU*15^a$vSl6hLOy1 z;J^h~jZy8r>UmB|u+VL0i+h0cRujJ@Nxv5;cIiQFP5_?bs%|$jnJ>bg&G}^AK86K4yP^ zc|?YM?EsR598Uc!pzf3H94F*rGKCqzA%R`nxBpx$PEAZCTN3Fw>);b=YU+A?<3fB$ z6nO$j+W9~}KZl3o0LX|#^yX%XovZC1i2_#z${V!8oM(?~sy=&Y@{@ChsuAS{Kouo?KVfU3gx z0LHd#TN#MN8my7<{Etmwe5iq`D#{%IPaz;eKnH*^0N2~1ISWTkmDX+Ebsc4phWA%@ z2VQ*4D(t``CDp{j>pXAeaqiX&^o)IwjktV9I3Ld>7M51-V&|zbK33})mzifJl#}Z7UFv&H)?NQgZ7KFxo8fJoIp8_S=2=pxFL&j z!C>2KW$tpUee;r+q!dsU>m-zHZEu$|jRs(-V9Dtpm}Bn_mj&bGK^!!EgjV9YU?xPq51}7QnG9OdnP2bbg zqY$sm8z<8pB#Wg(2W7#~x@c}nxAgRM#I*i)X^2LWYiSdsP=%uBxOMwfC<+v@NM5wo zA$`5Q@5g&edlzT?378~Reo?mBvM z5gsb9_SbEJ6@nd3lTuzR!Yv=97J5qWfa}%O*XY4;AoU0jmu=|DiieasD-;UCcDE*6 zO!s~TlmT(|lJ6u4%fd}dHT!uXZsB;Ybcj$4n4ux2i;J0Zm_!x;?gQxgt1+gtcD>ag=9U$%=Jhw~kSgxf-0dm1;x9u~~yf?5dRGY$~TNU|Ed5%q;;@Vr~F zk0{!?r$O9aw(VfbsKrR?^PC;s<{QivpYAwR4;o|Kk)s5NP{jKO56L(C;rLfZ{7a>q z6B84)I1;M0-DCuYR*cz2hi!~-xH~oH;ROaI+}Db;Y`S-kg3LG4c%+#YYSgjHsj*0} zn$}ZEU@iDAF|k<1_<#y&vE1BTP^Ky#<3g|s<@n-h*nnr5Os;JGa?LO_9m&F`Q*TtT z59*WFXJn=t?Fk5@6CN!ruo@t>1&(Z>Sj%+p$VM6(1w?YiG8$z2Nuo%dIe$5s_k%i~ z-H_{OiyVI)(vYMD0Mkfy^-C~RIe0zBY2;i$W`+8A@Xzm$0(l|kuEWsch%{|UIVu$# zSBxS(x|`_hJm`-1{n=TOks$!D=$nBOipNvu#MHEG5w1m|DyfUQF{{&@R51fr=vR^d1*F-->c_(}`TfPr^?E6Zam z^`P(`gImR?&E@Z}S!Wco1#@MZ?CKA6QHlS?jXpLW8iib)a(dLTzZogx%nuKW^Y)IG zXV(yOvm3tbuDyE=XEcNUuAoRR#-E%KzgdT|n`lvu5LN@(JASe>V2v(j55 zWF+y0ei7gaf5fKuZ4G>X4at3QzrqRCjN8c)Dv+ZwJI{;53+*tFON1sqBULDJb?f}- z$41r*u<49wzl}&>>B8|^anNalw6{h#(afcc;tHL=j7zzC|M?ttrMT^Z`Evm8JQbc&UnQ^ zVU#9lnFO9CjAjzF)`?M=bY@4S>DIjkCm6=9B%AbHrw(*Juk3j?jw`#Syy`CEo0LxO zRqB!T@iDnA19=`V4PWnWmPl)ljNbbV&OZd|PW^eA=Cb5~sk#W_*$AG{POqGgll01P zgRic+BZED9B#xJ=*3r|rJ*#2p$wA<0-qprdEHl4JTQ$tr+NotAZtI7i1K;ydrp|5f z*2Yq6C)-4F5>cpPVjTSoBIuTyi^FUusBC=WrQ!ORo&U4PC8ebmt*laT!8;-ZK&?#T zXM$M3_9ZMSEdO+!hr?Q9(A-lrGOxQFhG_Gu&s^IT@XU|>(1>ZtFNGkkpeX=+O9mg+(-j5a_2&MvB>w^e`&yyz~ z9d`D{+_-Tg1rM+us6G$SAVrZ>yoA;4%gGGbE%@R>dHmQe)_WYQ9|uyeSkA=orZfZq z66f4lkJ^KBKUO273A4kzbf=MG);;h4*#L(8j%1baSQ5f>vMqyk=hB z_;Bk~V1*cRf#2^rEbBMj>1iAbJImt(ApzwLs|iiAb#14NVLC`j?TgO+`KB^dG!b53 zR{!)f6Stl&+CK-Zk~KPV49qp8%Ys5zbke1m3i%Mi{rPoX|>p^(iy z9k6lvMyn;&_wjkx!^crQRZq_6&F*Ze@JH@#ma;k^ARtlc@u^Ti3SFfhQbW&UW)NS- zUyZBbtR3(P|7+y6($=jtsngv``d0E+?&o#sAZe0aSc-^L3J^M$ipC7ySo zbtKO_$bWI}bk63AuDPoZcXY*Uo9ac=$qOACDr(xl*pN1ye46Fsv)@3(V_hemtLtP< zG7HJ(a#{dN{_uV}d<9}F^Q5B)AyDBx16$QlQjc+Xo=?Z06Lj4vDNqmQU@H84}@3TdQ7vns03LD?o5F(5s;(0YrEKDYc|Wb&?qh*8|hau8d*Rf}KyLl>?@7 z>*7wJ>PfVJH}P#ZsLULL@x{f(R>Z%owjmplNODHd3b3WZmFMjSzQjCzsm+|iC4dnW ztQ}oU);h+DnZ9*soy$sHt>p|di13ntGWLLujmF|+pnZ1d5rI3J5$vJ8s{ubcq${c* z!3%Ii+^=>B)&lKs2Edvz&>e#FhYde&ww*=FQ;&yYL55uIj}yNSl+Hp$5>el&IL7_Z z;ywb~J2>WmEIxFdUvsA+6)7Y5g@uK!7S&AhEKC91nIQpbu{!W#Lxa7tP_ADMG-3lW ze20qWgS>Wk_34WE5R{=U-^!&p7r+sd%JrKB7$JyJG*3~Mw`nhxXQM||jNOA3pC@{r zeQQjywho-U(eMZK%r>}0o<~~-BiXG z%Mu#9`t$6W&XsDf6%pHkKU~k8Ml*5uhwddn8O_B!`H#OCcv#cqJ3I&+Uxq5V0}KXl z2m0?sd7C3(v2xX_`|yhiXp_kvKi;07^sIb~U8|SmaHlKd@8Pw**8!wvqZD*X@BogZ zzC&2?T`vFEzamA>yk7J3^cuWkgvrfDJST{Cl=3)u<~~fR)!b&RpuW64{)jo`u+R2Vd~xx zqdcc6!Cj)X@GiWJkvu911JkN#MTzEN&`j)M+N+W`6;dWuf5cFJjxWIGQsKsSS|)Bn zwB>q7lFSVK&n;tmkAQ7~Boi9iGw53qDzegOKC&a5{pWB$7>QTMO0hYnyl zfQSrM12~8HlNT!cH)-|Xsl&>J1(80s0{oca@3N7Y@ULkyJp6HT6B2NSKnxWa6{JcI zmb6_@#W@Iomt-jjdV?QocyRs|7Q5GLN&Sv>ON=}C!hCAL-edFVqti`WpjucKtKtY< zar)kXXR!PC(&o>zqmxq1%*?-Z-DBYU1}|Nt!?P;2IQIIxy7H+jcMV)!a~leZJ}`Ur zFCopdqotN!NyEgu!m2XJYV$5{6L_>@DBZ()4?!E;Qt6atn^YcYEStSr%gf6vVc-cM z{iNN&Jjh~Ev5BXy=k|UK!OLTMqe)c#v>^%UZ1P3lyL9x&1={G8Xr1@lKe;sPG10!gSbpT zVIiR*5KF}tW|_L2<P|txI^n;~otSjqLPYkE|1dyaeTnClUsQ`9v=6k{_SS+J`3|K742r zw$aPP*Y|Nl!Ne8Y0^i@ZiPSo%iW-*OQLPR+bwNAU@!PWoOsZR)qD=iriVIqB_OaO%sTM@IBMd_qWd&$LaeEdL^r(x0y;+sy0u4C4cytNGpqcd+ zX68F!dY&%G2Kf1j^7FqKlWwE>%mC$rIwW(S(wxp~Ffs)I+sptbV<{1U7R6>23ailm z4p-VAmu)PNbW9S(i$nkzj}1IL`S_@IG)l_}Kr%Ozn6mI#Uk~Wnf(bfO@eB`l1e)jlxO@&~B6^1R>f ztFo~KkdkST;$A%3lTjgI5BMOnW9f*`T~WK_pnys4FNd{}0KSKCWY z4eBR0a09poL)_9j24p|PTZq#got`Lf zrYDHL%DEHcQ~C=;MuF?K;Cl3e*KJY#=#E>}l@IMu zjZBcSzuYpHfYUX%aOOW1<-`Qc zp-g>$=|45V`IEDMR^hR@d7oAOtLm&*d$b>id+fGD`q5rsa~p1LuNInrle8lv5-C!4 zL)8d4#{;Lr9z$r$g=sxbhra;?H4prR8+B{z``%fYMFy^Tc|@SrFe1H}ft134fX{?XUV^AtVkdcjxc0tL=9#~2ZF^ZsQD^uN(!1e3lRzN8cyeu@A?O?t{{*9-1X|9gceGtssnbAXpXAZ&*bstFjkL~#uNfpm|a-6c5M^LRlREFUE4qxlR9ST zbw8*Fq8R%UnPYT!KhZtk}kV!2u7X`gTD2G7o2 zQYw-FbqQ9D-Df{}1nXBMN9zR95K$KoiOzyQBDi8!!f0og{+RL(hq}0iF9Vw~5Um~1 zvg^BGBxp{~Vt+6&Abu_$do~IS>@i+U{2R_fo_m?7apo*t}c!KFEJ7>&GQqlpi{*)lv=Q}vi245nYW|| zQYt@P3t}QPSp8aFNQc3ojEjvA-%KkY>KS#khsP@q~ zM~z^*sBEjUl7<58=geL8k0#5vp-O614RBTT{CpNjv_o+&QYwoQ*LdUcfSJU{uAMS1 zKkI;d5gEs;*RQ$y4iloMb6ODzDbcy80%`Y(qWOxp26U6&T72CLYKq7;5YLm6twBS$ zja>lQnCYidXkV8J>Zr-t95Ez`Zb}L$JBo61#j*Re5JgD#oa(mFurL?wEPam$5JeODGuTY|B(2?Z-vz)_DK~V_+nm~D z>AL8U*-mC{PbodbbLx&*?Ir+WzGU5!8ga6L>21iIVxF5SxKanOiw{C@qp$|R2 zRcL!dvfKs@wafHrWOoE&y1kUFR0n|~;Dz^4Cc9OT!JZ^-OBhIjj22TJpPR_O==y`+ zeaox_)B0qUZinJuNH?7EjKH7;WXl_%p3H~h#0k}!7#>3gtpH6i_J!3BNlsB=;eE&t z%ut;c170iqnv2beiYg10OLAsH00^*Fuu1hwC$3()WJn-gg|82NKKhJyG&yY1gYat@ zudqo-=H3WZ7C@2WCzVhv$$NfQ%c2>@9Sq}TG>K76X zX*laBUqb-%dPxQ}BIH5jv#Zq%6?qxabZzymK>~?3xE9l^kKt(@HhxAlmOstK0j~`a zz^;!m(+HbvoSaVCTwPfPTOzJAVaul?x@_a+RRg_nYGyA{lmHKfh|CCS6)fKzZup)~ zU5^drygd*RA=AcaGj@z5K^4>X9RN;_(Fd9MLFOjalhf0O@DajvV|%{M6^F_QZnX5f zU(aTO!+MBXQoBTyBQfWG+~P2KR?>?CHolNzyS< z^K}>@#-Z{#93|-!7~0o~LrZ9MlmrqbwTO7dHr1>1tSgU_bYj-39;!zI+(A})_{wLm zg_#p*WfX0-pd}D8NQnBNn{*o`M@?kIv>FfKA!Gf;VG^FgLU4r!DpcUW&60MN1uo1)|`5evlP&(p)xzpbMUc;ATzfePC zJITrGIiqi~bwIiK0#^)54Ks)rUEdLX4N*^HD2No=0mMM95uLOts04Jiwej0MlJB}z z{?~U*g^gPNu;tl%^bjE9Rxp=&1QQ-9Y;qnHW+xomLLU851- z@Fa-f!ZNNL{k>(>VP&eaf3e9KQZook?3gu(fK$lyOT5ahXhb3zi5&h}(@+;e?Vp7W zejjTqb}0XLC!+^E8ev&MD%^vLhm+l1nZQ)WYNWYieAQXY(cVgeJsw0kpX6>9jdmd{ zDN$_9VFnGu2KNO>EQxMDFU|_mFw_x7@ICUUhfN|j6@OkigPGI1gHuw3c;r9ER%{h5&rIIT#|sTk@BFdm#wlfG!5K zVIDm1N9RHzOL)p$&?}Zia0=zAUhxN%scdlJm>huS;}OIH5=x1lo4^MB0um~WSnqTw z@b00~U)Ch!e-U;40s^)nNy3bZc7V=B!IU6^CBvoJrmH;jo&IBlvIT2;x*P+dGTm+h zltEXzHTri+QR+R~k&3-FjABdDIt-LX{FF7yC#TJDoTqV8i7=YThruWlUChl;uBdT@ zLcn0HclNA+bKZt32SX`q8_w6h3!gA4Gs(D`ThTr*aB12?z$ z5QVX@6x$&2=Y1$wFqYCvrx~?O5zrkbv@1|mQC~@;;Z-MZ1|;$ZNZ{QQyx}cSRJZ3| zs6ggoidUOS2Y4W%B+##VU=K*)A03a>k%TKG2BD9D>?L3@MZQtU!_kUE06_GX-h(TE z7@dKZOqlpgH3x{t8x_bLbZQMR7>x838Id`l$cmw#Kmv$b zCv>46x~Y@Hlc*OCVO4~505iS^(51NC5-x&T#j_02w^JOU_%>pz1X>M=){c*lFFFI1 zl@{DrJHE^~8&5$JkZ6z4vlzVlMNiK#x?S<6CtjVWo?p(B`49*Uf3M|QFB!V3AEWZc z=ml+pV#JzwF&y$|f}-e-!qNSJdYy4T9@mo-8_NdGwt4L2_a=3a_=eCbL4<1K9Cs4@ zDLmv+MDNdcNVEEF4Uu#znU};lg6T7{_Q9JQmcKPUCcogw1F<_&yuSA?jmYPqDd@xu8QQ7WJPz!~YlIY=0F*6fco;K-j!5PX!$xbjsM>N_X6o_IB%%2mYAN2DBpJ9-y88AEfFWT?Z?=An~pC0{x#t$rSg#TqkLlvbx za`QFv_iN@TPdA@?$Bc$NgXJR}K*n!Ub*x|ZV#%M$XQo!t;`fIlWcbg|x{*O}v_r3a^4%vB%x!B(JfDgO`uZ zNqb&hU3K!Ee}4V50{^VQ|DP*B`O5KdxP5nCU9Guc-JhR+DT@zih0at` zC`(HZgpL2Ap|ce@E^Q&Eo6dN#_B{u>Pkva0;NewYWd?uqMb>@V}g9g_v{Z4yDWdgp+$@B zoTtXEtB;(CozrVwoCTSwVsxo1t%4~-$9fxt!uI_$zNx#r)@vS zdS%c4Epz^+Q}bEDGQo?xDU^PzNSn~>2QQznzp#;2XpLC4+tkX{YaDJ9J1KOV9C&d( zdbZ!}ZOhkDnj7bbmgbsV0uoN~`8=G+wLzgw6U`q1x4h zWqY*Q+49w1XA?V**2+3#|HL>`=I(y`cz;8@9>>+9{-jlVYb<=Z$2&xwMi(eIt;#EJ zJ+N=e(kydis&_Qh;k~j3#ZXt?TI(#LgSKbO&y7J!s1upiEyG@5i0b%-1yD zJz=ouekbbd@!uyE#xoA@Ta)NCVIC4&r~Xn*v`}o-fuD8!l;KJWWqgMlvC~eMW6QgS zD#k$ikdN6RhoG@s|3Wqfp1#7uKk6dd>gJwWkN~zl_STTz6?7?F^jYW`iBJ9Qw|x++BZE8yyJ$btCJD;&bSHrOI z=j>n34liDYUEn{#w3SyuUx!bos^M}(Sd=pjh1eNoa&cy+qB3NrBrT<6Ti&$BZ7oez zHr)TP;~h(VhhdFGw0ZplWpfq|*SBf$>#q!*qw=darRf{&?sV+!E%-yBj4a#vK_6j@ z=Vh!jUN~EnG~*CGe=xw2y<0u+t5pk2HfX@Qt+d&vAIZ&>l$)sDI-hWvajBu~Xp~Qq z?l*PL@I&c4i`OTR>zQTPDvr(-@68z3tD?9-@p>b|)8%GfDkPu3WSs zb|x8($@Lg(FTUeBmG)ax^y}7|46D`GLcN;aKfdWLr%=^y|LqfCdf1DLn~_IYoAvrclTdqa+>MIEyJO@`6}|W5IZ>>a?~Aqj@8>li9Vai(d)w&4 z#h1D)83O$4WnQki7`htru9w2+w?(eaT_d5vcQ4&-mDW$n#7PkYhFnL()!W-6LmEej zoiTcxcXB<`$*Q}>=9;{7!NEHui(>2YNNbi%d6o)}>BT<|sGi1<3U* zF`aS{BzCH3j6I}_88ZE@?OQn%`MmQw`*qgQY1nD;?(&J=prJ2xfq!hv(H&15SKRih z3#a2{73AE^9=u4&d$GLEOqR~P#Llal(auQEKKTcfh1n;SeaFZC$QmY|SFqftu*=6t zxx1I6iEc1{pI^uwU3>3+*)9Ps_VyL8<0DSK&mUg4!&*q5R|{MIJ!cvr|GS*Bn|djW zy^ZmP^AGpLo?mm4mT~s&Ud;?M!_bF@CB}}t(ax-*H`vAAZgAWcaEl}6fbOOD#LiE~ z?xXNOdMqvBD-W`6*U-8joZDTYa7?$|JN0qU9nVogHQqV95UM&B);^6*Ta{^<3*trJ z+!b|<(-`pb;MaZ6sQ8Z9`NLFA{FojyFZ&hUfl3vFrG=rX`%N_}MS{*gZ-LWig;#U9vBY ztLQkVw)ih`CjOmW=dvPn@=qIc$*{Q`kd48FLTrrXeYmjBczOTm^uL%^RWji{zrXLw zww>xmxy@_SE{F40v;LR=dVb;tUfF|R60ax}XEBS{D&MzXe;1zO^#7E#z6lN7H3{Y~{%Iy!;?m14HeLWrCuT zirZJovu_Ft7B!dTJ8;eT1r70b_yQT8aV=emwf`nN{YrMFk~b%Z}L-~FQ` zzyA2!$3+ZdN=L$nF6u>J-hb)l@zgmBdr__DwHI+Hy|J;%|K(OWOT)cwO73)lD;E?tDcr**+M{e2qE( zRp{?U#_yCHfp!5RcV(<@w%^-dJ*9h=lLg98O37kGiF3F}1e*?{mSD?wDo;aRUJi*>7q_qpooaQt;gvSOd_61y4j5K$ z%xt=8pT>Ysz_F)(PA&Zhq$s5WF232%+J4{jIi&mZ@b%-{Z7%UEU)mtS=v$>0(l16E zytr65JV1FoeT|mbnP5EjUvZG*J)5BMuA;`ej;w=W?_TFl#-7YndRe-f<)Mj;@>Ob^ z>ZXC0mlb0Ac=md!aU4_)7TXlO6f||FZbK!pGs`UNz=rcK-aksCsnxz@y}R+skY10! zrr6eJ&*R*<92EKV?FOHoj8SuDxYTj$e4>$=OUhtMvz*Mi?E;+%4!-*fTZx_Vb@aBx zk4dBiPqdCN1V`;l*W&fKDk8el$nm`)`vpr*wj*DQT=w(2*kLn~{gbjdwY$>sMSSIN zr`M);nG-ggBzAr?bQ3>zRYzW@FkCR6EkTYcSMwvQXv<|S%I+m6ul?U_*k0PL3S&|M z_HvH%y(jyoBe$Qe_+I;-{t~nD6Yt55)5~#ZW}UG!?#ozw(OO`3yZ0rzbMNGCU#-pb z{Bv`DDY$6I?>x!VUEx7`4h^;m^z0nNv^nvQ7!Ka~{O#Ue$)J8EioJm1T5>&;^Z^db zexqz_;imIC`|9^^l-1Qd$4!}kQ@wCNpQ39X@LH~2}r?i2p$5xT$T*n{UM zW;bR}6y*063|}O6D2xKmI2U@_P6ajn0p&e$)0D|EcR&5X@5iAk~*!>ZNp*9w_yjxKe%{HPJl$`ty;~1Wjp_hUq&ybH65HR-?z$BTV;RS&bixp z?Aaex+0eanu9<~0p;Cg)g*gRcny=y`CDV#}WxVr*>Mb<6&QLxDttNJUGHyGHJX}vg zb3i1miJgTeN%t~kDR?otXVWR!6t<6}v8k?AC&gbzwJTo`@Kk$culKBtq6$83?0>FJoT}P-53M0wIzz9x&D=C;`t8pbx~5;^ z5ephHka1wM-LNe>S!<}YBm7vZ<-YR;Ig_W!^~^CPlJgD$@?>7+gZrfm*%c;#jBbCM zzmjTmtbt4Ibwbwqv_Cp>qN(QFn>kZ-+BQ-sz0ZagwftRd zl6CYAtqG95sPJFknb@J#;t(Ma0l!@A!LF_Ct?fN?| z>n^0U?hjGCMHSC}no{>}&SN$-OVP;VhWt8FPnSe}6>B!yY zt9CJaB4f9k>k~T^)&Pe!Yp7b7HTE3OAAR@QFE-SxK-A>jcgJkmtX|4eLE)(A8MA4p z&VKp6mzEb>kJdfa%Zh{^xYoqQ?E1)|`(@88TUG6OPW#L={wk68MfZw{Y4#5; z4uyoAV~D-?{@e2YnP8M%&NsBxb@@3@$@}^BKlwCTa%fYBqviSvbDAipixO!{o#J`+ z&dTKS#Qfu=Kt{|7A`;B8)J7`F=JYwhNB-b;?JZ}sC9K*s8dR)ItKvmh}rlS9Y zpWob14e9gT&Eg2q<3Gu2YH7H>vD85ky}CaNdVxPX`yCYzKfSCQ*_pW7M7K~p?t|-}7QH=Txy}h2Xs;DiB`8Q0 z>LrNTeq-h1Obnr;7d%1iOfa+@MgFX(6CvlN&QHr(Qn-{M>cVuk{{Ydk=Gded{M@GkmLa`B$S*Wk12<%3tKp9{5&kIat z4$E;_+e|DWreP^x=E@CzF`ji{A5vb2Yp8u!;uVpemeVo$SH>``yGKt`R#I`Nqzv9_ z^!G1vJqt|7{;s?K^_TzYzyBLMi|f*zv48Z$|I_#X&A*fDnPnKazxmvM`X^$C z!f?zy_y2_*5DVG_KK)NW^xyou$sR3vUSs=X9`1Ihcs(3=jh$TW-H+M-Pf+&XGZ%YZ WZQNaW_nfwK^7x-$`EUG*@;?B9P27(F From 71f89966439abc0d7c927c20d32f148a8ec2bf48 Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Sun, 28 Apr 2019 11:54:28 -0700 Subject: [PATCH 11/17] Filter headers from select ingredients --- .../src/components/select-ingredients/select-ingredients.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Frontend/src/components/select-ingredients/select-ingredients.ts b/Frontend/src/components/select-ingredients/select-ingredients.ts index fb1aea8e7..7f1f4391c 100644 --- a/Frontend/src/components/select-ingredients/select-ingredients.ts +++ b/Frontend/src/components/select-ingredients/select-ingredients.ts @@ -47,7 +47,7 @@ export class SelectIngredientsComponent { } applyScale(init?) { - this.scaledIngredients = this.recipeService.scaleIngredients(this._ingredients, this.scale); + this.scaledIngredients = this.recipeService.scaleIngredients(this._ingredients, this.scale).filter(e => !e.isHeader); this.selectedIngredients = []; for (var i = 0; i < (this.scaledIngredients || []).length; i++) { From 6fd545097137647df7a214ed873eb76fba33c3d6 Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Sun, 28 Apr 2019 12:20:30 -0700 Subject: [PATCH 12/17] More typings --- .../components/select-ingredients/select-ingredients.ts | 8 ++++---- Frontend/src/components/select-recipe/select-recipe.ts | 2 +- Frontend/src/providers/recipe-service/recipe-service.ts | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Frontend/src/components/select-ingredients/select-ingredients.ts b/Frontend/src/components/select-ingredients/select-ingredients.ts index 7f1f4391c..a358d5b3e 100644 --- a/Frontend/src/components/select-ingredients/select-ingredients.ts +++ b/Frontend/src/components/select-ingredients/select-ingredients.ts @@ -32,7 +32,7 @@ export class SelectIngredientsComponent { @Output() selectedIngredientsChange = new EventEmitter(); @Input() - set initialScale(val) { + set initialScale(val: number) { this.scale = val; this.applyScale(); } @@ -46,7 +46,7 @@ export class SelectIngredientsComponent { }); } - applyScale(init?) { + applyScale(init?: boolean) { this.scaledIngredients = this.recipeService.scaleIngredients(this._ingredients, this.scale).filter(e => !e.isHeader); this.selectedIngredients = []; @@ -58,7 +58,7 @@ export class SelectIngredientsComponent { this.selectedIngredientsChange.emit(this.selectedIngredients); } - toggleIngredient(i) { + toggleIngredient(i: number) { if (this.ingredientBinders[i]) { this.selectedIngredients.push(this.scaledIngredients[i]); } else { @@ -70,7 +70,7 @@ export class SelectIngredientsComponent { for (let idx in Object.keys(this.ingredientBinders)) { if (this.ingredientBinders.hasOwnProperty(idx)) { this.ingredientBinders[idx] = this.allSelected - this.toggleIngredient(idx) + this.toggleIngredient(parseInt(idx)) } } } diff --git a/Frontend/src/components/select-recipe/select-recipe.ts b/Frontend/src/components/select-recipe/select-recipe.ts index 2f37ec93e..9ca441ae6 100644 --- a/Frontend/src/components/select-recipe/select-recipe.ts +++ b/Frontend/src/components/select-recipe/select-recipe.ts @@ -38,7 +38,7 @@ export class SelectRecipeComponent { public navCtrl: NavController ) {} - search(text) { + search(text: string) { let loading = this.loadingService.start(); this.recipeService.search(text, {}).subscribe(response => { diff --git a/Frontend/src/providers/recipe-service/recipe-service.ts b/Frontend/src/providers/recipe-service/recipe-service.ts index 6df19b583..81745bd3a 100644 --- a/Frontend/src/providers/recipe-service/recipe-service.ts +++ b/Frontend/src/providers/recipe-service/recipe-service.ts @@ -105,7 +105,7 @@ export class RecipeServiceProvider { ); } - search(query, options) { + search(query: string, options: { labels: string[] }) { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' @@ -124,7 +124,7 @@ export class RecipeServiceProvider { ); } - fetchById(recipeId) { + fetchById(recipeId: string) { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' @@ -317,7 +317,7 @@ export class RecipeServiceProvider { ); } - importLCB(lcbFile, includeStockRecipes?) { + importLCB(lcbFile, includeStockRecipes?: boolean) { let formData: FormData = new FormData(); formData.append('lcbdb', lcbFile, lcbFile.name) @@ -394,7 +394,7 @@ export class RecipeServiceProvider { return lines; } - scaleIngredientsPrompt(currentScale, cb) { + scaleIngredientsPrompt(currentScale: number, cb) { let alert = this.alertCtrl.create({ title: 'Recipe Scale', message: 'Enter a number or fraction to scale the recipe', From 006ea2e03b7b009aba3807717293194191d1d5d1 Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Sun, 28 Apr 2019 12:21:27 -0700 Subject: [PATCH 13/17] Add spacing below shopping list modal --- .../add-recipe-to-shopping-list-modal.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Frontend/src/pages/recipe-components/add-recipe-to-shopping-list-modal/add-recipe-to-shopping-list-modal.html b/Frontend/src/pages/recipe-components/add-recipe-to-shopping-list-modal/add-recipe-to-shopping-list-modal.html index c3565e94b..1492989f4 100644 --- a/Frontend/src/pages/recipe-components/add-recipe-to-shopping-list-modal/add-recipe-to-shopping-list-modal.html +++ b/Frontend/src/pages/recipe-components/add-recipe-to-shopping-list-modal/add-recipe-to-shopping-list-modal.html @@ -42,6 +42,8 @@ [ingredients]="recipe.ingredients" [initialScale]="scale" (selectedIngredientsChange)="selectedIngredients = $event"> + +
From dda68e71e84d77f7e0b869d7f6eefc43fa055a86 Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Sun, 28 Apr 2019 21:17:07 -0700 Subject: [PATCH 14/17] Update release notes --- .../pages/info-components/release-notes/release-notes.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Frontend/src/pages/info-components/release-notes/release-notes.html b/Frontend/src/pages/info-components/release-notes/release-notes.html index 50ca380b7..c22b98656 100644 --- a/Frontend/src/pages/info-components/release-notes/release-notes.html +++ b/Frontend/src/pages/info-components/release-notes/release-notes.html @@ -20,6 +20,13 @@
+ v1.7.6
+ - Add ability to temporarily check-off/mark ingredients or instructions
+ - Updated welcome screen text
+ - Fixed a bug where ingredient headers would appear in "add to shopping list"
+ +
+ v1.7.5
- Add support for headers in ingredients and instructions
- Add tutorial for adding headers
From 247e899ca90b9c29425fd8b8be95e3009e259eea Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Sun, 28 Apr 2019 21:17:19 -0700 Subject: [PATCH 15/17] Added paprika import --- Backend/routes/index.js | 156 ++++++++++++++++++ Backend/services/util.js | 18 +- .../import-paprika/import-paprika.html | 66 ++++++++ .../import-paprika/import-paprika.module.ts | 13 ++ .../import-paprika/import-paprika.scss | 9 + .../import-paprika/import-paprika.ts | 126 ++++++++++++++ .../settings-components/import/import.html | 4 + .../recipe-service/recipe-service.ts | 24 ++- 8 files changed, 411 insertions(+), 5 deletions(-) create mode 100644 Frontend/src/pages/settings-components/import-paprika/import-paprika.html create mode 100644 Frontend/src/pages/settings-components/import-paprika/import-paprika.module.ts create mode 100644 Frontend/src/pages/settings-components/import-paprika/import-paprika.scss create mode 100644 Frontend/src/pages/settings-components/import-paprika/import-paprika.ts diff --git a/Backend/routes/index.js b/Backend/routes/index.js index 13e33b41e..577308f0b 100644 --- a/Backend/routes/index.js +++ b/Backend/routes/index.js @@ -9,6 +9,7 @@ let mdb = require('mdb'); let extract = require('extract-zip'); let sqlite3 = require('sqlite3'); const performance = require('perf_hooks').performance; +let path = require('path'); // DB var Op = require("sequelize").Op; @@ -750,4 +751,159 @@ router.post( } ) +router.post( + '/import/paprika', + cors(), + MiddlewareService.validateSession(['user']), + MiddlewareService.validateUser, + multer({ + dest: '/tmp/paprika-import/', + }).single('paprikadb'), + async (req, res, next) => { + if (!req.file) { + res.status(400).send("Must include a file with the key lcbdb") + } else { + console.log(req.file.path) + } + + Raven.captureMessage("Starting Paprika Import"); + + let metrics = { + t0: performance.now(), + tExtracted: null, + tRecipesProcessed: null, + tRecipesSaved: null, + tLabelsSaved: null + } + + let zipPath = req.file.path; + let extractPath = zipPath + '-extract'; + + new Promise((resolve, reject) => { + extract(zipPath, { dir: extractPath }, err => { + if (err) { + if (err.message === 'end of central directory record signature not found') err.status = 406; + reject(err) + } + else resolve(extractPath); + }) + }).then(extractPath => { + metrics.tExtracted = performance.now(); + + let labelMap = {}; + let pendingRecipes = []; + return SQ.transaction(t => { + return fs.readdir(extractPath).then(fileNames => { + return fileNames.reduce((acc, fileName) => { + return acc.then(() => { + let filePath = path.join(extractPath, fileName); + + return fs.readFile(filePath).then(fileBuf => { + return UtilService.gunzip(fileBuf).then(data => { + let recipeData = JSON.parse(data.toString()); + + let imageP = recipeData.photo_data ? + UtilService.sendFileToS3(new Buffer(recipeData.photo_data, "base64"), true) : Promise.resolve(); + + return imageP.then(image => { + let notes = [ + recipeData.notes, + recipeData.nutritional_info ? `Nutritional Info: ${recipeData.difficulty}` : '', + recipeData.difficulty ? `Difficulty: ${recipeData.difficulty}` : '', + recipeData.rating ? `Rating: ${recipeData.rating}` : '' + ].filter(e => e && e.length > 0).join('\r\n'); + + let totalTime = [ + recipeData.total_time, + recipeData.cook_time ? `(${recipeData.cook_time} cooking time)` : '' + ].filter(e => e).join(' '); + + pendingRecipes.push({ + model: { + userId: res.locals.session.userId, + title: recipeData.name, + image, + description: recipeData.description, + ingredients: recipeData.ingredients, + instructions: recipeData.directions, + yield: recipeData.servings, + totalTime, + activeTime: recipeData.prep_time, + notes, + source: recipeData.source, + folder: 'main', + fromUserId: null, + url: recipeData.source_url + }, + labels: (recipeData.categories || []).map(e => e.trim().toLowerCase()).filter(e => e) + }); + }); + }) + }); + }); + }, Promise.resolve()).then(() => { + metrics.tRecipesProcessed = performance.now(); + + return Recipe.bulkCreate(pendingRecipes.map(el => el.model), { + returning: true, + transaction: t + }).then(recipes => { + recipes.map((recipe, idx) => { + pendingRecipes[idx].labels.map(labelTitle => { + labelMap[labelTitle] = labelMap[labelTitle] || []; + labelMap[labelTitle].push(recipe.id); + }) + }) + }) + }).then(() => { + metrics.tRecipesSaved = performance.now(); + + return Promise.all(Object.keys(labelMap).map(labelTitle => { + return Label.findOrCreate({ + where: { + userId: res.locals.session.userId, + title: labelTitle + }, + transaction: t + }).then(labels => { + return Recipe_Label.bulkCreate(labelMap[labelTitle].map(recipeId => { + return { + labelId: labels[0].id, + recipeId + } + }), { + transaction: t + }) + }); + })) + }); + }); + }); + }).then(() => { + metrics.tLabelsSaved = performance.now(); + + metrics.performance = { + tExtract: Math.floor(metrics.tExtracted - metrics.t0), + tRecipesProcess: Math.floor(metrics.tRecipesProcessed - metrics.tExtracted), + tRecipesSave: Math.floor(metrics.tRecipesSaved - metrics.tRecipesProcessed), + tLabelsSave: Math.floor(metrics.tLabelsSaved - metrics.tRecipesSaved) + } + + Raven.captureMessage('Paprika Metrics', { + extra: { + metrics + }, + user: res.locals.session.toJSON(), + level: 'info' + }); + + res.status(201).json({}); + }).catch(err => { + fs.removeSync(zipPath); + fs.removeSync(extractPath); + next(err); + }); + } +) + module.exports = router; diff --git a/Backend/services/util.js b/Backend/services/util.js index b10d40ba6..3d4b200ed 100644 --- a/Backend/services/util.js +++ b/Backend/services/util.js @@ -7,6 +7,7 @@ var Raven = require('raven'); let fs = require('fs-extra'); let sharp = require('sharp'); let path = require('path'); +let zlib = require('zlib'); // Service var FirebaseService = require('./firebase'); @@ -137,13 +138,15 @@ exports.sendURLToS3 = url => { }) } -exports.sendFileToS3 = path => { - return fs.readFile(path).then(buf => { +exports.sendFileToS3 = (file, isBuffer) => { + let p = isBuffer ? Promise.resolve(file) : fs.readFile(file); + + return p.then(buf => { return exports.convertImage(buf); }).then(stream => { return exports.sendBufferToS3(stream); }).then(result => { - var stats = fs.statSync(path); + var stats = isBuffer ? { size: file.length } : fs.statSync(file); return exports.formatS3ImageResponse(result.key, 'image/jpeg', stats["size"], result.s3Response.ETag) }) } @@ -290,3 +293,12 @@ let emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; exports.validateEmail = email => emailRegex.test(email); exports.validatePassword = password => typeof password === 'string' && password.length >= 6; + +exports.gunzip = buf => { + return new Promise((resolve, reject) => { + zlib.gunzip(buf, (err, result) => { + if (err) reject(err); + resolve(result); + }) + }) +} diff --git a/Frontend/src/pages/settings-components/import-paprika/import-paprika.html b/Frontend/src/pages/settings-components/import-paprika/import-paprika.html new file mode 100644 index 000000000..de4cadf3c --- /dev/null +++ b/Frontend/src/pages/settings-components/import-paprika/import-paprika.html @@ -0,0 +1,66 @@ + + + + + + Import Recipes from Paprika File + + + + + + + +

+

+ This will import your Paprika export file into Recipe Sage. Please note this feature is in beta.
+ All files must be in .paprikarecipes format (export as Paprika Recipe Format in Paprika settings). Other formats will not work. +

+
+

+ The import task can take several minutes
+ It depends on the format and size of your file
+

+
+

+ If you're having trouble with the import, send me an email with the file and I can import it manually.
+ If your file is very large, you may have trouble importing it via this interface due to browser limitations. +

+
+
+

+ Please consider donating if your file contains a large number of recipes or images. +

+
+ +
+ +
+ + + +
+

+ Warning! This filetype does not end in .paprikarecipes
+ It may not work. +

+
+ +
+

+ This file is too large to upload directly.
+ + Please contact me to have your database imported manually. + +

+
+ +
+ + + +
+
+ diff --git a/Frontend/src/pages/settings-components/import-paprika/import-paprika.module.ts b/Frontend/src/pages/settings-components/import-paprika/import-paprika.module.ts new file mode 100644 index 000000000..2b68bb9bb --- /dev/null +++ b/Frontend/src/pages/settings-components/import-paprika/import-paprika.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { IonicPageModule } from 'ionic-angular'; +import { ImportPaprikaPage } from './import-paprika'; + +@NgModule({ + declarations: [ + ImportPaprikaPage, + ], + imports: [ + IonicPageModule.forChild(ImportPaprikaPage), + ], +}) +export class ImportPaprikaPageModule {} diff --git a/Frontend/src/pages/settings-components/import-paprika/import-paprika.scss b/Frontend/src/pages/settings-components/import-paprika/import-paprika.scss new file mode 100644 index 000000000..b8c21558d --- /dev/null +++ b/Frontend/src/pages/settings-components/import-paprika/import-paprika.scss @@ -0,0 +1,9 @@ +page-import-paprika { + .description { + text-align: center; + } + + .actions { + text-align: center; + } +} diff --git a/Frontend/src/pages/settings-components/import-paprika/import-paprika.ts b/Frontend/src/pages/settings-components/import-paprika/import-paprika.ts new file mode 100644 index 000000000..1d58c877e --- /dev/null +++ b/Frontend/src/pages/settings-components/import-paprika/import-paprika.ts @@ -0,0 +1,126 @@ +import { Component } from '@angular/core'; +import { IonicPage, NavController, NavParams, ToastController } from 'ionic-angular'; + +import { LoadingServiceProvider } from '../../../providers/loading-service/loading-service'; +import { RecipeServiceProvider } from '../../../providers/recipe-service/recipe-service'; +import { UtilServiceProvider } from '../../../providers/util-service/util-service'; + +@IonicPage({ + segment: 'import/paprika', + priority: 'low' +}) +@Component({ + selector: 'page-import-paprika', + templateUrl: 'import-paprika.html', + providers: [ ] +}) +export class ImportPaprikaPage { + + loading = null; + imageFile = null; + + ignoreLargeFiles: boolean; + + constructor( + public navCtrl: NavController, + public loadingService: LoadingServiceProvider, + public toastCtrl: ToastController, + public recipeService: RecipeServiceProvider, + public utilService: UtilServiceProvider, + public navParams: NavParams) { + + this.ignoreLargeFiles = !!localStorage.getItem("largeFileOverride"); + } + + setFile(event) { + let files = event.srcElement.files + if (!files) { + return + } + + this.imageFile = files[0]; + } + + filePicker() { + document.getElementById('filePicker').click(); + } + + filePickerText() { + if (this.imageFile) { + return this.imageFile.name + ' Selected'; + } else { + return 'Choose .paprikarecipes file'; + } + } + + isFileTooLarge() { + if (!this.ignoreLargeFiles && this.imageFile && this.imageFile.size / 1024 / 1024 > 550) { + // File is larger than 550MB + return true; + } + return false; + } + + showFileTypeWarning() { + if (!this.imageFile || !this.imageFile.name) return false + return !this.imageFile.name.toLowerCase().endsWith('.paprikarecipes') + } + + presentToast(msg: string) { + this.toastCtrl.create({ + message: msg, + duration: 6000 + }).present(); + } + + submit() { + this.loading = this.loadingService.start(); + + this.recipeService.importPaprika(this.imageFile).subscribe(response => { + this.loading.dismiss(); + this.loading = null; + + this.presentToast('Import was successful!') + + this.navCtrl.setRoot('HomePage', { folder: 'main' }, { animate: true, direction: 'forward' }); + }, err => { + switch (err.status) { + case 0: + this.loading.dismiss(); + this.loading = null; + this.presentToast(this.utilService.standardMessages.offlinePushMessage) + break; + case 401: + this.loading.dismiss(); + this.loading = null; + this.navCtrl.setRoot('LoginPage', {}, { animate: true, direction: 'forward' }); + break; + case 406: + this.loading.dismiss(); + this.loading = null; + this.toastCtrl.create({ + message: "Hmm, we had trouble extracting that file. Please make sure it is in Paprika Recipe Format directly from the app. If you're having trouble, please feel free to send me an email.", + showCloseButton: true, + dismissOnPageChange: true + }).present(); + break; + case 504: + setTimeout(() => { + this.loading.dismiss(); + this.loading = null; + this.toastCtrl.create({ + message: "The import is taking a while (this can happen if your database is very large) - please check back in 5 minutes. If your recipes do not appear, please send me an email.", + showCloseButton: true + }).present(); + this.navCtrl.setRoot('HomePage', { folder: 'main' }, { animate: true, direction: 'forward' }); + }, 20000); + break; + default: + this.loading.dismiss(); + this.loading = null; + this.presentToast(this.utilService.standardMessages.unexpectedError); + break; + } + }); + } +} diff --git a/Frontend/src/pages/settings-components/import/import.html b/Frontend/src/pages/settings-components/import/import.html index b6ae43e68..8e7c173c7 100644 --- a/Frontend/src/pages/settings-components/import/import.html +++ b/Frontend/src/pages/settings-components/import/import.html @@ -34,5 +34,9 @@ Import Living Cookbook File + diff --git a/Frontend/src/providers/recipe-service/recipe-service.ts b/Frontend/src/providers/recipe-service/recipe-service.ts index 81745bd3a..a8e825cec 100644 --- a/Frontend/src/providers/recipe-service/recipe-service.ts +++ b/Frontend/src/providers/recipe-service/recipe-service.ts @@ -105,7 +105,7 @@ export class RecipeServiceProvider { ); } - search(query: string, options: { labels: string[] }) { + search(query: string, options?: { labels?: string[] }) { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' @@ -113,7 +113,7 @@ export class RecipeServiceProvider { }; var url = this.base + 'recipes/search' + this.getTokenQuery(); - if (options.labels && options.labels.length > 0) url += '&labels=' + options.labels.join(','); + if (options && options.labels && options.labels.length > 0) url += '&labels=' + options.labels.join(','); url += '&query=' + query; return this.http @@ -337,6 +337,26 @@ export class RecipeServiceProvider { } } + importPaprika(paprikaFile) { + let formData: FormData = new FormData(); + formData.append('paprikadb', paprikaFile, paprikaFile.name) + + const httpOptions = {}; + + return { + subscribe: (resolve, reject) => { + this.http + .post(`${this.base}import/paprika${this.getTokenQuery()}`, formData, httpOptions) + .pipe( + catchError(this.handleError) + ).subscribe(response => { + this.events.publish('recipe:generalUpdate'); + resolve(response); + }, reject); + } + } + } + scaleIngredients(ingredients: string, scale: number, boldify?: boolean): Ingredient[] { if (!ingredients) return []; From ac835f8bfdaaa808d0ec3d0ab5688c924a4fbc8a Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Sun, 28 Apr 2019 21:19:20 -0700 Subject: [PATCH 16/17] Update welcome text for paprika import --- Frontend/src/pages/info-components/welcome/welcome.html | 2 +- Frontend/src/pages/settings-components/import/import.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Frontend/src/pages/info-components/welcome/welcome.html b/Frontend/src/pages/info-components/welcome/welcome.html index fdbc1d51c..4bb80913d 100644 --- a/Frontend/src/pages/info-components/welcome/welcome.html +++ b/Frontend/src/pages/info-components/welcome/welcome.html @@ -53,7 +53,7 @@

Welcome to RecipeSage!

  • Send recipes and notes to your friends and family
  • Create collaborative shopping lists and meal plans
  • Clip recipes directly from the web
  • -
  • Import from Pepperplate and Living Cookbook
  • +
  • Import from Pepperplate, Living Cookbook, and Paprika
  • Export your recipes in multiple formats for data portability
  • diff --git a/Frontend/src/pages/settings-components/import/import.html b/Frontend/src/pages/settings-components/import/import.html index 8e7c173c7..db1123c46 100644 --- a/Frontend/src/pages/settings-components/import/import.html +++ b/Frontend/src/pages/settings-components/import/import.html @@ -15,7 +15,7 @@

    - RecipeSage supports importing from Pepperplate and Living Cookbook. + RecipeSage supports importing from Pepperplate, Living Cookbook, and Paprika.


    From d7b6c9eb20074b41d2d1f4f71eb7ab70e1081926 Mon Sep 17 00:00:00 2001 From: Julian Poyourow Date: Sun, 28 Apr 2019 21:21:09 -0700 Subject: [PATCH 17/17] Verbump 1.7.6 and release notes --- Backend/app.js | 2 +- Frontend/src/index.html | 2 +- .../src/pages/info-components/release-notes/release-notes.html | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Backend/app.js b/Backend/app.js index 32ff9ca7d..70d20375b 100644 --- a/Backend/app.js +++ b/Backend/app.js @@ -22,7 +22,7 @@ var devMode = appConfig.environment === 'dev'; Raven.config(appConfig.sentry.dsn, { environment: appConfig.environment, - release: '1.7.5' + release: '1.7.6' }).install(); // Routes diff --git a/Frontend/src/index.html b/Frontend/src/index.html index 076c538ca..6af6875c0 100644 --- a/Frontend/src/index.html +++ b/Frontend/src/index.html @@ -95,7 +95,7 @@ diff --git a/Frontend/src/pages/info-components/release-notes/release-notes.html b/Frontend/src/pages/info-components/release-notes/release-notes.html index c22b98656..b6b7bb060 100644 --- a/Frontend/src/pages/info-components/release-notes/release-notes.html +++ b/Frontend/src/pages/info-components/release-notes/release-notes.html @@ -21,6 +21,7 @@
    v1.7.6
    + - Add new Paprika importer (Settings -> Import -> Import from Paprika)
    - Add ability to temporarily check-off/mark ingredients or instructions
    - Updated welcome screen text
    - Fixed a bug where ingredient headers would appear in "add to shopping list"