@things-factory/reference-app 4.0.4 → 4.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  import '@things-factory/auth-ui' /* for domain-switch */
2
2
  import '@things-factory/barcode-base' /* for <default-label-printer-setting-let> */
3
3
  import '@things-factory/notification' /* for notification-badge */
4
+ import './components/ocr-viewpart'
4
5
 
5
6
  import { TOOL_POSITION, VIEWPART_POSITION, appendViewpart, toggleOverlay } from '@things-factory/layout-base'
6
7
  import { getEditor, registerEditor, registerRenderer } from '@operato/data-grist'
@@ -134,6 +135,24 @@ export default function bootstrap() {
134
135
  })
135
136
 
136
137
  /* setting app-tools */
138
+ store.dispatch({
139
+ type: APPEND_APP_TOOL,
140
+ tool: {
141
+ name: 'ocr-toggler',
142
+ template: html`
143
+ <mwc-icon
144
+ @click=${e => {
145
+ toggleOverlay('ocr-viewpart', {
146
+ backdrop: false
147
+ })
148
+ }}
149
+ >camera_enhance
150
+ </mwc-icon>
151
+ `,
152
+ position: TOOL_POSITION.REAR
153
+ }
154
+ })
155
+
137
156
  store.dispatch({
138
157
  type: APPEND_APP_TOOL,
139
158
  tool: {
@@ -162,6 +181,17 @@ export default function bootstrap() {
162
181
  position: VIEWPART_POSITION.HEADERBAR
163
182
  })
164
183
 
184
+ appendViewpart({
185
+ name: 'ocr-viewpart',
186
+ viewpart: {
187
+ show: false,
188
+ hovering: false,
189
+ resizable: true,
190
+ template: html` <ocr-viewpart style="min-width: 300px; height: 100%; width: 50vw;"></ocr-viewpart> `
191
+ },
192
+ position: VIEWPART_POSITION.ASIDEBAR
193
+ })
194
+
165
195
  appendViewpart({
166
196
  name: 'notification',
167
197
  viewpart: {
@@ -119,8 +119,16 @@ export class CameraCapturer extends LitElement {
119
119
  - ${file.name}
120
120
  <mwc-icon
121
121
  @click=${e => {
122
- this._files.splice(this._files.indexOf(file), 1)
123
- this.requestUpdate()
122
+ this._files = [...this._files.splice(this._files.indexOf(file), 1)]
123
+ this.dispatchEvent(
124
+ new CustomEvent('files-change', {
125
+ bubbles: true,
126
+ composed: true,
127
+ detail: {
128
+ files: this._files
129
+ }
130
+ })
131
+ )
124
132
  }}
125
133
  >delete_outline</mwc-icon
126
134
  >
@@ -137,6 +145,15 @@ export class CameraCapturer extends LitElement {
137
145
 
138
146
  this.addEventListener('file-drop', e => {
139
147
  this._files = this.multiple ? e.detail : e.detail[0] ? [e.detail[0]] : []
148
+ this.dispatchEvent(
149
+ new CustomEvent('files-change', {
150
+ bubbles: true,
151
+ composed: true,
152
+ detail: {
153
+ files: this._files
154
+ }
155
+ })
156
+ )
140
157
  })
141
158
  }
142
159
 
@@ -151,6 +168,16 @@ export class CameraCapturer extends LitElement {
151
168
  reset() {
152
169
  this.fileInput.value = ''
153
170
  this._files = []
171
+
172
+ this.dispatchEvent(
173
+ new CustomEvent('files-change', {
174
+ bubbles: true,
175
+ composed: true,
176
+ detail: {
177
+ files: this._files
178
+ }
179
+ })
180
+ )
154
181
  }
155
182
  }
156
183
 
@@ -0,0 +1,79 @@
1
+ import './camera-capturer'
2
+ import '@material/mwc-button'
3
+ import '@operato/ocr'
4
+
5
+ import { gql } from 'graphql-tag'
6
+ import { css, html, LitElement } from 'lit-element'
7
+
8
+ import { client } from '@things-factory/shell'
9
+
10
+ class OCRViewPart extends LitElement {
11
+ static get styles() {
12
+ return [
13
+ css`
14
+ :host {
15
+ display: flex;
16
+ flex-direction: column;
17
+ min-width: 240px;
18
+ }
19
+
20
+ ox-ocr-helper {
21
+ flex: 1;
22
+ }
23
+ `
24
+ ]
25
+ }
26
+
27
+ static get properties() {
28
+ return {}
29
+ }
30
+
31
+ render() {
32
+ const files = this._files || []
33
+ const index = this.index || 0
34
+ const file = files[index]
35
+ const image = file && URL.createObjectURL(file)
36
+ if (image) {
37
+ requestAnimationFrame(() => URL.revokeObjectURL(image))
38
+ }
39
+
40
+ return html` <ox-ocr-helper @attached=${this.upload.bind(this)}> </ox-ocr-helper> `
41
+ }
42
+
43
+ async upload(e) {
44
+ const images = e.detail
45
+
46
+ const response = await client.query({
47
+ query: gql`
48
+ query ocrRequest($images: [Upload!]!) {
49
+ ocrRequest(images: $images)
50
+ }
51
+ `,
52
+ variables: {
53
+ images: images
54
+ },
55
+ context: { hasUpload: true }
56
+ })
57
+
58
+ if (!response.errors) {
59
+ const helper = this.shadowRoot.querySelector('ox-ocr-helper')
60
+ helper.result = response.data.ocrRequest.map(result => {
61
+ return {
62
+ ...result,
63
+ boundaries: JSON.parse(result.boundaries)
64
+ }
65
+ })
66
+ } else {
67
+ document.dispatchEvent(
68
+ new CustomEvent('notify', {
69
+ detail: {
70
+ level: 'error',
71
+ message: response.errors
72
+ }
73
+ })
74
+ )
75
+ }
76
+ }
77
+ }
78
+
79
+ customElements.define('ocr-viewpart', OCRViewPart)
package/client/menu.js CHANGED
@@ -80,7 +80,7 @@ export function getMenuTemplate() {
80
80
  },
81
81
  {
82
82
  name: 'Label Scan',
83
- path: 'bizplace-register'
83
+ path: 'label-scan-page'
84
84
  },
85
85
  {
86
86
  name: 'Ghost Print',
@@ -2,11 +2,9 @@ import '@operato/mini-popup'
2
2
  import '@operato/data-grist'
3
3
 
4
4
  import { css, html } from 'lit-element'
5
- import { getEditor, getRenderer } from '@operato/data-grist'
6
- import { i18next, localize } from '@things-factory/i18n-base'
7
5
 
6
+ import { i18next, localize } from '@things-factory/i18n-base'
8
7
  import { PageView } from '@things-factory/shell'
9
- import { isMobileDevice } from '@things-factory/utils'
10
8
 
11
9
  class GristModePage extends localize(i18next)(PageView) {
12
10
  static get styles() {
@@ -0,0 +1,189 @@
1
+ import '@things-factory/barcode-ui'
2
+
3
+ import { css, html } from 'lit-element'
4
+ import { connect } from 'pwa-helpers/connect-mixin.js'
5
+
6
+ import { PageView, store } from '@things-factory/shell'
7
+
8
+ import { referencePageStyles } from './reference-page-styles'
9
+
10
+ class LabelScanPage extends connect(store)(PageView) {
11
+ static get styles() {
12
+ return [
13
+ referencePageStyles,
14
+ css`
15
+ :host {
16
+ display: block;
17
+ }
18
+
19
+ div {
20
+ overflow-wrap: anywhere;
21
+ }
22
+ `
23
+ ]
24
+ }
25
+
26
+ static get properties() {
27
+ return {
28
+ bcid: String,
29
+ value: String,
30
+ bcWidth: Number,
31
+ bcHeight: Number,
32
+ bcScale: Number,
33
+ padding: Number
34
+ }
35
+ }
36
+
37
+ get context() {
38
+ return {
39
+ title: 'Barcode Scan Input and Tag Component'
40
+ }
41
+ }
42
+
43
+ render() {
44
+ const bcid = this.bcid || 'code128'
45
+ const value = this.value || '213439240572'
46
+ const bcWidth = this.bcWidth || 30
47
+ const bcHeight = this.bcHeight || 30
48
+ const bcScale = this.bcScale || 3
49
+ const padding = this.padding || 5
50
+
51
+ return html`
52
+ <section>
53
+ <h2>Barcode Scanable Input</h2>
54
+ <p>
55
+ barcode-scanable-input 컴포넌트는 카메라 입력을 통해서 바코드를 스캔하고, 스캔한 결과를 입력 필드에 적용한다.
56
+ </p>
57
+ <barcode-scanable-input
58
+ name="barcode-input"
59
+ custom-input
60
+ @keypress=${e => {
61
+ if (e.keyCode === 13) {
62
+ e.preventDefault()
63
+ this.value = e.target.shadowRoot.querySelector('input').value
64
+ console.log('input completed.', e.target.value)
65
+ }
66
+ }}
67
+ ></barcode-scanable-input>
68
+ </section>
69
+
70
+ <section>
71
+ <h2>Input Field with Barcode Image</h2>
72
+ <p>barcode-input 컴포넌트는 입력필드에 입력된 결과를 바코드 이미지로도 보여준다.</p>
73
+
74
+ <label>bacode type</label>
75
+ <select
76
+ @change=${e => {
77
+ this.bcid = e.target.value
78
+ }}
79
+ .value=${bcid}
80
+ >
81
+ <option value="code39">code39</option>
82
+ <option value="code128" selected>code128</option>
83
+ <option value="qrcode">QR</option>
84
+ </select>
85
+
86
+ <label>bacode width</label>
87
+ <select
88
+ @change=${e => {
89
+ this.bcWidth = Number(e.target.value)
90
+ }}
91
+ .value=${bcWidth}
92
+ >
93
+ <option value="10">10</option>
94
+ <option value="30" selected>30</option>
95
+ <option value="50">50</option>
96
+ </select>
97
+
98
+ <label>bacode height</label>
99
+ <select
100
+ @change=${e => {
101
+ this.bcHeight = Number(e.target.value)
102
+ }}
103
+ .value=${bcHeight}
104
+ >
105
+ <option value="10">10</option>
106
+ <option value="30" selected>30</option>
107
+ <option value="50">50</option>
108
+ </select>
109
+
110
+ <label>bacode scale</label>
111
+ <select
112
+ @change=${e => {
113
+ this.bcScale = Number(e.target.value)
114
+ }}
115
+ .value=${bcScale}
116
+ >
117
+ <option value="2">2</option>
118
+ <option value="3" selected>3</option>
119
+ <option value="4">4</option>
120
+ </select>
121
+
122
+ <label>padding</label>
123
+ <select
124
+ @change=${e => {
125
+ this.padding = Number(e.target.value)
126
+ }}
127
+ .value=${padding}
128
+ >
129
+ <option value="3">3</option>
130
+ <option value="5" selected>5</option>
131
+ <option value="10">10</option>
132
+ </select>
133
+
134
+ <barcode-input
135
+ .bcid=${bcid}
136
+ .value=${value}
137
+ .bcWidth=${bcWidth}
138
+ .bcHeight=${bcHeight}
139
+ .bcScale=${bcScale}
140
+ @change=${e => {
141
+ this.value = e.target.value
142
+ }}
143
+ ></barcode-input>
144
+ </section>
145
+
146
+ <section>
147
+ <h2>Barcode Image Component</h2>
148
+ <p>
149
+ barcode-tag 컴포넌트는 바코드 이미지를 보여준다. 바코드 이미지를 클릭하면 이미지 파일을 다운로드 받을 수 있다.
150
+ </p>
151
+
152
+ <barcode-tag
153
+ .bcid=${bcid}
154
+ .value=${value}
155
+ .bcWidth=${bcWidth}
156
+ .bcHeight=${bcHeight}
157
+ .bcScale=${bcScale}
158
+ .padding=${padding}
159
+ ></barcode-tag>
160
+ </section>
161
+ `
162
+ }
163
+
164
+ updated(changes) {
165
+ /*
166
+ * If this page properties are changed, this callback will be invoked.
167
+ * This callback will be called back only when this page is activated.
168
+ */
169
+ if (changes.has('itemId') || changes.has('params')) {
170
+ /* do something */
171
+ }
172
+ }
173
+
174
+ stateChanged(state) {
175
+ /*
176
+ * application wide state changed
177
+ *
178
+ */
179
+ }
180
+
181
+ pageUpdated(changes, lifecycle, before) {
182
+ if (this.active) {
183
+ } else {
184
+ /* this page is deactivated */
185
+ }
186
+ }
187
+ }
188
+
189
+ customElements.define('label-scan-page', LabelScanPage)
@@ -1,12 +1,13 @@
1
1
  import '@things-factory/form-ui'
2
2
  import '../components/camera-capturer'
3
3
 
4
- import { PageView, client, store } from '@things-factory/shell'
4
+ import { gql } from 'graphql-tag'
5
5
  import { css, html } from 'lit-element'
6
+ import { connect } from 'pwa-helpers/connect-mixin.js'
6
7
 
8
+ import { client, PageView, store } from '@things-factory/shell'
7
9
  import { CommonButtonStyles } from '@things-factory/styles'
8
- import { connect } from 'pwa-helpers/connect-mixin.js'
9
- import { gql } from 'graphql-tag'
10
+
10
11
  import { referencePageStyles } from './reference-page-styles'
11
12
 
12
13
  class OCRPage extends connect(store)(PageView) {
package/client/route.js CHANGED
@@ -55,6 +55,10 @@ export default function route(page) {
55
55
  import('./pages/direct-print')
56
56
  return page
57
57
 
58
+ case 'label-scan-page':
59
+ import('./pages/label-scan-page')
60
+ return page
61
+
58
62
  case 'ghost-print-page':
59
63
  import('./pages/ghost-print-page')
60
64
  return page
@@ -1,3 +1,7 @@
1
1
  module.exports = {
2
- subdomain: "system"
2
+ subdomain: 'system',
3
+ awsAppSync: {
4
+ apiUrl: '',
5
+ apiKey: ''
6
+ }
3
7
  }
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.interpretImages = void 0;
7
+ const node_fetch_1 = __importDefault(require("node-fetch"));
8
+ const env_1 = require("@things-factory/env");
9
+ const utils_1 = require("@things-factory/utils");
10
+ const appSyncConfig = env_1.config.get('awsAppSync');
11
+ async function interpretImages(images) {
12
+ const query = utils_1.gqlBuilder.jsonToGraphQLQuery({
13
+ mutation: {
14
+ upload: {
15
+ __args: {
16
+ images
17
+ },
18
+ success_count: true,
19
+ failure_count: true,
20
+ result: {
21
+ key_values: true,
22
+ boundaries: true,
23
+ texts: true
24
+ }
25
+ }
26
+ }
27
+ });
28
+ const body = JSON.stringify({ query });
29
+ const response = await (0, node_fetch_1.default)(appSyncConfig['apiUrl'], {
30
+ method: 'POST',
31
+ headers: {
32
+ 'Content-Type': 'application/graphql',
33
+ 'x-api-key': appSyncConfig['apiKey']
34
+ },
35
+ body
36
+ });
37
+ const jsonRes = await response.json();
38
+ return jsonRes;
39
+ }
40
+ exports.interpretImages = interpretImages;
41
+ // @deprecated
42
+ function getRequestBody(images) {
43
+ // @things-factory/utils gqlBuilder.buildArgs 와 유사
44
+ const imageArgs = images.map(obj => {
45
+ const args = [];
46
+ for (let key in obj) {
47
+ args.push(`${key}: \"${obj[key]}\"`);
48
+ }
49
+ return '{' + args.join(',') + '}';
50
+ });
51
+ // console.log(gqlBuilder.buildArgs(images))
52
+ // console.log(imageArgs)
53
+ const body = JSON.stringify({
54
+ query: `mutation {
55
+ upload(images: ${imageArgs}) {
56
+ success_count
57
+ failure_count
58
+ results {
59
+ key_values
60
+ boundaries
61
+ texts
62
+ }
63
+ }
64
+ }`
65
+ });
66
+ return body;
67
+ }
68
+ //# sourceMappingURL=lambda-call.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lambda-call.js","sourceRoot":"","sources":["../../../server/service/reference/lambda-call.ts"],"names":[],"mappings":";;;;;;AAAA,4DAA8B;AAE9B,6CAA4C;AAC5C,iDAAkD;AAElD,MAAM,aAAa,GAAG,YAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;AAEvC,KAAK,UAAU,eAAe,CAAC,MAAM;IAC1C,MAAM,KAAK,GAAG,kBAAU,CAAC,kBAAkB,CAAC;QAC1C,QAAQ,EAAE;YACR,MAAM,EAAE;gBACN,MAAM,EAAE;oBACN,MAAM;iBACP;gBACD,aAAa,EAAE,IAAI;gBACnB,aAAa,EAAE,IAAI;gBACnB,MAAM,EAAE;oBACN,UAAU,EAAE,IAAI;oBAChB,UAAU,EAAE,IAAI;oBAChB,KAAK,EAAE,IAAI;iBACZ;aACF;SACF;KACF,CAAC,CAAA;IAEF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;IACtC,MAAM,QAAQ,GAAG,MAAM,IAAA,oBAAK,EAAC,aAAa,CAAC,QAAQ,CAAC,EAAE;QACpD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,qBAAqB;YACrC,WAAW,EAAE,aAAa,CAAC,QAAQ,CAAC;SACrC;QACD,IAAI;KACL,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;IACrC,OAAO,OAAO,CAAA;AAChB,CAAC;AA9BD,0CA8BC;AAED,cAAc;AACd,SAAS,cAAc,CAAC,MAAM;IAC5B,kDAAkD;IAClD,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;QACjC,MAAM,IAAI,GAAG,EAAE,CAAA;QACf,KAAK,IAAI,GAAG,IAAI,GAAG,EAAE;YACnB,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;SACrC;QACD,OAAO,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;IACnC,CAAC,CAAC,CAAA;IAEF,4CAA4C;IAC5C,yBAAyB;IAEzB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,KAAK,EAAE;uBACY,SAAS;;;;;;;;;MAS1B;KACH,CAAC,CAAA;IAEF,OAAO,IAAI,CAAA;AACb,CAAC"}
@@ -13,13 +13,16 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.ReferenceQuery = void 0;
16
- const type_graphql_1 = require("type-graphql");
17
16
  const graphql_upload_1 = require("graphql-upload");
17
+ const type_graphql_1 = require("type-graphql");
18
+ const attachment_base_1 = require("@things-factory/attachment-base");
18
19
  const shell_1 = require("@things-factory/shell");
19
20
  const utils_1 = require("@things-factory/utils");
20
- const attachment_base_1 = require("@things-factory/attachment-base");
21
+ const lambda_call_1 = require("./lambda-call");
22
+ const debug = require('debug')('things-factory:reference-app');
21
23
  let ReferenceQuery = class ReferenceQuery {
22
24
  async ocrRequest(_, images, context) {
25
+ var _a, _b;
23
26
  const { domain, user, tx } = context.state;
24
27
  const all = Promise.all(images);
25
28
  if ((images === null || images === void 0 ? void 0 : images.length) > 0) {
@@ -31,8 +34,27 @@ let ReferenceQuery = class ReferenceQuery {
31
34
  };
32
35
  });
33
36
  await (0, attachment_base_1.createAttachments)(_, { attachments }, context);
37
+ const files = await Promise.all(images.map(async (file) => {
38
+ const { createReadStream, filename, mimetype } = await file;
39
+ const readStream = createReadStream();
40
+ const image = await this.streamToString(readStream);
41
+ // format is 'image/jpeg'
42
+ const formatArr = mimetype.split('/');
43
+ return { data: image, name: filename, format: formatArr[formatArr.length - 1] };
44
+ }));
45
+ const res = await (0, lambda_call_1.interpretImages)(files);
46
+ console.log('interpretImages response', JSON.stringify(res, null, ' '));
47
+ return (_b = (_a = res.data) === null || _a === void 0 ? void 0 : _a.upload) === null || _b === void 0 ? void 0 : _b.result;
34
48
  }
35
- return 'success';
49
+ return [];
50
+ }
51
+ streamToString(stream) {
52
+ const chunks = [];
53
+ return new Promise((resolve, reject) => {
54
+ stream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
55
+ stream.on('error', (err) => reject(err));
56
+ stream.on('end', () => resolve(Buffer.concat(chunks).toString('base64')));
57
+ });
36
58
  }
37
59
  async ocrPendingJob(_, tag, context) {
38
60
  const { domain } = context.state;
@@ -49,7 +71,7 @@ let ReferenceQuery = class ReferenceQuery {
49
71
  }
50
72
  };
51
73
  __decorate([
52
- (0, type_graphql_1.Query)(returns => String, { description: 'To reference of ocr request' }),
74
+ (0, type_graphql_1.Query)(returns => shell_1.ScalarAny, { description: 'To reference of ocr request' }),
53
75
  __param(0, (0, type_graphql_1.Root)()),
54
76
  __param(1, (0, type_graphql_1.Arg)('images', () => [graphql_upload_1.GraphQLUpload])),
55
77
  __param(2, (0, type_graphql_1.Ctx)()),
@@ -1 +1 @@
1
- {"version":3,"file":"reference-query.js","sourceRoot":"","sources":["../../../server/service/reference/reference-query.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,+CAAmF;AACnF,mDAA0D;AAC1D,iDAAuD;AACvD,iDAA6C;AAC7C,qEAA+E;AAG/E,IAAa,cAAc,GAA3B,MAAa,cAAc;IAEzB,KAAK,CAAC,UAAU,CACN,CAAC,EAET,MAAoB,EACb,OAAY;QAEnB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAE/B,IAAI,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,MAAM,IAAG,CAAC,EAAE;YACtB,MAAM,WAAW,GAAiB,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE;gBACxD,OAAO;oBACL,IAAI,EAAE,UAAU;oBAChB,KAAK,EAAE,GAAG;oBACV,QAAQ,EAAE,WAAW;iBACtB,CAAA;YACH,CAAC,CAAC,CAAA;YACF,MAAM,IAAA,mCAAiB,EAAC,CAAC,EAAE,EAAE,WAAW,EAAE,EAAE,OAAO,CAAC,CAAA;SACrD;QAED,OAAO,SAAS,CAAA;IAClB,CAAC;IAGD,KAAK,CAAC,aAAa,CAAS,CAAC,EAAc,GAAW,EAAS,OAAY;QACzE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,EAAE,EAAE;YAC7B,MAAM,IAAA,aAAK,EAAC,GAAG,CAAC,CAAA;YAEhB,IAAA,uBAAe,EAAC;gBACd,MAAM;gBACN,GAAG;gBACH,QAAQ,EAAE,CAAC;gBACX,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,SAAS;aAC5B,CAAC,CAAA;SACH;QAED,OAAO,SAAS,CAAA;IAClB,CAAC;CACF,CAAA;AAxCC;IADC,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;IAEtE,WAAA,IAAA,mBAAI,GAAE,CAAA;IACN,WAAA,IAAA,kBAAG,EAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,8BAAa,CAAC,CAAC,CAAA;IAEpC,WAAA,IAAA,kBAAG,GAAE,CAAA;;;;gDAiBP;AAGD;IADC,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;IAC7D,WAAA,IAAA,mBAAI,GAAE,CAAA;IAAK,WAAA,IAAA,kBAAG,EAAC,KAAK,CAAC,CAAA;IAAe,WAAA,IAAA,kBAAG,GAAE,CAAA;;;;mDAe7D;AAzCU,cAAc;IAD1B,IAAA,uBAAQ,GAAE;GACE,cAAc,CA0C1B;AA1CY,wCAAc"}
1
+ {"version":3,"file":"reference-query.js","sourceRoot":"","sources":["../../../server/service/reference/reference-query.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/reference-app",
3
- "version": "4.0.4",
3
+ "version": "4.0.5",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "client/index.js",
6
6
  "things-factory": true,
@@ -41,45 +41,46 @@
41
41
  "@material/mwc-checkbox": "^0.22.1",
42
42
  "@operato/data-grist": "^0.2.18",
43
43
  "@operato/ghost-print": "0.1.14",
44
- "@operato/mini-popup": "^0.2.3",
44
+ "@operato/mini-popup": "^0.2.5",
45
+ "@operato/ocr": "^0.2.21",
45
46
  "@operato/pull-to-refresh": "^0.2.4",
46
- "@things-factory/api": "^4.0.4",
47
- "@things-factory/apptool-ui": "^4.0.4",
48
- "@things-factory/auth-ui": "^4.0.4",
49
- "@things-factory/board-service": "^4.0.4",
50
- "@things-factory/board-ui": "^4.0.4",
51
- "@things-factory/context-ui": "^4.0.4",
52
- "@things-factory/dashboard": "^4.0.4",
53
- "@things-factory/export-ui": "^4.0.4",
54
- "@things-factory/export-ui-excel": "^4.0.4",
55
- "@things-factory/grist-ui": "^4.0.1",
56
- "@things-factory/help": "^4.0.4",
57
- "@things-factory/i18n-ui": "^4.0.4",
58
- "@things-factory/integration-ui": "^4.0.4",
59
- "@things-factory/lite-menu": "^4.0.4",
60
- "@things-factory/more-ui": "^4.0.4",
61
- "@things-factory/notification": "^4.0.4",
62
- "@things-factory/oauth2-client": "^4.0.4",
63
- "@things-factory/print-ui": "^4.0.4",
64
- "@things-factory/resource-ui": "^4.0.4",
65
- "@things-factory/scene-chartjs": "^4.0.4",
66
- "@things-factory/scene-clock": "^4.0.4",
67
- "@things-factory/scene-form": "^4.0.4",
68
- "@things-factory/scene-gauge": "^4.0.4",
69
- "@things-factory/scene-half-roundrect": "^4.0.4",
70
- "@things-factory/scene-indoor-map": "^4.0.4",
71
- "@things-factory/scene-news-ticker": "^4.0.4",
72
- "@things-factory/scene-progressbar": "^4.0.4",
73
- "@things-factory/scene-random": "^4.0.4",
74
- "@things-factory/scene-switch": "^4.0.4",
75
- "@things-factory/scene-tab": "^4.0.4",
76
- "@things-factory/setting-base": "^4.0.4",
77
- "@things-factory/setting-ui": "^4.0.4",
78
- "@things-factory/shell": "^4.0.4",
79
- "@things-factory/system-ui": "^4.0.4"
47
+ "@things-factory/api": "^4.0.5",
48
+ "@things-factory/apptool-ui": "^4.0.5",
49
+ "@things-factory/auth-ui": "^4.0.5",
50
+ "@things-factory/board-service": "^4.0.5",
51
+ "@things-factory/board-ui": "^4.0.5",
52
+ "@things-factory/context-ui": "^4.0.5",
53
+ "@things-factory/dashboard": "^4.0.5",
54
+ "@things-factory/export-ui": "^4.0.5",
55
+ "@things-factory/export-ui-excel": "^4.0.5",
56
+ "@things-factory/grist-ui": "^4.0.5",
57
+ "@things-factory/help": "^4.0.5",
58
+ "@things-factory/i18n-ui": "^4.0.5",
59
+ "@things-factory/integration-ui": "^4.0.5",
60
+ "@things-factory/lite-menu": "^4.0.5",
61
+ "@things-factory/more-ui": "^4.0.5",
62
+ "@things-factory/notification": "^4.0.5",
63
+ "@things-factory/oauth2-client": "^4.0.5",
64
+ "@things-factory/print-ui": "^4.0.5",
65
+ "@things-factory/resource-ui": "^4.0.5",
66
+ "@things-factory/scene-chartjs": "^4.0.5",
67
+ "@things-factory/scene-clock": "^4.0.5",
68
+ "@things-factory/scene-form": "^4.0.5",
69
+ "@things-factory/scene-gauge": "^4.0.5",
70
+ "@things-factory/scene-half-roundrect": "^4.0.5",
71
+ "@things-factory/scene-indoor-map": "^4.0.5",
72
+ "@things-factory/scene-news-ticker": "^4.0.5",
73
+ "@things-factory/scene-progressbar": "^4.0.5",
74
+ "@things-factory/scene-random": "^4.0.5",
75
+ "@things-factory/scene-switch": "^4.0.5",
76
+ "@things-factory/scene-tab": "^4.0.5",
77
+ "@things-factory/setting-base": "^4.0.5",
78
+ "@things-factory/setting-ui": "^4.0.5",
79
+ "@things-factory/shell": "^4.0.5",
80
+ "@things-factory/system-ui": "^4.0.5"
80
81
  },
81
82
  "devDependencies": {
82
- "@things-factory/builder": "^4.0.3"
83
+ "@things-factory/builder": "^4.0.5"
83
84
  },
84
- "gitHead": "292d7c4e2675d228d6be474ea6a0525d26ac1657"
85
+ "gitHead": "b7b2976818dceab74a34903499d408eed5d45b04"
85
86
  }
@@ -0,0 +1,69 @@
1
+ import fetch from 'node-fetch'
2
+
3
+ import { config } from '@things-factory/env'
4
+ import { gqlBuilder } from '@things-factory/utils'
5
+
6
+ const appSyncConfig = config.get('awsAppSync')
7
+
8
+ export async function interpretImages(images) {
9
+ const query = gqlBuilder.jsonToGraphQLQuery({
10
+ mutation: {
11
+ upload: {
12
+ __args: {
13
+ images
14
+ },
15
+ success_count: true,
16
+ failure_count: true,
17
+ result: {
18
+ key_values: true,
19
+ boundaries: true,
20
+ texts: true
21
+ }
22
+ }
23
+ }
24
+ })
25
+
26
+ const body = JSON.stringify({ query })
27
+ const response = await fetch(appSyncConfig['apiUrl'], {
28
+ method: 'POST',
29
+ headers: {
30
+ 'Content-Type': 'application/graphql',
31
+ 'x-api-key': appSyncConfig['apiKey']
32
+ },
33
+ body
34
+ })
35
+
36
+ const jsonRes = await response.json()
37
+ return jsonRes
38
+ }
39
+
40
+ // @deprecated
41
+ function getRequestBody(images) {
42
+ // @things-factory/utils gqlBuilder.buildArgs 와 유사
43
+ const imageArgs = images.map(obj => {
44
+ const args = []
45
+ for (let key in obj) {
46
+ args.push(`${key}: \"${obj[key]}\"`)
47
+ }
48
+ return '{' + args.join(',') + '}'
49
+ })
50
+
51
+ // console.log(gqlBuilder.buildArgs(images))
52
+ // console.log(imageArgs)
53
+
54
+ const body = JSON.stringify({
55
+ query: `mutation {
56
+ upload(images: ${imageArgs}) {
57
+ success_count
58
+ failure_count
59
+ results {
60
+ key_values
61
+ boundaries
62
+ texts
63
+ }
64
+ }
65
+ }`
66
+ })
67
+
68
+ return body
69
+ }
@@ -1,18 +1,23 @@
1
- import { Resolver, Mutation, Query, Root, Arg, Ctx, Directive } from 'type-graphql'
2
- import { GraphQLUpload, FileUpload } from 'graphql-upload'
3
- import { publishProgress } from '@things-factory/shell'
4
- import { sleep } from '@things-factory/utils'
1
+ import { FileUpload, GraphQLUpload } from 'graphql-upload'
2
+ import { Arg, Ctx, Query, Resolver, Root } from 'type-graphql'
3
+
5
4
  import { Attachment, createAttachments } from '@things-factory/attachment-base'
5
+ import { publishProgress, ScalarAny } from '@things-factory/shell'
6
+ import { sleep } from '@things-factory/utils'
7
+
8
+ import { interpretImages } from './lambda-call'
9
+
10
+ const debug = require('debug')('things-factory:reference-app')
6
11
 
7
12
  @Resolver()
8
13
  export class ReferenceQuery {
9
- @Query(returns => String, { description: 'To reference of ocr request' })
14
+ @Query(returns => ScalarAny, { description: 'To reference of ocr request' })
10
15
  async ocrRequest(
11
16
  @Root() _,
12
17
  @Arg('images', () => [GraphQLUpload])
13
18
  images: FileUpload[],
14
19
  @Ctx() context: any
15
- ): Promise<string> {
20
+ ): Promise<ScalarAny> {
16
21
  const { domain, user, tx } = context.state
17
22
  const all = Promise.all(images)
18
23
 
@@ -25,9 +30,32 @@ export class ReferenceQuery {
25
30
  }
26
31
  })
27
32
  await createAttachments(_, { attachments }, context)
33
+
34
+ const files = await Promise.all(
35
+ images.map(async file => {
36
+ const { createReadStream, filename, mimetype } = await file
37
+ const readStream = createReadStream()
38
+ const image = await this.streamToString(readStream)
39
+ // format is 'image/jpeg'
40
+ const formatArr = mimetype.split('/')
41
+ return { data: image, name: filename, format: formatArr[formatArr.length - 1] }
42
+ })
43
+ )
44
+ const res = await interpretImages(files)
45
+ console.log('interpretImages response', JSON.stringify(res, null, ' '))
46
+ return res.data?.upload?.result
28
47
  }
29
48
 
30
- return 'success'
49
+ return []
50
+ }
51
+
52
+ streamToString (stream) {
53
+ const chunks = [];
54
+ return new Promise((resolve, reject) => {
55
+ stream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
56
+ stream.on('error', (err) => reject(err));
57
+ stream.on('end', () => resolve(Buffer.concat(chunks).toString('base64')));
58
+ })
31
59
  }
32
60
 
33
61
  @Query(returns => String, { description: 'To reference of pending job progress' })
@@ -56,6 +56,10 @@ export default {
56
56
  tagname: 'direct-print',
57
57
  page: 'direct-print'
58
58
  },
59
+ {
60
+ tagname: 'label-scan-page',
61
+ page: 'label-scan-page'
62
+ },
59
63
  {
60
64
  tagname: 'ghost-print-page',
61
65
  page: 'ghost-print-page'