Showing
71 changed files
with
5132 additions
and
0 deletions
final/.gitignore
0 → 100644
1 | +node_modules/ |
final/LICENSE
0 → 100644
1 | +MIT License | ||
2 | + | ||
3 | +Copyright (c) 2016 Jerry Wang (https://jw84.co) | ||
4 | + | ||
5 | +Permission is hereby granted, free of charge, to any person obtaining a copy | ||
6 | +of this software and associated documentation files (the "Software"), to deal | ||
7 | +in the Software without restriction, including without limitation the rights | ||
8 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
9 | +copies of the Software, and to permit persons to whom the Software is | ||
10 | +furnished to do so, subject to the following conditions: | ||
11 | + | ||
12 | +The above copyright notice and this permission notice shall be included in all | ||
13 | +copies or substantial portions of the Software. | ||
14 | + | ||
15 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
18 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
20 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
21 | +SOFTWARE. |
final/Procfile
0 → 100644
1 | +web: node index.js |
final/Seq2Seq.py
0 → 100644
1 | +import tensorflow as tf | ||
2 | +import numpy as np | ||
3 | +import sys | ||
4 | +from random import randint | ||
5 | +import datetime | ||
6 | +from sklearn.utils import shuffle | ||
7 | +import pickle | ||
8 | +import os | ||
9 | +# Removes an annoying Tensorflow warning | ||
10 | +import sys | ||
11 | +sys.stdout.reconfigure(encoding='utf-8') | ||
12 | +os.environ['TF_CPP_MIN_LOG_LEVEL']='2' | ||
13 | + | ||
14 | + | ||
15 | + | ||
16 | +def createTrainingMatrices(conversationFileName, wList, maxLen): | ||
17 | + conversationDictionary = np.load(conversationFileName, allow_pickle=True).item() | ||
18 | + numExamples = len(conversationDictionary) | ||
19 | + xTrain = np.zeros((numExamples, maxLen), dtype='int32') | ||
20 | + yTrain = np.zeros((numExamples, maxLen), dtype='int32') | ||
21 | + for index,(key,value) in enumerate(conversationDictionary.items()): | ||
22 | + # Will store integerized representation of strings here (initialized as padding) | ||
23 | + encoderMessage = np.full((maxLen), wList.index('<pad>'), dtype='int32') | ||
24 | + decoderMessage = np.full((maxLen), wList.index('<pad>'), dtype='int32') | ||
25 | + # Getting all the individual words in the strings | ||
26 | + keySplit = key.split() | ||
27 | + valueSplit = value.split() | ||
28 | + keyCount = len(keySplit) | ||
29 | + valueCount = len(valueSplit) | ||
30 | + # Throw out sequences that are too long or are empty | ||
31 | + if (keyCount > (maxLen - 1) or valueCount > (maxLen - 1) or valueCount == 0 or keyCount == 0): | ||
32 | + continue | ||
33 | + # Integerize the encoder string | ||
34 | + for keyIndex, word in enumerate(keySplit): | ||
35 | + try: | ||
36 | + encoderMessage[keyIndex] = wList.index(word) | ||
37 | + except ValueError: | ||
38 | + # TODO: This isnt really the right way to handle this scenario | ||
39 | + encoderMessage[keyIndex] = 0 | ||
40 | + encoderMessage[keyIndex + 1] = wList.index('<EOS>') | ||
41 | + # Integerize the decoder stringd | ||
42 | + for valueIndex, word in enumerate(valueSplit): | ||
43 | + try: | ||
44 | + decoderMessage[valueIndex] = wList.index(word) | ||
45 | + except ValueError: | ||
46 | + decoderMessage[valueIndex] = 0 | ||
47 | + decoderMessage[valueIndex + 1] = wList.index('<EOS>') | ||
48 | + xTrain[index] = encoderMessage | ||
49 | + yTrain[index] = decoderMessage | ||
50 | + # Remove rows with all zeros | ||
51 | + yTrain = yTrain[~np.all(yTrain == 0, axis=1)] | ||
52 | + xTrain = xTrain[~np.all(xTrain == 0, axis=1)] | ||
53 | + numExamples = xTrain.shape[0] | ||
54 | + return numExamples, xTrain, yTrain | ||
55 | + | ||
56 | +def getTrainingBatch(localXTrain, localYTrain, localBatchSize, maxLen): | ||
57 | + num = randint(0,numTrainingExamples - localBatchSize - 1) | ||
58 | + arr = localXTrain[num:num + localBatchSize] | ||
59 | + labels = localYTrain[num:num + localBatchSize] | ||
60 | + # Reversing the order of encoder string apparently helps as per 2014 paper | ||
61 | + reversedList = list(arr) | ||
62 | + for index,example in enumerate(reversedList): | ||
63 | + reversedList[index] = list(reversed(example)) | ||
64 | + | ||
65 | + # Lagged labels are for the training input into the decoder | ||
66 | + laggedLabels = [] | ||
67 | + EOStokenIndex = wordList.index('<EOS>') | ||
68 | + padTokenIndex = wordList.index('<pad>') | ||
69 | + for example in labels: | ||
70 | + eosFound = np.argwhere(example==EOStokenIndex)[0] | ||
71 | + shiftedExample = np.roll(example,1) | ||
72 | + shiftedExample[0] = EOStokenIndex | ||
73 | + # The EOS token was already at the end, so no need for pad | ||
74 | + if (eosFound != (maxLen - 1)): | ||
75 | + shiftedExample[eosFound+1] = padTokenIndex | ||
76 | + laggedLabels.append(shiftedExample) | ||
77 | + | ||
78 | + # Need to transpose these | ||
79 | + reversedList = np.asarray(reversedList).T.tolist() | ||
80 | + labels = labels.T.tolist() | ||
81 | + laggedLabels = np.asarray(laggedLabels).T.tolist() | ||
82 | + return reversedList, labels, laggedLabels | ||
83 | + | ||
84 | +def translateToSentences(inputs, wList, encoder=False): | ||
85 | + EOStokenIndex = wList.index('<EOS>') | ||
86 | + padTokenIndex = wList.index('<pad>') | ||
87 | + numStrings = len(inputs[0]) | ||
88 | + numLengthOfStrings = len(inputs) | ||
89 | + listOfStrings = [''] * numStrings | ||
90 | + for mySet in inputs: | ||
91 | + for index,num in enumerate(mySet): | ||
92 | + if (num != EOStokenIndex and num != padTokenIndex): | ||
93 | + if (encoder): | ||
94 | + # Encodings are in reverse! | ||
95 | + listOfStrings[index] = wList[num] + " " + listOfStrings[index] | ||
96 | + else: | ||
97 | + listOfStrings[index] = listOfStrings[index] + " " + wList[num] | ||
98 | + listOfStrings = [string.strip() for string in listOfStrings] | ||
99 | + return listOfStrings | ||
100 | + | ||
101 | +def getTestInput(inputMessage, wList, maxLen): | ||
102 | + encoderMessage = np.full((maxLen), wList.index('<pad>'), dtype='int32') | ||
103 | + inputSplit = inputMessage.lower().split() | ||
104 | + for index,word in enumerate(inputSplit): | ||
105 | + try: | ||
106 | + encoderMessage[index] = wList.index(word) | ||
107 | + except ValueError: | ||
108 | + continue | ||
109 | + encoderMessage[index + 1] = wList.index('<EOS>') | ||
110 | + encoderMessage = encoderMessage[::-1] | ||
111 | + encoderMessageList=[] | ||
112 | + for num in encoderMessage: | ||
113 | + encoderMessageList.append([num]) | ||
114 | + return encoderMessageList | ||
115 | + | ||
116 | +def idsToSentence(ids, wList): | ||
117 | + EOStokenIndex = wList.index('<EOS>') | ||
118 | + padTokenIndex = wList.index('<pad>') | ||
119 | + myStr = "" | ||
120 | + listOfResponses=[] | ||
121 | + for num in ids: | ||
122 | + if (num[0] == EOStokenIndex or num[0] == padTokenIndex): | ||
123 | + listOfResponses.append(myStr) | ||
124 | + myStr = "" | ||
125 | + else: | ||
126 | + myStr = myStr + wList[num[0]] + " " | ||
127 | + if myStr: | ||
128 | + listOfResponses.append(myStr) | ||
129 | + listOfResponses = [i for i in listOfResponses if i] | ||
130 | + return listOfResponses | ||
131 | + | ||
132 | +# Hyperparamters | ||
133 | +batchSize = 24 | ||
134 | +maxEncoderLength = 15 | ||
135 | +maxDecoderLength = maxEncoderLength | ||
136 | +lstmUnits = 112 | ||
137 | +embeddingDim = lstmUnits | ||
138 | +numLayersLSTM = 3 | ||
139 | +numIterations = 500000 | ||
140 | + | ||
141 | +# Loading in all the data structures | ||
142 | +with open("wordList.txt", "rb") as fp: | ||
143 | + wordList = pickle.load(fp) | ||
144 | + | ||
145 | +vocabSize = len(wordList) | ||
146 | + | ||
147 | +# If you've run the entirety of word2vec.py then these lines will load in | ||
148 | +# the embedding matrix. | ||
149 | +if (os.path.isfile('embeddingMatrix.npy')): | ||
150 | + wordVectors = np.load('embeddingMatrix.npy') | ||
151 | + wordVecDimensions = wordVectors.shape[1] | ||
152 | +else: | ||
153 | + question = 'Since we cant find an embedding matrix, how many dimensions do you want your word vectors to be?: ' | ||
154 | + wordVecDimensions = 2 | ||
155 | + | ||
156 | +# Add two entries to the word vector matrix. One to represent padding tokens, | ||
157 | +# and one to represent an end of sentence token | ||
158 | +padVector = np.zeros((1, wordVecDimensions), dtype='int32') | ||
159 | +EOSVector = np.ones((1, wordVecDimensions), dtype='int32') | ||
160 | +if (os.path.isfile('embeddingMatrix.npy')): | ||
161 | + wordVectors = np.concatenate((wordVectors,padVector), axis=0) | ||
162 | + wordVectors = np.concatenate((wordVectors,EOSVector), axis=0) | ||
163 | + | ||
164 | +# Need to modify the word list as well | ||
165 | +wordList.append('<pad>') | ||
166 | +wordList.append('<EOS>') | ||
167 | +vocabSize = vocabSize + 2 | ||
168 | + | ||
169 | +if (os.path.isfile('Seq2SeqXTrain.npy') and os.path.isfile('Seq2SeqYTrain.npy')): | ||
170 | + xTrain = np.load('Seq2SeqXTrain.npy') | ||
171 | + yTrain = np.load('Seq2SeqYTrain.npy') | ||
172 | + print ('Finished loading training matrices') | ||
173 | + numTrainingExamples = xTrain.shape[0] | ||
174 | +else: | ||
175 | + numTrainingExamples, xTrain, yTrain = createTrainingMatrices(r'C:\Users\dlgpd\Desktop\20-1\oss\term-project\Learn_for_yourself\final\conversationDictionary.npy', wordList, maxEncoderLength) | ||
176 | + np.save('Seq2SeqXTrain.npy', xTrain) | ||
177 | + np.save('Seq2SeqYTrain.npy', yTrain) | ||
178 | + print ('Finished creating training matrices') | ||
179 | + | ||
180 | +tf.compat.v1.reset_default_graph() | ||
181 | + | ||
182 | +# Create the placeholders | ||
183 | +encoderInputs = [tf.placeholder(tf.int32, shape=(None,)) for i in range(maxEncoderLength)] | ||
184 | +decoderLabels = [tf.placeholder(tf.int32, shape=(None,)) for i in range(maxDecoderLength)] | ||
185 | +decoderInputs = [tf.placeholder(tf.int32, shape=(None,)) for i in range(maxDecoderLength)] | ||
186 | +feedPrevious = tf.placeholder(tf.bool) | ||
187 | + | ||
188 | +encoderLSTM = tf.nn.rnn_cell.BasicLSTMCell(lstmUnits, state_is_tuple=True) | ||
189 | + | ||
190 | +#encoderLSTM = tf.nn.rnn_cell.MultiRNNCell([singleCell]*numLayersLSTM, state_is_tuple=True) | ||
191 | +# Architectural choice of of whether or not to include ^ | ||
192 | + | ||
193 | +decoderOutputs, decoderFinalState = tf.contrib.legacy_seq2seq.embedding_rnn_seq2seq(encoderInputs, decoderInputs, encoderLSTM, | ||
194 | + vocabSize, vocabSize, embeddingDim, feed_previous=feedPrevious) | ||
195 | + | ||
196 | +decoderPrediction = tf.argmax(decoderOutputs, 2) | ||
197 | + | ||
198 | +lossWeights = [tf.ones_like(l, dtype=tf.float32) for l in decoderLabels] | ||
199 | +loss = tf.contrib.legacy_seq2seq.sequence_loss(decoderOutputs, decoderLabels, lossWeights, vocabSize) | ||
200 | +optimizer = tf.train.AdamOptimizer(1e-4).minimize(loss) | ||
201 | + | ||
202 | +sess = tf.Session() | ||
203 | +saver = tf.train.Saver() | ||
204 | +# If you're loading in a saved model, uncomment the following line and comment out line 202 | ||
205 | +#saver.restore(sess, tf.train.latest_checkpoint('models/')) | ||
206 | +sess.run(tf.global_variables_initializer()) | ||
207 | + | ||
208 | +# Uploading results to Tensorboard | ||
209 | +tf.summary.scalar('Loss', loss) | ||
210 | +merged = tf.summary.merge_all() | ||
211 | +logdir = "tensorboard/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S") + "/" | ||
212 | +writer = tf.summary.FileWriter(logdir, sess.graph) | ||
213 | + | ||
214 | +# Some test strings that we'll use as input at intervals during training | ||
215 | +encoderTestStrings = ["밥먹었어?", | ||
216 | + "이모티콘", | ||
217 | + "잘쟈", | ||
218 | + "어머나" | ||
219 | + ] | ||
220 | + | ||
221 | +zeroVector = np.zeros((1), dtype='int32') | ||
222 | + | ||
223 | +for i in range(numIterations): | ||
224 | + | ||
225 | + encoderTrain, decoderTargetTrain, decoderInputTrain = getTrainingBatch(xTrain, yTrain, batchSize, maxEncoderLength) | ||
226 | + feedDict = {encoderInputs[t]: encoderTrain[t] for t in range(maxEncoderLength)} | ||
227 | + feedDict.update({decoderLabels[t]: decoderTargetTrain[t] for t in range(maxDecoderLength)}) | ||
228 | + feedDict.update({decoderInputs[t]: decoderInputTrain[t] for t in range(maxDecoderLength)}) | ||
229 | + feedDict.update({feedPrevious: False}) | ||
230 | + | ||
231 | + curLoss, _, pred = sess.run([loss, optimizer, decoderPrediction], feed_dict=feedDict) | ||
232 | + | ||
233 | + if (i % 50 == 0): | ||
234 | + print('Current loss:', curLoss, 'at iteration', i) | ||
235 | + summary = sess.run(merged, feed_dict=feedDict) | ||
236 | + writer.add_summary(summary, i) | ||
237 | + if (i % 25 == 0 and i != 0): | ||
238 | + num = randint(0,len(encoderTestStrings) - 1) | ||
239 | + print (encoderTestStrings[num]) | ||
240 | + inputVector = getTestInput(encoderTestStrings[num], wordList, maxEncoderLength); | ||
241 | + feedDict = {encoderInputs[t]: inputVector[t] for t in range(maxEncoderLength)} | ||
242 | + feedDict.update({decoderLabels[t]: zeroVector for t in range(maxDecoderLength)}) | ||
243 | + feedDict.update({decoderInputs[t]: zeroVector for t in range(maxDecoderLength)}) | ||
244 | + feedDict.update({feedPrevious: True}) | ||
245 | + ids = (sess.run(decoderPrediction, feed_dict=feedDict)) | ||
246 | + print (idsToSentence(ids, wordList)) | ||
247 | + | ||
248 | + if (i % 100 == 0 and i != 0): | ||
249 | + savePath = saver.save(sess, "models/pretrained_seq2seq.ckpt", global_step=i) |
final/Word2Vec.py
0 → 100644
1 | +import tensorflow as tf | ||
2 | +import numpy as np | ||
3 | +import re | ||
4 | +from collections import Counter | ||
5 | +import sys | ||
6 | +import math | ||
7 | +from random import randint | ||
8 | +import pickle | ||
9 | +import os | ||
10 | +import sys | ||
11 | +sys.stdout.reconfigure(encoding='utf-8') | ||
12 | + | ||
13 | +# This Word2Vec implementation is largely based on this paper | ||
14 | +# https://papers.nips.cc/paper/5021-distributed-representations-of-words-and-phrases-and-their-compositionality.pdf | ||
15 | +# It's a bit old, but Word2Vec is still SOTA and relatively simple, so I'm going with it | ||
16 | + | ||
17 | +# Check out Tensorflow's documentation which is pretty good for Word2Vec | ||
18 | +# https://www.tensorflow.org/tutorials/word2vec | ||
19 | + | ||
20 | +wordVecDimensions = 100 | ||
21 | +batchSize = 128 | ||
22 | +numNegativeSample = 64 | ||
23 | +windowSize = 5 | ||
24 | +numIterations = 100000 | ||
25 | + | ||
26 | +# This function just takes in the conversation data and makes it | ||
27 | +# into one huge string, and then uses a Counter to identify words | ||
28 | +# and the number of occurences | ||
29 | +def processDataset(filename): | ||
30 | + openedFile = open(filename, 'r', encoding='UTF8') | ||
31 | + allLines = openedFile.readlines() | ||
32 | + myStr = "" | ||
33 | + for line in allLines: | ||
34 | + myStr += line | ||
35 | + finalDict = Counter(myStr.split()) | ||
36 | + return myStr, finalDict | ||
37 | + | ||
38 | +def createTrainingMatrices(dictionary, corpus): | ||
39 | + allUniqueWords = list(dictionary.keys()) | ||
40 | + allWords = corpus.split() | ||
41 | + numTotalWords = len(allWords) | ||
42 | + xTrain=[] | ||
43 | + yTrain=[] | ||
44 | + for i in range(numTotalWords): | ||
45 | + if i % 100000 == 0: | ||
46 | + print ('Finished %d/%d total words' % (i, numTotalWords)) | ||
47 | + wordsAfter = allWords[i + 1:i + windowSize + 1] | ||
48 | + wordsBefore = allWords[max(0, i - windowSize):i] | ||
49 | + wordsAdded = wordsAfter + wordsBefore | ||
50 | + for word in wordsAdded: | ||
51 | + xTrain.append(allUniqueWords.index(allWords[i])) | ||
52 | + yTrain.append(allUniqueWords.index(word)) | ||
53 | + return xTrain, yTrain | ||
54 | + | ||
55 | +def getTrainingBatch(): | ||
56 | + num = randint(0,numTrainingExamples - batchSize - 1) | ||
57 | + arr = xTrain[num:num + batchSize] | ||
58 | + labels = yTrain[num:num + batchSize] | ||
59 | + return arr, labels[:,np.newaxis] | ||
60 | + | ||
61 | +continueWord2Vec = True | ||
62 | +# Loading the data structures if they are present in the directory | ||
63 | +if (os.path.isfile('Word2VecXTrain.npy') and os.path.isfile('Word2VecYTrain.npy') and os.path.isfile('wordList.txt')): | ||
64 | + xTrain = np.load('Word2VecXTrain.npy') | ||
65 | + yTrain = np.load('Word2VecYTrain.npy') | ||
66 | + print ('Finished loading training matrices') | ||
67 | + with open("wordList.txt", "rb") as fp: | ||
68 | + wordList = pickle.load(fp) | ||
69 | + print ('Finished loading word list') | ||
70 | + | ||
71 | +else: | ||
72 | + fullCorpus, datasetDictionary = processDataset(r'C:\Users\dlgpd\Desktop\20-1\oss\term-project\Learn_for_yourself\final\conversationData.txt') | ||
73 | + print ('Finished parsing and cleaning dataset') | ||
74 | + wordList = list(datasetDictionary.keys()) | ||
75 | + createOwnVectors = 'y' | ||
76 | + if (createOwnVectors == 'y'): | ||
77 | + xTrain, yTrain = createTrainingMatrices(datasetDictionary, fullCorpus) | ||
78 | + print ('Finished creating training matrices') | ||
79 | + np.save(r'C:\Users\dlgpd\Desktop\20-1\oss\term-project\Learn_for_yourself\final\Word2VecXTrain.npy', xTrain) | ||
80 | + np.save(r'C:\Users\dlgpd\Desktop\20-1\oss\term-project\Learn_for_yourself\final\Word2VecYTrain.npy', yTrain) | ||
81 | + else: | ||
82 | + continueWord2Vec = False | ||
83 | + with open(r"C:\Users\dlgpd\Desktop\20-1\oss\term-project\Learn_for_yourself\final\wordList.txt", "wb") as fp: | ||
84 | + pickle.dump(wordList, fp) |
final/app.js
0 → 100644
1 | +// call all the required packages | ||
2 | +var express = require('express') | ||
3 | +var bodyParser= require('body-parser') | ||
4 | +var multer = require('multer'); | ||
5 | +let { PythonShell } = require ( 'python-shell' ) | ||
6 | + | ||
7 | +//CREATE EXPRESS APP | ||
8 | +var app = express(); | ||
9 | + | ||
10 | +app.use(bodyParser.urlencoded({extended: true})) | ||
11 | +app.use('/users', express.static('uploads')) | ||
12 | + | ||
13 | +var options = { | ||
14 | + mode: 'text', | ||
15 | + pythonPath: '', | ||
16 | + pythonOptions: ['-u'], | ||
17 | + scriptPath: '', | ||
18 | + // args: ['value1', 'value2', 'value3'], | ||
19 | + encoding: 'utf8' | ||
20 | + }; | ||
21 | + | ||
22 | + | ||
23 | +//ROUTES WILL GO HERE | ||
24 | +app.get('/',function(req,res){ | ||
25 | + res.sendFile(__dirname + '/index.html'); | ||
26 | + }); | ||
27 | + | ||
28 | +var storage = multer.diskStorage({ | ||
29 | + destination: function (req, file, cb) { | ||
30 | + cb(null, 'uploads') | ||
31 | + }, | ||
32 | + filename: function (req, file, cb) { | ||
33 | + cb(null, "dataset.txt") | ||
34 | + } | ||
35 | + }) | ||
36 | + | ||
37 | +var upload = multer({ storage: storage }) | ||
38 | + | ||
39 | +app.post('/uploadfile', upload.single('myFile'), (req, res, next) => { | ||
40 | +const file = req.file | ||
41 | + if (!file) { | ||
42 | + const error = new Error('Please upload a file') | ||
43 | + error.httpStatusCode = 400 | ||
44 | + return next(error) | ||
45 | + } | ||
46 | + res.send(file) | ||
47 | + }) | ||
48 | + | ||
49 | +var upload = multer({ storage: storage }) | ||
50 | + | ||
51 | +app.get('/makebot', (req, res) => { | ||
52 | + res.sendFile(__dirname + '/index.html'); | ||
53 | + | ||
54 | + PythonShell.run('createDataset.py', options, (err, results) => { | ||
55 | + if (err) throw err; | ||
56 | + console.log('results: %j', results); | ||
57 | + }); | ||
58 | + res.send("create Data set") | ||
59 | + | ||
60 | + PythonShell.run('Word2Vec.py', options, (err, results) => { | ||
61 | + if (err) throw err; | ||
62 | + console.log('results: %j', results); | ||
63 | + }); | ||
64 | + res.send("word to vector") | ||
65 | + | ||
66 | + PythonShell.run('Seq2Seq.py', options, (err, results) => { | ||
67 | + if (err) throw err; | ||
68 | + console.log('results: %j', results); | ||
69 | + res.send("seq 2 seq") | ||
70 | + | ||
71 | + | ||
72 | + | ||
73 | + | ||
74 | + }); | ||
75 | + }) | ||
76 | + | ||
77 | + | ||
78 | +app.listen(3000, () => console.log('Server started on port 3000')); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
final/createDataset.py
0 → 100644
1 | +import pandas as pd | ||
2 | +import numpy as np | ||
3 | +import os | ||
4 | +import re | ||
5 | +from datetime import datetime | ||
6 | +import sys | ||
7 | +sys.stdout.reconfigure(encoding='utf-8') | ||
8 | + | ||
9 | +currentPath = os.getcwd() | ||
10 | + | ||
11 | + | ||
12 | +def getFacebookData(): | ||
13 | + print("function active") | ||
14 | + personName = "이혜연" | ||
15 | + personName = personName.rstrip('\r') | ||
16 | + responseDictionary = dict() | ||
17 | + with open(r'C:\Users\dlgpd\Desktop\20-1\oss\term-project\Learn_for_yourself\final\uploads\dataset.txt', 'r', encoding='UTF8') as fbFile: | ||
18 | + allLines = fbFile.readlines() | ||
19 | + | ||
20 | + myMessage, otherPersonsMessage, currentSpeaker = "","","" | ||
21 | + | ||
22 | + for index,lines in enumerate(allLines): | ||
23 | + rightBracket = lines.find(':') + 5 | ||
24 | + justMessage = lines[rightBracket:] | ||
25 | + colon = justMessage.find(':') | ||
26 | + # Find messages that I sent | ||
27 | + | ||
28 | + | ||
29 | + if (justMessage[:colon-1] == personName): | ||
30 | + if not myMessage: | ||
31 | + # Want to find the first message that I send (if I send multiple | ||
32 | + # in a row) | ||
33 | + startMessageIndex = index - 1 | ||
34 | + myMessage += justMessage[colon + 2:] | ||
35 | + | ||
36 | + elif myMessage: | ||
37 | + # Now go and see what message the other person sent by looking at | ||
38 | + # previous messages | ||
39 | + for counter in range(startMessageIndex, 0, -1): | ||
40 | + currentLine = allLines[counter] | ||
41 | + rightBracket = currentLine.find(':') + 5 | ||
42 | + justMessage = currentLine[rightBracket:] | ||
43 | + colon = justMessage.find(':') | ||
44 | + if not currentSpeaker: | ||
45 | + # The first speaker not named me | ||
46 | + currentSpeaker = justMessage[:colon] | ||
47 | + elif (currentSpeaker != justMessage[:colon] and otherPersonsMessage): | ||
48 | + # A different person started speaking, so now I know that the | ||
49 | + # first person's message is done | ||
50 | + otherPersonsMessage = cleanMessage(otherPersonsMessage) | ||
51 | + myMessage = cleanMessage(myMessage) | ||
52 | + responseDictionary[otherPersonsMessage] = myMessage | ||
53 | + break | ||
54 | + otherPersonsMessage = justMessage[colon + 2:] + otherPersonsMessage | ||
55 | + myMessage, otherPersonsMessage, currentSpeaker = "","","" | ||
56 | + | ||
57 | + return responseDictionary | ||
58 | + | ||
59 | + | ||
60 | + | ||
61 | +def cleanMessage(message): | ||
62 | + # Remove new lines within message | ||
63 | + cleanedMessage = message.replace('\n',' ').lower() | ||
64 | + # Deal with some weird tokens | ||
65 | + cleanedMessage = cleanedMessage.replace("\xc2\xa0", "") | ||
66 | + # Remove punctuation | ||
67 | + cleanedMessage = re.sub('([.,!?])','', cleanedMessage) | ||
68 | + # Remove multiple spaces in message | ||
69 | + cleanedMessage = re.sub(' +',' ', cleanedMessage) | ||
70 | + return cleanedMessage | ||
71 | + | ||
72 | +combinedDictionary = {} | ||
73 | + | ||
74 | +combinedDictionary.update(getFacebookData()) | ||
75 | + | ||
76 | +print ('Total len of dictionary', len(combinedDictionary)) | ||
77 | + | ||
78 | +print('Saving conversation data dictionary') | ||
79 | +np.save(r'C:\Users\dlgpd\Desktop\20-1\oss\term-project\Learn_for_yourself\final\conversationDictionary.npy', combinedDictionary) | ||
80 | + | ||
81 | +conversationFile = open(r'C:\Users\dlgpd\Desktop\20-1\oss\term-project\Learn_for_yourself\final\conversationData.txt', 'w', encoding='UTF8') | ||
82 | +for key, value in combinedDictionary.items(): | ||
83 | + if (not key.strip() or not value.strip()): | ||
84 | + # If there are empty strings | ||
85 | + continue | ||
86 | + conversationFile.write(key.strip() + value.strip()) |
final/flask/server.py
0 → 100644
1 | +import numpy as np | ||
2 | +import pickle | ||
3 | +import tensorflow as tf | ||
4 | +from flask import Flask, jsonify, render_template, request | ||
5 | +import model | ||
6 | + | ||
7 | +# Load in data structures | ||
8 | +with open("data/wordList.txt", "rb") as fp: | ||
9 | + wordList = pickle.load(fp) | ||
10 | +wordList.append('<pad>') | ||
11 | +wordList.append('<EOS>') | ||
12 | + | ||
13 | +# Load in hyperparamters | ||
14 | +vocabSize = len(wordList) | ||
15 | +batchSize = 24 | ||
16 | +maxEncoderLength = 15 | ||
17 | +maxDecoderLength = 15 | ||
18 | +lstmUnits = 112 | ||
19 | +numLayersLSTM = 3 | ||
20 | + | ||
21 | +# Create placeholders | ||
22 | +encoderInputs = [tf.placeholder(tf.int32, shape=(None,)) for i in range(maxEncoderLength)] | ||
23 | +decoderLabels = [tf.placeholder(tf.int32, shape=(None,)) for i in range(maxDecoderLength)] | ||
24 | +decoderInputs = [tf.placeholder(tf.int32, shape=(None,)) for i in range(maxDecoderLength)] | ||
25 | +feedPrevious = tf.placeholder(tf.bool) | ||
26 | + | ||
27 | +encoderLSTM = tf.nn.rnn_cell.BasicLSTMCell(lstmUnits, state_is_tuple=True) | ||
28 | +#encoderLSTM = tf.nn.rnn_cell.MultiRNNCell([singleCell]*numLayersLSTM, state_is_tuple=True) | ||
29 | +decoderOutputs, decoderFinalState = tf.contrib.legacy_seq2seq.embedding_rnn_seq2seq(encoderInputs, decoderInputs, encoderLSTM, | ||
30 | + vocabSize, vocabSize, lstmUnits, feed_previous=feedPrevious) | ||
31 | + | ||
32 | +decoderPrediction = tf.argmax(decoderOutputs, 2) | ||
33 | + | ||
34 | +# Start session and get graph | ||
35 | +sess = tf.Session() | ||
36 | +#y, variables = model.getModel(encoderInputs, decoderLabels, decoderInputs, feedPrevious) | ||
37 | + | ||
38 | +# Load in pretrained model | ||
39 | +saver = tf.train.Saver() | ||
40 | +saver.restore(sess, tf.train.latest_checkpoint('models')) | ||
41 | +zeroVector = np.zeros((1), dtype='int32') | ||
42 | + | ||
43 | +def pred(inputString): | ||
44 | + inputVector = model.getTestInput(inputString, wordList, maxEncoderLength) | ||
45 | + feedDict = {encoderInputs[t]: inputVector[t] for t in range(maxEncoderLength)} | ||
46 | + feedDict.update({decoderLabels[t]: zeroVector for t in range(maxDecoderLength)}) | ||
47 | + feedDict.update({decoderInputs[t]: zeroVector for t in range(maxDecoderLength)}) | ||
48 | + feedDict.update({feedPrevious: True}) | ||
49 | + ids = (sess.run(decoderPrediction, feed_dict=feedDict)) | ||
50 | + return model.idsToSentence(ids, wordList) | ||
51 | + | ||
52 | +# webapp | ||
53 | +app = Flask(__name__, template_folder='./') | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
final/index.html
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html lang="en"> | ||
3 | +<head> | ||
4 | + <meta charset="UTF-8"> | ||
5 | + <title>MY APP</title> | ||
6 | +</head> | ||
7 | +<body> | ||
8 | + | ||
9 | +<div> | ||
10 | + <script type ="text/javascript"> | ||
11 | + function movepage() { | ||
12 | + location.href = "/makebot"; | ||
13 | + } | ||
14 | + </script> | ||
15 | + | ||
16 | +<form action="/uploadfile" enctype="multipart/form-data" method="POST"> | ||
17 | + <input type="file" name="myFile" /> | ||
18 | + <input type="submit" value="Upload a file"/> | ||
19 | +</form> | ||
20 | + <input type="button" value="make chatbot" onclick="movepage()"/> | ||
21 | +</div> | ||
22 | + | ||
23 | + | ||
24 | +</body> | ||
25 | +</html> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
final/management/.gitignore
0 → 100644
1 | +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
2 | + | ||
3 | +# database | ||
4 | +/database.json | ||
5 | + | ||
6 | +# dependencies | ||
7 | +/node_modules | ||
8 | +/.pnp | ||
9 | +.pnp.js | ||
10 | + | ||
11 | +# testing | ||
12 | +/coverage | ||
13 | + | ||
14 | +# production | ||
15 | +/build | ||
16 | + | ||
17 | +# misc | ||
18 | +.DS_Store | ||
19 | +.env.local | ||
20 | +.env.development.local | ||
21 | +.env.test.local | ||
22 | +.env.production.local | ||
23 | + | ||
24 | +npm-debug.log* | ||
25 | +yarn-debug.log* | ||
26 | +yarn-error.log* |
final/management/client/.gitignore
0 → 100644
1 | +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
2 | + | ||
3 | +# dependencies | ||
4 | +/node_modules | ||
5 | +/.pnp | ||
6 | +.pnp.js | ||
7 | + | ||
8 | +# testing | ||
9 | +/coverage | ||
10 | + | ||
11 | +# production | ||
12 | +/build | ||
13 | + | ||
14 | +# misc | ||
15 | +.DS_Store | ||
16 | +.env.local | ||
17 | +.env.development.local | ||
18 | +.env.test.local | ||
19 | +.env.production.local | ||
20 | + | ||
21 | +npm-debug.log* | ||
22 | +yarn-debug.log* | ||
23 | +yarn-error.log* |
final/management/client/README.md
0 → 100644
1 | +This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). | ||
2 | + | ||
3 | +## Available Scripts | ||
4 | + | ||
5 | +In the project directory, you can run: | ||
6 | + | ||
7 | +### `npm start` | ||
8 | + | ||
9 | +Runs the app in the development mode.<br /> | ||
10 | +Open [http://localhost:3000](http://localhost:3000) to view it in the browser. | ||
11 | + | ||
12 | +The page will reload if you make edits.<br /> | ||
13 | +You will also see any lint errors in the console. | ||
14 | + | ||
15 | +### `npm test` | ||
16 | + | ||
17 | +Launches the test runner in the interactive watch mode.<br /> | ||
18 | +See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. | ||
19 | + | ||
20 | +### `npm run build` | ||
21 | + | ||
22 | +Builds the app for production to the `build` folder.<br /> | ||
23 | +It correctly bundles React in production mode and optimizes the build for the best performance. | ||
24 | + | ||
25 | +The build is minified and the filenames include the hashes.<br /> | ||
26 | +Your app is ready to be deployed! | ||
27 | + | ||
28 | +See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. | ||
29 | + | ||
30 | +### `npm run eject` | ||
31 | + | ||
32 | +**Note: this is a one-way operation. Once you `eject`, you can’t go back!** | ||
33 | + | ||
34 | +If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. | ||
35 | + | ||
36 | +Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. | ||
37 | + | ||
38 | +You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. | ||
39 | + | ||
40 | +## Learn More | ||
41 | + | ||
42 | +You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). | ||
43 | + | ||
44 | +To learn React, check out the [React documentation](https://reactjs.org/). | ||
45 | + | ||
46 | +### Code Splitting | ||
47 | + | ||
48 | +This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting | ||
49 | + | ||
50 | +### Analyzing the Bundle Size | ||
51 | + | ||
52 | +This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size | ||
53 | + | ||
54 | +### Making a Progressive Web App | ||
55 | + | ||
56 | +This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app | ||
57 | + | ||
58 | +### Advanced Configuration | ||
59 | + | ||
60 | +This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration | ||
61 | + | ||
62 | +### Deployment | ||
63 | + | ||
64 | +This section has moved here: https://facebook.github.io/create-react-app/docs/deployment | ||
65 | + | ||
66 | +### `npm run build` fails to minify | ||
67 | + | ||
68 | +This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify |
final/management/client/package-lock.json
0 → 100644
This diff could not be displayed because it is too large.
final/management/client/package.json
0 → 100644
1 | +{ | ||
2 | + "name": "management", | ||
3 | + "version": "0.1.0", | ||
4 | + "private": true, | ||
5 | + "dependencies": { | ||
6 | + "@material-ui/core": "^4.10.2", | ||
7 | + "@testing-library/jest-dom": "^4.2.4", | ||
8 | + "@testing-library/react": "^9.5.0", | ||
9 | + "@testing-library/user-event": "^7.2.1", | ||
10 | + "react": "^16.13.1", | ||
11 | + "react-dom": "^16.13.1", | ||
12 | + "react-scripts": "3.4.1" | ||
13 | + }, | ||
14 | + "scripts": { | ||
15 | + "start": "react-scripts start", | ||
16 | + "build": "react-scripts build", | ||
17 | + "test": "react-scripts test", | ||
18 | + "eject": "react-scripts eject" | ||
19 | + }, | ||
20 | + "eslintConfig": { | ||
21 | + "extends": "react-app" | ||
22 | + }, | ||
23 | + "browserslist": { | ||
24 | + "production": [ | ||
25 | + ">0.2%", | ||
26 | + "not dead", | ||
27 | + "not op_mini all" | ||
28 | + ], | ||
29 | + "development": [ | ||
30 | + "last 1 chrome version", | ||
31 | + "last 1 firefox version", | ||
32 | + "last 1 safari version" | ||
33 | + ] | ||
34 | + }, | ||
35 | + "proxy": "http://localhost:5000/" | ||
36 | +} |
final/management/client/public/favicon.ico
0 → 100644
No preview for this file type
final/management/client/public/index.html
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html lang="en"> | ||
3 | + <head> | ||
4 | + <meta charset="utf-8" /> | ||
5 | + <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> | ||
6 | + <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
7 | + <meta name="theme-color" content="#000000" /> | ||
8 | + <meta | ||
9 | + name="description" | ||
10 | + content="Web site created using create-react-app" | ||
11 | + /> | ||
12 | + <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> | ||
13 | + <!-- | ||
14 | + manifest.json provides metadata used when your web app is installed on a | ||
15 | + user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ | ||
16 | + --> | ||
17 | + <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> | ||
18 | + <!-- | ||
19 | + Notice the use of %PUBLIC_URL% in the tags above. | ||
20 | + It will be replaced with the URL of the `public` folder during the build. | ||
21 | + Only files inside the `public` folder can be referenced from the HTML. | ||
22 | + | ||
23 | + Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will | ||
24 | + work correctly both with client-side routing and a non-root public URL. | ||
25 | + Learn how to configure a non-root public URL by running `npm run build`. | ||
26 | + --> | ||
27 | + <title>React App</title> | ||
28 | + </head> | ||
29 | + <body> | ||
30 | + <noscript>You need to enable JavaScript to run this app.</noscript> | ||
31 | + <div id="root"></div> | ||
32 | + <!-- | ||
33 | + This HTML file is a template. | ||
34 | + If you open it directly in the browser, you will see an empty page. | ||
35 | + | ||
36 | + You can add webfonts, meta tags, or analytics to this file. | ||
37 | + The build step will place the bundled scripts into the <body> tag. | ||
38 | + | ||
39 | + To begin the development, run `npm start` or `yarn start`. | ||
40 | + To create a production bundle, use `npm run build` or `yarn build`. | ||
41 | + --> | ||
42 | + </body> | ||
43 | +</html> |
final/management/client/public/logo192.png
0 → 100644

5.22 KB
final/management/client/public/logo512.png
0 → 100644

9.44 KB
final/management/client/public/manifest.json
0 → 100644
1 | +{ | ||
2 | + "short_name": "React App", | ||
3 | + "name": "Create React App Sample", | ||
4 | + "icons": [ | ||
5 | + { | ||
6 | + "src": "favicon.ico", | ||
7 | + "sizes": "64x64 32x32 24x24 16x16", | ||
8 | + "type": "image/x-icon" | ||
9 | + }, | ||
10 | + { | ||
11 | + "src": "logo192.png", | ||
12 | + "type": "image/png", | ||
13 | + "sizes": "192x192" | ||
14 | + }, | ||
15 | + { | ||
16 | + "src": "logo512.png", | ||
17 | + "type": "image/png", | ||
18 | + "sizes": "512x512" | ||
19 | + } | ||
20 | + ], | ||
21 | + "start_url": ".", | ||
22 | + "display": "standalone", | ||
23 | + "theme_color": "#000000", | ||
24 | + "background_color": "#ffffff" | ||
25 | +} |
final/management/client/public/robots.txt
0 → 100644
final/management/client/src/App.css
0 → 100644
1 | +.App { | ||
2 | + text-align: center; | ||
3 | +} | ||
4 | + | ||
5 | +.App-logo { | ||
6 | + height: 40vmin; | ||
7 | + pointer-events: none; | ||
8 | +} | ||
9 | + | ||
10 | +@media (prefers-reduced-motion: no-preference) { | ||
11 | + .App-logo { | ||
12 | + animation: App-logo-spin infinite 20s linear; | ||
13 | + } | ||
14 | +} | ||
15 | + | ||
16 | +.App-header { | ||
17 | + background-color: #282c34; | ||
18 | + min-height: 100vh; | ||
19 | + display: flex; | ||
20 | + flex-direction: column; | ||
21 | + align-items: center; | ||
22 | + justify-content: center; | ||
23 | + font-size: calc(10px + 2vmin); | ||
24 | + color: white; | ||
25 | +} | ||
26 | + | ||
27 | +.App-link { | ||
28 | + color: #61dafb; | ||
29 | +} | ||
30 | + | ||
31 | +@keyframes App-logo-spin { | ||
32 | + from { | ||
33 | + transform: rotate(0deg); | ||
34 | + } | ||
35 | + to { | ||
36 | + transform: rotate(360deg); | ||
37 | + } | ||
38 | +} |
final/management/client/src/App.js
0 → 100644
1 | +import React, { Component } from 'react'; | ||
2 | +import './App.css'; | ||
3 | +import Customer from './components/Customer'; | ||
4 | +import Paper from '@material-ui/core/Paper'; | ||
5 | +import Table from '@material-ui/core/Table'; | ||
6 | +import TableHead from '@material-ui/core/TableHead'; | ||
7 | +import TableBody from '@material-ui/core/TableBody'; | ||
8 | +import TableRow from '@material-ui/core/TableRow'; | ||
9 | +import TableCell from '@material-ui/core/TableCell'; | ||
10 | +import { withStyles } from '@material-ui/core/styles'; | ||
11 | +import CircularProgress from '@material-ui/core/CircularProgress'; | ||
12 | + | ||
13 | +const styles = theme => ({ | ||
14 | + root: { | ||
15 | + width: '100%', | ||
16 | + marginTop: theme.spacing.unit*3, | ||
17 | + overflowX: "auto" | ||
18 | + }, | ||
19 | + table: { | ||
20 | + minWidth: 1080 | ||
21 | + }, | ||
22 | + progress: { | ||
23 | + margine: theme.spacing.unit*2 | ||
24 | + } | ||
25 | +}) | ||
26 | + | ||
27 | +class App extends Component { | ||
28 | + state = { | ||
29 | + customers: "", | ||
30 | + completed: 0 | ||
31 | + } | ||
32 | + | ||
33 | + //모든 컴포넌트 준비 완료일 때 실행 -- react가 라이브러리이기 때문 ! | ||
34 | + componentDidMount() { | ||
35 | + this.timer = setInterval(this.progress,20); | ||
36 | + this.callApi() | ||
37 | + .then(res => this.setState({customers: res})) | ||
38 | + .catch(err => console.log(err)); | ||
39 | + } | ||
40 | + | ||
41 | + callApi = async() => { | ||
42 | + //local의/api/customers에 접속해 response로 받아오고 이를 body변수에 담아주겠다 | ||
43 | + const response = await fetch('/api/customers'); | ||
44 | + const body = await response.json(); | ||
45 | + return body; | ||
46 | + } | ||
47 | + | ||
48 | + progress = () => { | ||
49 | + const { completed } = this.state; | ||
50 | + this.setState({completed : completed >=100 ? 0: completed +1}); | ||
51 | + } | ||
52 | + | ||
53 | + render() { | ||
54 | + const { classes } = this.props; | ||
55 | + return ( | ||
56 | + <Paper className = {classes.root}> | ||
57 | + <Table className = {classes.table}> | ||
58 | + <TableHead> | ||
59 | + <TableRow> | ||
60 | + <TableCell>번호</TableCell> | ||
61 | + <TableCell>이미지</TableCell> | ||
62 | + <TableCell>이름</TableCell> | ||
63 | + <TableCell>생년월일</TableCell> | ||
64 | + <TableCell>성별</TableCell> | ||
65 | + <TableCell>직업</TableCell> | ||
66 | + </TableRow> | ||
67 | + </TableHead> | ||
68 | + | ||
69 | + <TableBody> | ||
70 | + {this.state.customers ? this.state.customers.map (c=> { | ||
71 | + return (<Customer key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />) | ||
72 | + }) : | ||
73 | + <TableRow> | ||
74 | + <TableCell colSpan="6" align="center"> | ||
75 | + <CircularProgress className={classes.progress} varient="determinate" value={this.state.completed}/> | ||
76 | + </TableCell> | ||
77 | + </TableRow> | ||
78 | + } | ||
79 | + </TableBody> | ||
80 | + </Table> | ||
81 | + </Paper> | ||
82 | + ); | ||
83 | + } | ||
84 | +} | ||
85 | + | ||
86 | +export default withStyles(styles)(App); |
final/management/client/src/App.test.js
0 → 100644
1 | +import React from 'react'; | ||
2 | +import { render } from '@testing-library/react'; | ||
3 | +import App from './App'; | ||
4 | + | ||
5 | +test('renders learn react link', () => { | ||
6 | + const { getByText } = render(<App />); | ||
7 | + const linkElement = getByText(/learn react/i); | ||
8 | + expect(linkElement).toBeInTheDocument(); | ||
9 | +}); |
1 | +import React from 'react'; | ||
2 | +import TableRow from '@material-ui/core/TableRow'; | ||
3 | +import TableCell from '@material-ui/core/TableCell'; | ||
4 | + | ||
5 | +class Customer extends React.Component { | ||
6 | + render(){ | ||
7 | + return( | ||
8 | + <TableRow> | ||
9 | + <TableCell>{this.props.id}</TableCell> | ||
10 | + <TableCell><img src={this.props.image} alt="profile"/></TableCell> | ||
11 | + <TableCell>{this.props.name}</TableCell> | ||
12 | + <TableCell>{this.props.birthday}</TableCell> | ||
13 | + <TableCell>{this.props.gender}</TableCell> | ||
14 | + <TableCell>{this.props.job}</TableCell> | ||
15 | + </TableRow> | ||
16 | + ); | ||
17 | + } | ||
18 | +} | ||
19 | + | ||
20 | + | ||
21 | + | ||
22 | +export default Customer; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
final/management/client/src/index.css
0 → 100644
1 | +body { | ||
2 | + margin: 0; | ||
3 | + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', | ||
4 | + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', | ||
5 | + sans-serif; | ||
6 | + -webkit-font-smoothing: antialiased; | ||
7 | + -moz-osx-font-smoothing: grayscale; | ||
8 | +} | ||
9 | + | ||
10 | +code { | ||
11 | + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', | ||
12 | + monospace; | ||
13 | +} |
final/management/client/src/index.js
0 → 100644
1 | +import React from 'react'; | ||
2 | +import ReactDOM from 'react-dom'; | ||
3 | +import './index.css'; | ||
4 | +import App from './App'; | ||
5 | +import * as serviceWorker from './serviceWorker'; | ||
6 | + | ||
7 | +ReactDOM.render( | ||
8 | + <React.StrictMode> | ||
9 | + <App /> | ||
10 | + </React.StrictMode>, | ||
11 | + document.getElementById('root') | ||
12 | +); | ||
13 | + | ||
14 | +// If you want your app to work offline and load faster, you can change | ||
15 | +// unregister() to register() below. Note this comes with some pitfalls. | ||
16 | +// Learn more about service workers: https://bit.ly/CRA-PWA | ||
17 | +serviceWorker.unregister(); |
final/management/client/src/logo.svg
0 → 100644
1 | +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"> | ||
2 | + <g fill="#61DAFB"> | ||
3 | + <path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/> | ||
4 | + <circle cx="420.9" cy="296.5" r="45.7"/> | ||
5 | + <path d="M520.5 78.1z"/> | ||
6 | + </g> | ||
7 | +</svg> |
final/management/client/src/serviceWorker.js
0 → 100644
1 | +// This optional code is used to register a service worker. | ||
2 | +// register() is not called by default. | ||
3 | + | ||
4 | +// This lets the app load faster on subsequent visits in production, and gives | ||
5 | +// it offline capabilities. However, it also means that developers (and users) | ||
6 | +// will only see deployed updates on subsequent visits to a page, after all the | ||
7 | +// existing tabs open on the page have been closed, since previously cached | ||
8 | +// resources are updated in the background. | ||
9 | + | ||
10 | +// To learn more about the benefits of this model and instructions on how to | ||
11 | +// opt-in, read https://bit.ly/CRA-PWA | ||
12 | + | ||
13 | +const isLocalhost = Boolean( | ||
14 | + window.location.hostname === 'localhost' || | ||
15 | + // [::1] is the IPv6 localhost address. | ||
16 | + window.location.hostname === '[::1]' || | ||
17 | + // 127.0.0.0/8 are considered localhost for IPv4. | ||
18 | + window.location.hostname.match( | ||
19 | + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ | ||
20 | + ) | ||
21 | +); | ||
22 | + | ||
23 | +export function register(config) { | ||
24 | + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { | ||
25 | + // The URL constructor is available in all browsers that support SW. | ||
26 | + const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); | ||
27 | + if (publicUrl.origin !== window.location.origin) { | ||
28 | + // Our service worker won't work if PUBLIC_URL is on a different origin | ||
29 | + // from what our page is served on. This might happen if a CDN is used to | ||
30 | + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 | ||
31 | + return; | ||
32 | + } | ||
33 | + | ||
34 | + window.addEventListener('load', () => { | ||
35 | + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; | ||
36 | + | ||
37 | + if (isLocalhost) { | ||
38 | + // This is running on localhost. Let's check if a service worker still exists or not. | ||
39 | + checkValidServiceWorker(swUrl, config); | ||
40 | + | ||
41 | + // Add some additional logging to localhost, pointing developers to the | ||
42 | + // service worker/PWA documentation. | ||
43 | + navigator.serviceWorker.ready.then(() => { | ||
44 | + console.log( | ||
45 | + 'This web app is being served cache-first by a service ' + | ||
46 | + 'worker. To learn more, visit https://bit.ly/CRA-PWA' | ||
47 | + ); | ||
48 | + }); | ||
49 | + } else { | ||
50 | + // Is not localhost. Just register service worker | ||
51 | + registerValidSW(swUrl, config); | ||
52 | + } | ||
53 | + }); | ||
54 | + } | ||
55 | +} | ||
56 | + | ||
57 | +function registerValidSW(swUrl, config) { | ||
58 | + navigator.serviceWorker | ||
59 | + .register(swUrl) | ||
60 | + .then(registration => { | ||
61 | + registration.onupdatefound = () => { | ||
62 | + const installingWorker = registration.installing; | ||
63 | + if (installingWorker == null) { | ||
64 | + return; | ||
65 | + } | ||
66 | + installingWorker.onstatechange = () => { | ||
67 | + if (installingWorker.state === 'installed') { | ||
68 | + if (navigator.serviceWorker.controller) { | ||
69 | + // At this point, the updated precached content has been fetched, | ||
70 | + // but the previous service worker will still serve the older | ||
71 | + // content until all client tabs are closed. | ||
72 | + console.log( | ||
73 | + 'New content is available and will be used when all ' + | ||
74 | + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' | ||
75 | + ); | ||
76 | + | ||
77 | + // Execute callback | ||
78 | + if (config && config.onUpdate) { | ||
79 | + config.onUpdate(registration); | ||
80 | + } | ||
81 | + } else { | ||
82 | + // At this point, everything has been precached. | ||
83 | + // It's the perfect time to display a | ||
84 | + // "Content is cached for offline use." message. | ||
85 | + console.log('Content is cached for offline use.'); | ||
86 | + | ||
87 | + // Execute callback | ||
88 | + if (config && config.onSuccess) { | ||
89 | + config.onSuccess(registration); | ||
90 | + } | ||
91 | + } | ||
92 | + } | ||
93 | + }; | ||
94 | + }; | ||
95 | + }) | ||
96 | + .catch(error => { | ||
97 | + console.error('Error during service worker registration:', error); | ||
98 | + }); | ||
99 | +} | ||
100 | + | ||
101 | +function checkValidServiceWorker(swUrl, config) { | ||
102 | + // Check if the service worker can be found. If it can't reload the page. | ||
103 | + fetch(swUrl, { | ||
104 | + headers: { 'Service-Worker': 'script' }, | ||
105 | + }) | ||
106 | + .then(response => { | ||
107 | + // Ensure service worker exists, and that we really are getting a JS file. | ||
108 | + const contentType = response.headers.get('content-type'); | ||
109 | + if ( | ||
110 | + response.status === 404 || | ||
111 | + (contentType != null && contentType.indexOf('javascript') === -1) | ||
112 | + ) { | ||
113 | + // No service worker found. Probably a different app. Reload the page. | ||
114 | + navigator.serviceWorker.ready.then(registration => { | ||
115 | + registration.unregister().then(() => { | ||
116 | + window.location.reload(); | ||
117 | + }); | ||
118 | + }); | ||
119 | + } else { | ||
120 | + // Service worker found. Proceed as normal. | ||
121 | + registerValidSW(swUrl, config); | ||
122 | + } | ||
123 | + }) | ||
124 | + .catch(() => { | ||
125 | + console.log( | ||
126 | + 'No internet connection found. App is running in offline mode.' | ||
127 | + ); | ||
128 | + }); | ||
129 | +} | ||
130 | + | ||
131 | +export function unregister() { | ||
132 | + if ('serviceWorker' in navigator) { | ||
133 | + navigator.serviceWorker.ready | ||
134 | + .then(registration => { | ||
135 | + registration.unregister(); | ||
136 | + }) | ||
137 | + .catch(error => { | ||
138 | + console.error(error.message); | ||
139 | + }); | ||
140 | + } | ||
141 | +} |
final/management/client/src/setupTests.js
0 → 100644
final/management/package-lock.json
0 → 100644
1 | +{ | ||
2 | + "name": "management", | ||
3 | + "version": "1.0.0", | ||
4 | + "lockfileVersion": 1, | ||
5 | + "requires": true, | ||
6 | + "dependencies": { | ||
7 | + "accepts": { | ||
8 | + "version": "1.3.7", | ||
9 | + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", | ||
10 | + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", | ||
11 | + "requires": { | ||
12 | + "mime-types": "~2.1.24", | ||
13 | + "negotiator": "0.6.2" | ||
14 | + } | ||
15 | + }, | ||
16 | + "array-flatten": { | ||
17 | + "version": "1.1.1", | ||
18 | + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", | ||
19 | + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" | ||
20 | + }, | ||
21 | + "bignumber.js": { | ||
22 | + "version": "9.0.0", | ||
23 | + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", | ||
24 | + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" | ||
25 | + }, | ||
26 | + "body-parser": { | ||
27 | + "version": "1.19.0", | ||
28 | + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", | ||
29 | + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", | ||
30 | + "requires": { | ||
31 | + "bytes": "3.1.0", | ||
32 | + "content-type": "~1.0.4", | ||
33 | + "debug": "2.6.9", | ||
34 | + "depd": "~1.1.2", | ||
35 | + "http-errors": "1.7.2", | ||
36 | + "iconv-lite": "0.4.24", | ||
37 | + "on-finished": "~2.3.0", | ||
38 | + "qs": "6.7.0", | ||
39 | + "raw-body": "2.4.0", | ||
40 | + "type-is": "~1.6.17" | ||
41 | + } | ||
42 | + }, | ||
43 | + "bytes": { | ||
44 | + "version": "3.1.0", | ||
45 | + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", | ||
46 | + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" | ||
47 | + }, | ||
48 | + "content-disposition": { | ||
49 | + "version": "0.5.3", | ||
50 | + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", | ||
51 | + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", | ||
52 | + "requires": { | ||
53 | + "safe-buffer": "5.1.2" | ||
54 | + } | ||
55 | + }, | ||
56 | + "content-type": { | ||
57 | + "version": "1.0.4", | ||
58 | + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", | ||
59 | + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" | ||
60 | + }, | ||
61 | + "cookie": { | ||
62 | + "version": "0.4.0", | ||
63 | + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", | ||
64 | + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" | ||
65 | + }, | ||
66 | + "cookie-signature": { | ||
67 | + "version": "1.0.6", | ||
68 | + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", | ||
69 | + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" | ||
70 | + }, | ||
71 | + "core-util-is": { | ||
72 | + "version": "1.0.2", | ||
73 | + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", | ||
74 | + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" | ||
75 | + }, | ||
76 | + "debug": { | ||
77 | + "version": "2.6.9", | ||
78 | + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", | ||
79 | + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", | ||
80 | + "requires": { | ||
81 | + "ms": "2.0.0" | ||
82 | + } | ||
83 | + }, | ||
84 | + "depd": { | ||
85 | + "version": "1.1.2", | ||
86 | + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", | ||
87 | + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" | ||
88 | + }, | ||
89 | + "destroy": { | ||
90 | + "version": "1.0.4", | ||
91 | + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", | ||
92 | + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" | ||
93 | + }, | ||
94 | + "ee-first": { | ||
95 | + "version": "1.1.1", | ||
96 | + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", | ||
97 | + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" | ||
98 | + }, | ||
99 | + "encodeurl": { | ||
100 | + "version": "1.0.2", | ||
101 | + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", | ||
102 | + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" | ||
103 | + }, | ||
104 | + "escape-html": { | ||
105 | + "version": "1.0.3", | ||
106 | + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", | ||
107 | + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" | ||
108 | + }, | ||
109 | + "etag": { | ||
110 | + "version": "1.8.1", | ||
111 | + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", | ||
112 | + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" | ||
113 | + }, | ||
114 | + "express": { | ||
115 | + "version": "4.17.1", | ||
116 | + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", | ||
117 | + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", | ||
118 | + "requires": { | ||
119 | + "accepts": "~1.3.7", | ||
120 | + "array-flatten": "1.1.1", | ||
121 | + "body-parser": "1.19.0", | ||
122 | + "content-disposition": "0.5.3", | ||
123 | + "content-type": "~1.0.4", | ||
124 | + "cookie": "0.4.0", | ||
125 | + "cookie-signature": "1.0.6", | ||
126 | + "debug": "2.6.9", | ||
127 | + "depd": "~1.1.2", | ||
128 | + "encodeurl": "~1.0.2", | ||
129 | + "escape-html": "~1.0.3", | ||
130 | + "etag": "~1.8.1", | ||
131 | + "finalhandler": "~1.1.2", | ||
132 | + "fresh": "0.5.2", | ||
133 | + "merge-descriptors": "1.0.1", | ||
134 | + "methods": "~1.1.2", | ||
135 | + "on-finished": "~2.3.0", | ||
136 | + "parseurl": "~1.3.3", | ||
137 | + "path-to-regexp": "0.1.7", | ||
138 | + "proxy-addr": "~2.0.5", | ||
139 | + "qs": "6.7.0", | ||
140 | + "range-parser": "~1.2.1", | ||
141 | + "safe-buffer": "5.1.2", | ||
142 | + "send": "0.17.1", | ||
143 | + "serve-static": "1.14.1", | ||
144 | + "setprototypeof": "1.1.1", | ||
145 | + "statuses": "~1.5.0", | ||
146 | + "type-is": "~1.6.18", | ||
147 | + "utils-merge": "1.0.1", | ||
148 | + "vary": "~1.1.2" | ||
149 | + } | ||
150 | + }, | ||
151 | + "finalhandler": { | ||
152 | + "version": "1.1.2", | ||
153 | + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", | ||
154 | + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", | ||
155 | + "requires": { | ||
156 | + "debug": "2.6.9", | ||
157 | + "encodeurl": "~1.0.2", | ||
158 | + "escape-html": "~1.0.3", | ||
159 | + "on-finished": "~2.3.0", | ||
160 | + "parseurl": "~1.3.3", | ||
161 | + "statuses": "~1.5.0", | ||
162 | + "unpipe": "~1.0.0" | ||
163 | + } | ||
164 | + }, | ||
165 | + "forwarded": { | ||
166 | + "version": "0.1.2", | ||
167 | + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", | ||
168 | + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" | ||
169 | + }, | ||
170 | + "fresh": { | ||
171 | + "version": "0.5.2", | ||
172 | + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", | ||
173 | + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" | ||
174 | + }, | ||
175 | + "http-errors": { | ||
176 | + "version": "1.7.2", | ||
177 | + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", | ||
178 | + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", | ||
179 | + "requires": { | ||
180 | + "depd": "~1.1.2", | ||
181 | + "inherits": "2.0.3", | ||
182 | + "setprototypeof": "1.1.1", | ||
183 | + "statuses": ">= 1.5.0 < 2", | ||
184 | + "toidentifier": "1.0.0" | ||
185 | + } | ||
186 | + }, | ||
187 | + "iconv-lite": { | ||
188 | + "version": "0.4.24", | ||
189 | + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", | ||
190 | + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", | ||
191 | + "requires": { | ||
192 | + "safer-buffer": ">= 2.1.2 < 3" | ||
193 | + } | ||
194 | + }, | ||
195 | + "inherits": { | ||
196 | + "version": "2.0.3", | ||
197 | + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", | ||
198 | + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" | ||
199 | + }, | ||
200 | + "ipaddr.js": { | ||
201 | + "version": "1.9.0", | ||
202 | + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", | ||
203 | + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" | ||
204 | + }, | ||
205 | + "media-typer": { | ||
206 | + "version": "0.3.0", | ||
207 | + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", | ||
208 | + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" | ||
209 | + }, | ||
210 | + "merge-descriptors": { | ||
211 | + "version": "1.0.1", | ||
212 | + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", | ||
213 | + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" | ||
214 | + }, | ||
215 | + "methods": { | ||
216 | + "version": "1.1.2", | ||
217 | + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", | ||
218 | + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" | ||
219 | + }, | ||
220 | + "mime": { | ||
221 | + "version": "1.6.0", | ||
222 | + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", | ||
223 | + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" | ||
224 | + }, | ||
225 | + "mime-db": { | ||
226 | + "version": "1.42.0", | ||
227 | + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", | ||
228 | + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" | ||
229 | + }, | ||
230 | + "mime-types": { | ||
231 | + "version": "2.1.25", | ||
232 | + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", | ||
233 | + "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", | ||
234 | + "requires": { | ||
235 | + "mime-db": "1.42.0" | ||
236 | + } | ||
237 | + }, | ||
238 | + "ms": { | ||
239 | + "version": "2.0.0", | ||
240 | + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", | ||
241 | + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" | ||
242 | + }, | ||
243 | + "mysql": { | ||
244 | + "version": "2.18.1", | ||
245 | + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", | ||
246 | + "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", | ||
247 | + "requires": { | ||
248 | + "bignumber.js": "9.0.0", | ||
249 | + "readable-stream": "2.3.7", | ||
250 | + "safe-buffer": "5.1.2", | ||
251 | + "sqlstring": "2.3.1" | ||
252 | + }, | ||
253 | + "dependencies": { | ||
254 | + "isarray": { | ||
255 | + "version": "1.0.0", | ||
256 | + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", | ||
257 | + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" | ||
258 | + }, | ||
259 | + "readable-stream": { | ||
260 | + "version": "2.3.7", | ||
261 | + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", | ||
262 | + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", | ||
263 | + "requires": { | ||
264 | + "core-util-is": "~1.0.0", | ||
265 | + "inherits": "~2.0.3", | ||
266 | + "isarray": "~1.0.0", | ||
267 | + "process-nextick-args": "~2.0.0", | ||
268 | + "safe-buffer": "~5.1.1", | ||
269 | + "string_decoder": "~1.1.1", | ||
270 | + "util-deprecate": "~1.0.1" | ||
271 | + } | ||
272 | + }, | ||
273 | + "string_decoder": { | ||
274 | + "version": "1.1.1", | ||
275 | + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", | ||
276 | + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", | ||
277 | + "requires": { | ||
278 | + "safe-buffer": "~5.1.0" | ||
279 | + } | ||
280 | + } | ||
281 | + } | ||
282 | + }, | ||
283 | + "negotiator": { | ||
284 | + "version": "0.6.2", | ||
285 | + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", | ||
286 | + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" | ||
287 | + }, | ||
288 | + "on-finished": { | ||
289 | + "version": "2.3.0", | ||
290 | + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", | ||
291 | + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", | ||
292 | + "requires": { | ||
293 | + "ee-first": "1.1.1" | ||
294 | + } | ||
295 | + }, | ||
296 | + "parseurl": { | ||
297 | + "version": "1.3.3", | ||
298 | + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", | ||
299 | + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" | ||
300 | + }, | ||
301 | + "path-to-regexp": { | ||
302 | + "version": "0.1.7", | ||
303 | + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", | ||
304 | + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" | ||
305 | + }, | ||
306 | + "process-nextick-args": { | ||
307 | + "version": "2.0.1", | ||
308 | + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", | ||
309 | + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" | ||
310 | + }, | ||
311 | + "proxy-addr": { | ||
312 | + "version": "2.0.5", | ||
313 | + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", | ||
314 | + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", | ||
315 | + "requires": { | ||
316 | + "forwarded": "~0.1.2", | ||
317 | + "ipaddr.js": "1.9.0" | ||
318 | + } | ||
319 | + }, | ||
320 | + "qs": { | ||
321 | + "version": "6.7.0", | ||
322 | + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", | ||
323 | + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" | ||
324 | + }, | ||
325 | + "range-parser": { | ||
326 | + "version": "1.2.1", | ||
327 | + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", | ||
328 | + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" | ||
329 | + }, | ||
330 | + "raw-body": { | ||
331 | + "version": "2.4.0", | ||
332 | + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", | ||
333 | + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", | ||
334 | + "requires": { | ||
335 | + "bytes": "3.1.0", | ||
336 | + "http-errors": "1.7.2", | ||
337 | + "iconv-lite": "0.4.24", | ||
338 | + "unpipe": "1.0.0" | ||
339 | + } | ||
340 | + }, | ||
341 | + "safe-buffer": { | ||
342 | + "version": "5.1.2", | ||
343 | + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", | ||
344 | + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" | ||
345 | + }, | ||
346 | + "safer-buffer": { | ||
347 | + "version": "2.1.2", | ||
348 | + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", | ||
349 | + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" | ||
350 | + }, | ||
351 | + "send": { | ||
352 | + "version": "0.17.1", | ||
353 | + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", | ||
354 | + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", | ||
355 | + "requires": { | ||
356 | + "debug": "2.6.9", | ||
357 | + "depd": "~1.1.2", | ||
358 | + "destroy": "~1.0.4", | ||
359 | + "encodeurl": "~1.0.2", | ||
360 | + "escape-html": "~1.0.3", | ||
361 | + "etag": "~1.8.1", | ||
362 | + "fresh": "0.5.2", | ||
363 | + "http-errors": "~1.7.2", | ||
364 | + "mime": "1.6.0", | ||
365 | + "ms": "2.1.1", | ||
366 | + "on-finished": "~2.3.0", | ||
367 | + "range-parser": "~1.2.1", | ||
368 | + "statuses": "~1.5.0" | ||
369 | + }, | ||
370 | + "dependencies": { | ||
371 | + "ms": { | ||
372 | + "version": "2.1.1", | ||
373 | + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", | ||
374 | + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" | ||
375 | + } | ||
376 | + } | ||
377 | + }, | ||
378 | + "serve-static": { | ||
379 | + "version": "1.14.1", | ||
380 | + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", | ||
381 | + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", | ||
382 | + "requires": { | ||
383 | + "encodeurl": "~1.0.2", | ||
384 | + "escape-html": "~1.0.3", | ||
385 | + "parseurl": "~1.3.3", | ||
386 | + "send": "0.17.1" | ||
387 | + } | ||
388 | + }, | ||
389 | + "setprototypeof": { | ||
390 | + "version": "1.1.1", | ||
391 | + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", | ||
392 | + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" | ||
393 | + }, | ||
394 | + "sqlstring": { | ||
395 | + "version": "2.3.1", | ||
396 | + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", | ||
397 | + "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" | ||
398 | + }, | ||
399 | + "statuses": { | ||
400 | + "version": "1.5.0", | ||
401 | + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", | ||
402 | + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" | ||
403 | + }, | ||
404 | + "toidentifier": { | ||
405 | + "version": "1.0.0", | ||
406 | + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", | ||
407 | + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" | ||
408 | + }, | ||
409 | + "type-is": { | ||
410 | + "version": "1.6.18", | ||
411 | + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", | ||
412 | + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", | ||
413 | + "requires": { | ||
414 | + "media-typer": "0.3.0", | ||
415 | + "mime-types": "~2.1.24" | ||
416 | + } | ||
417 | + }, | ||
418 | + "unpipe": { | ||
419 | + "version": "1.0.0", | ||
420 | + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", | ||
421 | + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" | ||
422 | + }, | ||
423 | + "util-deprecate": { | ||
424 | + "version": "1.0.2", | ||
425 | + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", | ||
426 | + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" | ||
427 | + }, | ||
428 | + "utils-merge": { | ||
429 | + "version": "1.0.1", | ||
430 | + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", | ||
431 | + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" | ||
432 | + }, | ||
433 | + "vary": { | ||
434 | + "version": "1.1.2", | ||
435 | + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", | ||
436 | + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" | ||
437 | + } | ||
438 | + } | ||
439 | +} |
final/management/package.json
0 → 100644
1 | +{ | ||
2 | + "name" : "management", | ||
3 | + "version": "1.0.0", | ||
4 | + "scripts":{ | ||
5 | + "client": "cd client && yarn start", | ||
6 | + "server": "node server.js", | ||
7 | + "dev": "concurrently --kill-others-on-fail \"yarn server\" \"yarn client\"" | ||
8 | + }, | ||
9 | + "dependencies": { | ||
10 | + "body-parser": "^1.18.3", | ||
11 | + "express": "^4.16.4" | ||
12 | + }, | ||
13 | + "devDependencies": { | ||
14 | + "concurrently":"^" | ||
15 | + } | ||
16 | +} |
final/management/server.js
0 → 100644
1 | +const fs = require('fs'); | ||
2 | +const express = require('express'); | ||
3 | +const bodyParser = require('body-parser'); | ||
4 | +const app = express(); | ||
5 | +const port = process.env.PORT || 5000; | ||
6 | + | ||
7 | +app.use(bodyParser.json()); | ||
8 | +app.use(bodyParser.urlencoded({ exrended : true })); | ||
9 | + | ||
10 | +const data = fs.readFileSync('./database.json'); | ||
11 | +const conf = JSON.parse(data); | ||
12 | +const mysql = require('mysql'); | ||
13 | + | ||
14 | +const connection = mysql.createConnection({ | ||
15 | + host: conf.host, | ||
16 | + user: conf.user, | ||
17 | + password: conf.password, | ||
18 | + port: conf.port, | ||
19 | + database: conf.databse | ||
20 | +}); | ||
21 | +connection.connect(); | ||
22 | + | ||
23 | + | ||
24 | +app.get('/api/customers', (req, res) => { | ||
25 | + connection.query( | ||
26 | + "SELECT * FROM CUSTOMER", | ||
27 | + (err,rows,fields) => { | ||
28 | + res.send(rows); | ||
29 | + } | ||
30 | + ) | ||
31 | +}) | ||
32 | + | ||
33 | +app.listen(port, () => console.log(`Listening on port ${port}`)); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
final/models/checkpoint
0 → 100644
1 | +model_checkpoint_path: "pretrained_seq2seq.ckpt-108300" | ||
2 | +all_model_checkpoint_paths: "pretrained_seq2seq.ckpt-107900" | ||
3 | +all_model_checkpoint_paths: "pretrained_seq2seq.ckpt-108000" | ||
4 | +all_model_checkpoint_paths: "pretrained_seq2seq.ckpt-108100" | ||
5 | +all_model_checkpoint_paths: "pretrained_seq2seq.ckpt-108200" | ||
6 | +all_model_checkpoint_paths: "pretrained_seq2seq.ckpt-108300" |
This file is too large to display.
No preview for this file type
No preview for this file type
This file is too large to display.
No preview for this file type
No preview for this file type
This file is too large to display.
No preview for this file type
No preview for this file type
This file is too large to display.
No preview for this file type
No preview for this file type
This file is too large to display.
No preview for this file type
No preview for this file type
final/myJsBots/my-own-bot/.eslintrc.js
0 → 100644
1 | +/* eslint-disable */ | ||
2 | +module.exports = { | ||
3 | + "extends": "standard", | ||
4 | + "rules": { | ||
5 | + "semi": [2, "always"], | ||
6 | + "indent": [2, 4], | ||
7 | + "no-return-await": 0, | ||
8 | + "space-before-function-paren": [2, { | ||
9 | + "named": "never", | ||
10 | + "anonymous": "never", | ||
11 | + "asyncArrow": "always" | ||
12 | + }], | ||
13 | + "template-curly-spacing": [2, "always"] | ||
14 | + } | ||
15 | +}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
final/myJsBots/my-own-bot/.gitignore
0 → 100644
final/myJsBots/my-own-bot/README.md
0 → 100644
1 | +# my-own-bot | ||
2 | + | ||
3 | +like me | ||
4 | + | ||
5 | +This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to create a simple bot that accepts input from the user and echoes it back. | ||
6 | + | ||
7 | +## Prerequisites | ||
8 | + | ||
9 | +- [Node.js](https://nodejs.org) version 10.14.1 or higher | ||
10 | + | ||
11 | + ```bash | ||
12 | + # determine node version | ||
13 | + node --version | ||
14 | + ``` | ||
15 | + | ||
16 | +## To run the bot | ||
17 | + | ||
18 | +- Install modules | ||
19 | + | ||
20 | + ```bash | ||
21 | + npm install | ||
22 | + ``` | ||
23 | + | ||
24 | +- Start the bot | ||
25 | + | ||
26 | + ```bash | ||
27 | + npm start | ||
28 | + ``` | ||
29 | + | ||
30 | +## Testing the bot using Bot Framework Emulator | ||
31 | + | ||
32 | +[Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. | ||
33 | + | ||
34 | +- Install the Bot Framework Emulator version 4.9.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases) | ||
35 | + | ||
36 | +### Connect to the bot using Bot Framework Emulator | ||
37 | + | ||
38 | +- Launch Bot Framework Emulator | ||
39 | +- File -> Open Bot | ||
40 | +- Enter a Bot URL of `http://localhost:3978/api/messages` | ||
41 | + | ||
42 | +## Deploy the bot to Azure | ||
43 | + | ||
44 | +To learn more about deploying a bot to Azure, see [Deploy your bot to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions. | ||
45 | + | ||
46 | + | ||
47 | +## Further reading | ||
48 | + | ||
49 | +- [Bot Framework Documentation](https://docs.botframework.com) | ||
50 | +- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0) | ||
51 | +- [Dialogs](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-dialog?view=azure-bot-service-4.0) | ||
52 | +- [Gathering Input Using Prompts](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-prompts?view=azure-bot-service-4.0) | ||
53 | +- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0) | ||
54 | +- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0) | ||
55 | +- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0) | ||
56 | +- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest) | ||
57 | +- [Azure Portal](https://portal.azure.com) | ||
58 | +- [Language Understanding using LUIS](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/) | ||
59 | +- [Channels and Bot Connector Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-concepts?view=azure-bot-service-4.0) | ||
60 | +- [Restify](https://www.npmjs.com/package/restify) | ||
61 | +- [dotenv](https://www.npmjs.com/package/dotenv) |
final/myJsBots/my-own-bot/bot.js
0 → 100644
1 | +// Copyright (c) Microsoft Corporation. All rights reserved. | ||
2 | +// Licensed under the MIT License. | ||
3 | + | ||
4 | +const { ActivityHandler, MessageFactory } = require('botbuilder'); | ||
5 | + | ||
6 | +class EchoBot extends ActivityHandler { | ||
7 | + constructor() { | ||
8 | + super(); | ||
9 | + // See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types. | ||
10 | + this.onMessage(async (context, next) => { | ||
11 | + const replyText = `Echo: ${ context.activity.text }`; | ||
12 | + await context.sendActivity(MessageFactory.text(replyText, replyText)); | ||
13 | + // By calling next() you ensure that the next BotHandler is run. | ||
14 | + await next(); | ||
15 | + }); | ||
16 | + | ||
17 | + this.onMembersAdded(async (context, next) => { | ||
18 | + const membersAdded = context.activity.membersAdded; | ||
19 | + const welcomeText = 'Hello and welcome!'; | ||
20 | + for (let cnt = 0; cnt < membersAdded.length; ++cnt) { | ||
21 | + if (membersAdded[cnt].id !== context.activity.recipient.id) { | ||
22 | + await context.sendActivity(MessageFactory.text(welcomeText, welcomeText)); | ||
23 | + } | ||
24 | + } | ||
25 | + // By calling next() you ensure that the next BotHandler is run. | ||
26 | + await next(); | ||
27 | + }); | ||
28 | + } | ||
29 | +} | ||
30 | + | ||
31 | +module.exports.EchoBot = EchoBot; |
1 | +{ | ||
2 | + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", | ||
3 | + "contentVersion": "1.0.0.0", | ||
4 | + "parameters": { | ||
5 | + "groupLocation": { | ||
6 | + "value": "" | ||
7 | + }, | ||
8 | + "groupName": { | ||
9 | + "value": "" | ||
10 | + }, | ||
11 | + "appId": { | ||
12 | + "value": "" | ||
13 | + }, | ||
14 | + "appSecret": { | ||
15 | + "value": "" | ||
16 | + }, | ||
17 | + "botId": { | ||
18 | + "value": "" | ||
19 | + }, | ||
20 | + "botSku": { | ||
21 | + "value": "" | ||
22 | + }, | ||
23 | + "newAppServicePlanName": { | ||
24 | + "value": "" | ||
25 | + }, | ||
26 | + "newAppServicePlanSku": { | ||
27 | + "value": { | ||
28 | + "name": "S1", | ||
29 | + "tier": "Standard", | ||
30 | + "size": "S1", | ||
31 | + "family": "S", | ||
32 | + "capacity": 1 | ||
33 | + } | ||
34 | + }, | ||
35 | + "newAppServicePlanLocation": { | ||
36 | + "value": "" | ||
37 | + }, | ||
38 | + "newWebAppName": { | ||
39 | + "value": "" | ||
40 | + } | ||
41 | + } | ||
42 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +{ | ||
2 | + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", | ||
3 | + "contentVersion": "1.0.0.0", | ||
4 | + "parameters": { | ||
5 | + "appId": { | ||
6 | + "value": "" | ||
7 | + }, | ||
8 | + "appSecret": { | ||
9 | + "value": "" | ||
10 | + }, | ||
11 | + "botId": { | ||
12 | + "value": "" | ||
13 | + }, | ||
14 | + "botSku": { | ||
15 | + "value": "" | ||
16 | + }, | ||
17 | + "newAppServicePlanName": { | ||
18 | + "value": "" | ||
19 | + }, | ||
20 | + "newAppServicePlanSku": { | ||
21 | + "value": { | ||
22 | + "name": "S1", | ||
23 | + "tier": "Standard", | ||
24 | + "size": "S1", | ||
25 | + "family": "S", | ||
26 | + "capacity": 1 | ||
27 | + } | ||
28 | + }, | ||
29 | + "appServicePlanLocation": { | ||
30 | + "value": "" | ||
31 | + }, | ||
32 | + "existingAppServicePlan": { | ||
33 | + "value": "" | ||
34 | + }, | ||
35 | + "newWebAppName": { | ||
36 | + "value": "" | ||
37 | + } | ||
38 | + } | ||
39 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +{ | ||
2 | + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", | ||
3 | + "contentVersion": "1.0.0.0", | ||
4 | + "parameters": { | ||
5 | + "groupLocation": { | ||
6 | + "type": "string", | ||
7 | + "metadata": { | ||
8 | + "description": "Specifies the location of the Resource Group." | ||
9 | + } | ||
10 | + }, | ||
11 | + "groupName": { | ||
12 | + "type": "string", | ||
13 | + "metadata": { | ||
14 | + "description": "Specifies the name of the Resource Group." | ||
15 | + } | ||
16 | + }, | ||
17 | + "appId": { | ||
18 | + "type": "string", | ||
19 | + "metadata": { | ||
20 | + "description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings." | ||
21 | + } | ||
22 | + }, | ||
23 | + "appSecret": { | ||
24 | + "type": "string", | ||
25 | + "metadata": { | ||
26 | + "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings." | ||
27 | + } | ||
28 | + }, | ||
29 | + "botId": { | ||
30 | + "type": "string", | ||
31 | + "metadata": { | ||
32 | + "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable." | ||
33 | + } | ||
34 | + }, | ||
35 | + "botSku": { | ||
36 | + "type": "string", | ||
37 | + "metadata": { | ||
38 | + "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." | ||
39 | + } | ||
40 | + }, | ||
41 | + "newAppServicePlanName": { | ||
42 | + "type": "string", | ||
43 | + "metadata": { | ||
44 | + "description": "The name of the App Service Plan." | ||
45 | + } | ||
46 | + }, | ||
47 | + "newAppServicePlanSku": { | ||
48 | + "type": "object", | ||
49 | + "defaultValue": { | ||
50 | + "name": "S1", | ||
51 | + "tier": "Standard", | ||
52 | + "size": "S1", | ||
53 | + "family": "S", | ||
54 | + "capacity": 1 | ||
55 | + }, | ||
56 | + "metadata": { | ||
57 | + "description": "The SKU of the App Service Plan. Defaults to Standard values." | ||
58 | + } | ||
59 | + }, | ||
60 | + "newAppServicePlanLocation": { | ||
61 | + "type": "string", | ||
62 | + "metadata": { | ||
63 | + "description": "The location of the App Service Plan. Defaults to \"westus\"." | ||
64 | + } | ||
65 | + }, | ||
66 | + "newWebAppName": { | ||
67 | + "type": "string", | ||
68 | + "defaultValue": "", | ||
69 | + "metadata": { | ||
70 | + "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." | ||
71 | + } | ||
72 | + } | ||
73 | + }, | ||
74 | + "variables": { | ||
75 | + "appServicePlanName": "[parameters('newAppServicePlanName')]", | ||
76 | + "resourcesLocation": "[parameters('newAppServicePlanLocation')]", | ||
77 | + "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", | ||
78 | + "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", | ||
79 | + "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]" | ||
80 | + }, | ||
81 | + "resources": [ | ||
82 | + { | ||
83 | + "name": "[parameters('groupName')]", | ||
84 | + "type": "Microsoft.Resources/resourceGroups", | ||
85 | + "apiVersion": "2018-05-01", | ||
86 | + "location": "[parameters('groupLocation')]", | ||
87 | + "properties": { | ||
88 | + } | ||
89 | + }, | ||
90 | + { | ||
91 | + "type": "Microsoft.Resources/deployments", | ||
92 | + "apiVersion": "2018-05-01", | ||
93 | + "name": "storageDeployment", | ||
94 | + "resourceGroup": "[parameters('groupName')]", | ||
95 | + "dependsOn": [ | ||
96 | + "[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]" | ||
97 | + ], | ||
98 | + "properties": { | ||
99 | + "mode": "Incremental", | ||
100 | + "template": { | ||
101 | + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", | ||
102 | + "contentVersion": "1.0.0.0", | ||
103 | + "parameters": {}, | ||
104 | + "variables": {}, | ||
105 | + "resources": [ | ||
106 | + { | ||
107 | + "comments": "Create a new App Service Plan", | ||
108 | + "type": "Microsoft.Web/serverfarms", | ||
109 | + "name": "[variables('appServicePlanName')]", | ||
110 | + "apiVersion": "2018-02-01", | ||
111 | + "location": "[variables('resourcesLocation')]", | ||
112 | + "sku": "[parameters('newAppServicePlanSku')]", | ||
113 | + "properties": { | ||
114 | + "name": "[variables('appServicePlanName')]" | ||
115 | + } | ||
116 | + }, | ||
117 | + { | ||
118 | + "comments": "Create a Web App using the new App Service Plan", | ||
119 | + "type": "Microsoft.Web/sites", | ||
120 | + "apiVersion": "2015-08-01", | ||
121 | + "location": "[variables('resourcesLocation')]", | ||
122 | + "kind": "app", | ||
123 | + "dependsOn": [ | ||
124 | + "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]" | ||
125 | + ], | ||
126 | + "name": "[variables('webAppName')]", | ||
127 | + "properties": { | ||
128 | + "name": "[variables('webAppName')]", | ||
129 | + "serverFarmId": "[variables('appServicePlanName')]", | ||
130 | + "siteConfig": { | ||
131 | + "appSettings": [ | ||
132 | + { | ||
133 | + "name": "WEBSITE_NODE_DEFAULT_VERSION", | ||
134 | + "value": "10.14.1" | ||
135 | + }, | ||
136 | + { | ||
137 | + "name": "MicrosoftAppId", | ||
138 | + "value": "[parameters('appId')]" | ||
139 | + }, | ||
140 | + { | ||
141 | + "name": "MicrosoftAppPassword", | ||
142 | + "value": "[parameters('appSecret')]" | ||
143 | + } | ||
144 | + ], | ||
145 | + "cors": { | ||
146 | + "allowedOrigins": [ | ||
147 | + "https://botservice.hosting.portal.azure.net", | ||
148 | + "https://hosting.onecloud.azure-test.net/" | ||
149 | + ] | ||
150 | + } | ||
151 | + } | ||
152 | + } | ||
153 | + }, | ||
154 | + { | ||
155 | + "apiVersion": "2017-12-01", | ||
156 | + "type": "Microsoft.BotService/botServices", | ||
157 | + "name": "[parameters('botId')]", | ||
158 | + "location": "global", | ||
159 | + "kind": "bot", | ||
160 | + "sku": { | ||
161 | + "name": "[parameters('botSku')]" | ||
162 | + }, | ||
163 | + "properties": { | ||
164 | + "name": "[parameters('botId')]", | ||
165 | + "displayName": "[parameters('botId')]", | ||
166 | + "endpoint": "[variables('botEndpoint')]", | ||
167 | + "msaAppId": "[parameters('appId')]", | ||
168 | + "developerAppInsightsApplicationId": null, | ||
169 | + "developerAppInsightKey": null, | ||
170 | + "publishingCredentials": null, | ||
171 | + "storageResourceId": null | ||
172 | + }, | ||
173 | + "dependsOn": [ | ||
174 | + "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]" | ||
175 | + ] | ||
176 | + } | ||
177 | + ], | ||
178 | + "outputs": {} | ||
179 | + } | ||
180 | + } | ||
181 | + } | ||
182 | + ] | ||
183 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +{ | ||
2 | + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", | ||
3 | + "contentVersion": "1.0.0.0", | ||
4 | + "parameters": { | ||
5 | + "appId": { | ||
6 | + "type": "string", | ||
7 | + "metadata": { | ||
8 | + "description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings." | ||
9 | + } | ||
10 | + }, | ||
11 | + "appSecret": { | ||
12 | + "type": "string", | ||
13 | + "metadata": { | ||
14 | + "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings. Defaults to \"\"." | ||
15 | + } | ||
16 | + }, | ||
17 | + "botId": { | ||
18 | + "type": "string", | ||
19 | + "metadata": { | ||
20 | + "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable." | ||
21 | + } | ||
22 | + }, | ||
23 | + "botSku": { | ||
24 | + "defaultValue": "F0", | ||
25 | + "type": "string", | ||
26 | + "metadata": { | ||
27 | + "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." | ||
28 | + } | ||
29 | + }, | ||
30 | + "newAppServicePlanName": { | ||
31 | + "type": "string", | ||
32 | + "defaultValue": "", | ||
33 | + "metadata": { | ||
34 | + "description": "The name of the new App Service Plan." | ||
35 | + } | ||
36 | + }, | ||
37 | + "newAppServicePlanSku": { | ||
38 | + "type": "object", | ||
39 | + "defaultValue": { | ||
40 | + "name": "S1", | ||
41 | + "tier": "Standard", | ||
42 | + "size": "S1", | ||
43 | + "family": "S", | ||
44 | + "capacity": 1 | ||
45 | + }, | ||
46 | + "metadata": { | ||
47 | + "description": "The SKU of the App Service Plan. Defaults to Standard values." | ||
48 | + } | ||
49 | + }, | ||
50 | + "appServicePlanLocation": { | ||
51 | + "type": "string", | ||
52 | + "metadata": { | ||
53 | + "description": "The location of the App Service Plan." | ||
54 | + } | ||
55 | + }, | ||
56 | + "existingAppServicePlan": { | ||
57 | + "type": "string", | ||
58 | + "defaultValue": "", | ||
59 | + "metadata": { | ||
60 | + "description": "Name of the existing App Service Plan used to create the Web App for the bot." | ||
61 | + } | ||
62 | + }, | ||
63 | + "newWebAppName": { | ||
64 | + "type": "string", | ||
65 | + "defaultValue": "", | ||
66 | + "metadata": { | ||
67 | + "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." | ||
68 | + } | ||
69 | + } | ||
70 | + }, | ||
71 | + "variables": { | ||
72 | + "defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]", | ||
73 | + "useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]", | ||
74 | + "servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]", | ||
75 | + "resourcesLocation": "[parameters('appServicePlanLocation')]", | ||
76 | + "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", | ||
77 | + "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", | ||
78 | + "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]" | ||
79 | + }, | ||
80 | + "resources": [ | ||
81 | + { | ||
82 | + "comments": "Create a new App Service Plan if no existing App Service Plan name was passed in.", | ||
83 | + "type": "Microsoft.Web/serverfarms", | ||
84 | + "condition": "[not(variables('useExistingAppServicePlan'))]", | ||
85 | + "name": "[variables('servicePlanName')]", | ||
86 | + "apiVersion": "2018-02-01", | ||
87 | + "location": "[variables('resourcesLocation')]", | ||
88 | + "sku": "[parameters('newAppServicePlanSku')]", | ||
89 | + "properties": { | ||
90 | + "name": "[variables('servicePlanName')]" | ||
91 | + } | ||
92 | + }, | ||
93 | + { | ||
94 | + "comments": "Create a Web App using an App Service Plan", | ||
95 | + "type": "Microsoft.Web/sites", | ||
96 | + "apiVersion": "2015-08-01", | ||
97 | + "location": "[variables('resourcesLocation')]", | ||
98 | + "kind": "app", | ||
99 | + "dependsOn": [ | ||
100 | + "[resourceId('Microsoft.Web/serverfarms/', variables('servicePlanName'))]" | ||
101 | + ], | ||
102 | + "name": "[variables('webAppName')]", | ||
103 | + "properties": { | ||
104 | + "name": "[variables('webAppName')]", | ||
105 | + "serverFarmId": "[variables('servicePlanName')]", | ||
106 | + "siteConfig": { | ||
107 | + "appSettings": [ | ||
108 | + { | ||
109 | + "name": "WEBSITE_NODE_DEFAULT_VERSION", | ||
110 | + "value": "10.14.1" | ||
111 | + }, | ||
112 | + { | ||
113 | + "name": "MicrosoftAppId", | ||
114 | + "value": "[parameters('appId')]" | ||
115 | + }, | ||
116 | + { | ||
117 | + "name": "MicrosoftAppPassword", | ||
118 | + "value": "[parameters('appSecret')]" | ||
119 | + } | ||
120 | + ], | ||
121 | + "cors": { | ||
122 | + "allowedOrigins": [ | ||
123 | + "https://botservice.hosting.portal.azure.net", | ||
124 | + "https://hosting.onecloud.azure-test.net/" | ||
125 | + ] | ||
126 | + } | ||
127 | + } | ||
128 | + } | ||
129 | + }, | ||
130 | + { | ||
131 | + "apiVersion": "2017-12-01", | ||
132 | + "type": "Microsoft.BotService/botServices", | ||
133 | + "name": "[parameters('botId')]", | ||
134 | + "location": "global", | ||
135 | + "kind": "bot", | ||
136 | + "sku": { | ||
137 | + "name": "[parameters('botSku')]" | ||
138 | + }, | ||
139 | + "properties": { | ||
140 | + "name": "[parameters('botId')]", | ||
141 | + "displayName": "[parameters('botId')]", | ||
142 | + "endpoint": "[variables('botEndpoint')]", | ||
143 | + "msaAppId": "[parameters('appId')]", | ||
144 | + "developerAppInsightsApplicationId": null, | ||
145 | + "developerAppInsightKey": null, | ||
146 | + "publishingCredentials": null, | ||
147 | + "storageResourceId": null | ||
148 | + }, | ||
149 | + "dependsOn": [ | ||
150 | + "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]" | ||
151 | + ] | ||
152 | + } | ||
153 | + ] | ||
154 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
final/myJsBots/my-own-bot/index.js
0 → 100644
1 | +//This is still work in progress | ||
2 | +/* | ||
3 | +Please report any bugs to nicomwaks@gmail.com | ||
4 | + | ||
5 | +i have added console.log on line 48 | ||
6 | + | ||
7 | + | ||
8 | + | ||
9 | + | ||
10 | + */ | ||
11 | +'use strict' | ||
12 | + | ||
13 | +const express = require('express') | ||
14 | +const bodyParser = require('body-parser') | ||
15 | +const request = require('request') | ||
16 | +const app = express() | ||
17 | + | ||
18 | +app.set('port', (process.env.PORT || 5000)) | ||
19 | + | ||
20 | +// parse application/x-www-form-urlencoded | ||
21 | +app.use(bodyParser.urlencoded({extended: false})) | ||
22 | + | ||
23 | +// parse application/json | ||
24 | +app.use(bodyParser.json()) | ||
25 | + | ||
26 | +// index | ||
27 | +app.get('/', function (req, res) { | ||
28 | + res.send('hello world i am a secret bot') | ||
29 | +}) | ||
30 | + | ||
31 | +// for facebook verification | ||
32 | +app.get('/webhook/', function (req, res) { | ||
33 | + if (req.query['hub.verify_token'] === 'my_voice_is_my_password_verify_me') { | ||
34 | + res.send(req.query['hub.challenge']) | ||
35 | + } else { | ||
36 | + res.send('Error, wrong token') | ||
37 | + } | ||
38 | +}) | ||
39 | + | ||
40 | +// to post data | ||
41 | +app.post('/webhook/', function (req, res) { | ||
42 | + let messaging_events = req.body.entry[0].messaging | ||
43 | + for (let i = 0; i < messaging_events.length; i++) { | ||
44 | + let event = req.body.entry[0].messaging[i] | ||
45 | + let sender = event.sender.id | ||
46 | + if (event.message && event.message.text) { | ||
47 | + let text = event.message.text | ||
48 | + if (text === 'Generic'){ | ||
49 | + console.log("welcome to chatbot") | ||
50 | + //sendGenericMessage(sender) | ||
51 | + continue | ||
52 | + } | ||
53 | + sendTextMessage(sender, "Text received, echo: " + text.substring(0, 200)) | ||
54 | + } | ||
55 | + if (event.postback) { | ||
56 | + let text = JSON.stringify(event.postback) | ||
57 | + sendTextMessage(sender, "Postback received: "+text.substring(0, 200), token) | ||
58 | + continue | ||
59 | + } | ||
60 | + } | ||
61 | + res.sendStatus(200) | ||
62 | +}) | ||
63 | + | ||
64 | + | ||
65 | +// recommended to inject access tokens as environmental variables, e.g. | ||
66 | +// const token = process.env.FB_PAGE_ACCESS_TOKEN | ||
67 | +const token = "EAAupaHJrQWABAEqni9O28fmMkpTvq5TO9T9OdPVlEBbj7ZCodF9B8rKeChVGduhGNjxr4zSPXWZA6LKdWZBkCxkuHoYZBmgrmQsrol7RiPoeh8NgvHy60VTcAu5TxdcQdBVSaZBaJIEA8cBbJKuP84CMa8UnroXdZAqALIK8gj1VKGNY3JNvWG" | ||
68 | + | ||
69 | +function sendTextMessage(sender, text) { | ||
70 | + let messageData = { text:text } | ||
71 | + | ||
72 | + request({ | ||
73 | + url: 'https://graph.facebook.com/v2.6/me/messages', | ||
74 | + qs: {access_token:token}, | ||
75 | + method: 'POST', | ||
76 | + json: { | ||
77 | + recipient: {id:sender}, | ||
78 | + message: messageData, | ||
79 | + } | ||
80 | + }, function(error, response, body) { | ||
81 | + if (error) { | ||
82 | + console.log('Error sending messages: ', error) | ||
83 | + } else if (response.body.error) { | ||
84 | + console.log('Error: ', response.body.error) | ||
85 | + } | ||
86 | + }) | ||
87 | +} | ||
88 | + | ||
89 | +function sendGenericMessage(sender) { | ||
90 | + let messageData = { | ||
91 | + "attachment": { | ||
92 | + "type": "template", | ||
93 | + "payload": { | ||
94 | + "template_type": "generic", | ||
95 | + "elements": [{ | ||
96 | + "title": "First card", | ||
97 | + "subtitle": "Element #1 of an hscroll", | ||
98 | + "image_url": "http://messengerdemo.parseapp.com/img/rift.png", | ||
99 | + "buttons": [{ | ||
100 | + "type": "web_url", | ||
101 | + "url": "https://www.messenger.com", | ||
102 | + "title": "web url" | ||
103 | + }, { | ||
104 | + "type": "postback", | ||
105 | + "title": "Postback", | ||
106 | + "payload": "Payload for first element in a generic bubble", | ||
107 | + }], | ||
108 | + }, { | ||
109 | + "title": "Second card", | ||
110 | + "subtitle": "Element #2 of an hscroll", | ||
111 | + "image_url": "http://messengerdemo.parseapp.com/img/gearvr.png", | ||
112 | + "buttons": [{ | ||
113 | + "type": "postback", | ||
114 | + "title": "Postback", | ||
115 | + "payload": "Payload for second element in a generic bubble", | ||
116 | + }], | ||
117 | + }] | ||
118 | + } | ||
119 | + } | ||
120 | + } | ||
121 | + request({ | ||
122 | + url: 'https://graph.facebook.com/v2.6/me/messages', | ||
123 | + qs: {access_token:token}, | ||
124 | + method: 'POST', | ||
125 | + json: { | ||
126 | + recipient: {id:sender}, | ||
127 | + message: messageData, | ||
128 | + } | ||
129 | + }, function(error, response, body) { | ||
130 | + if (error) { | ||
131 | + console.log('Error sending messages: ', error) | ||
132 | + } else if (response.body.error) { | ||
133 | + console.log('Error: ', response.body.error) | ||
134 | + } | ||
135 | + }) | ||
136 | +} | ||
137 | + | ||
138 | +// spin spin sugar | ||
139 | +app.listen(app.get('port'), function() { | ||
140 | + console.log('running on port', app.get('port')) | ||
141 | +}) |
final/myJsBots/my-own-bot/package-lock.json
0 → 100644
This diff could not be displayed because it is too large.
final/myJsBots/my-own-bot/package.json
0 → 100644
1 | +{ | ||
2 | + "name": "my-own-bot", | ||
3 | + "version": "1.0.0", | ||
4 | + "description": "like me", | ||
5 | + "author": "Generated using Microsoft Bot Builder Yeoman generator v4.9.0", | ||
6 | + "license": "MIT", | ||
7 | + "main": "index.js", | ||
8 | + "scripts": { | ||
9 | + "start": "node ./index.js", | ||
10 | + "watch": "nodemon ./index.js", | ||
11 | + "lint": "eslint .", | ||
12 | + "test": "echo \"Error: no test specified\" && exit 1" | ||
13 | + }, | ||
14 | + "repository": { | ||
15 | + "type": "git", | ||
16 | + "url": "https://github.com" | ||
17 | + }, | ||
18 | + "dependencies": { | ||
19 | + "botbuilder": "~4.9.0", | ||
20 | + "dotenv": "^8.2.0", | ||
21 | + "restify": "~8.5.1" | ||
22 | + }, | ||
23 | + "devDependencies": { | ||
24 | + "eslint": "^7.0.0", | ||
25 | + "eslint-config-standard": "^14.1.1", | ||
26 | + "eslint-plugin-import": "^2.20.2", | ||
27 | + "eslint-plugin-node": "^11.1.0", | ||
28 | + "eslint-plugin-promise": "^4.2.1", | ||
29 | + "eslint-plugin-standard": "^4.0.1", | ||
30 | + "nodemon": "~2.0.4" | ||
31 | + } | ||
32 | +} |
final/package-lock.json
0 → 100644
1 | +{ | ||
2 | + "name": "secretbots", | ||
3 | + "version": "1.0.0", | ||
4 | + "lockfileVersion": 1, | ||
5 | + "requires": true, | ||
6 | + "dependencies": { | ||
7 | + "accepts": { | ||
8 | + "version": "1.3.7", | ||
9 | + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", | ||
10 | + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", | ||
11 | + "requires": { | ||
12 | + "mime-types": "~2.1.24", | ||
13 | + "negotiator": "0.6.2" | ||
14 | + } | ||
15 | + }, | ||
16 | + "ajv": { | ||
17 | + "version": "6.12.2", | ||
18 | + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", | ||
19 | + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", | ||
20 | + "requires": { | ||
21 | + "fast-deep-equal": "^3.1.1", | ||
22 | + "fast-json-stable-stringify": "^2.0.0", | ||
23 | + "json-schema-traverse": "^0.4.1", | ||
24 | + "uri-js": "^4.2.2" | ||
25 | + } | ||
26 | + }, | ||
27 | + "array-flatten": { | ||
28 | + "version": "1.1.1", | ||
29 | + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", | ||
30 | + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" | ||
31 | + }, | ||
32 | + "asn1": { | ||
33 | + "version": "0.2.4", | ||
34 | + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", | ||
35 | + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", | ||
36 | + "requires": { | ||
37 | + "safer-buffer": "~2.1.0" | ||
38 | + } | ||
39 | + }, | ||
40 | + "assert-plus": { | ||
41 | + "version": "1.0.0", | ||
42 | + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", | ||
43 | + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" | ||
44 | + }, | ||
45 | + "asynckit": { | ||
46 | + "version": "0.4.0", | ||
47 | + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", | ||
48 | + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" | ||
49 | + }, | ||
50 | + "aws-sign2": { | ||
51 | + "version": "0.7.0", | ||
52 | + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", | ||
53 | + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" | ||
54 | + }, | ||
55 | + "aws4": { | ||
56 | + "version": "1.10.0", | ||
57 | + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", | ||
58 | + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" | ||
59 | + }, | ||
60 | + "bcrypt-pbkdf": { | ||
61 | + "version": "1.0.2", | ||
62 | + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", | ||
63 | + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", | ||
64 | + "requires": { | ||
65 | + "tweetnacl": "^0.14.3" | ||
66 | + } | ||
67 | + }, | ||
68 | + "body-parser": { | ||
69 | + "version": "1.19.0", | ||
70 | + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", | ||
71 | + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", | ||
72 | + "requires": { | ||
73 | + "bytes": "3.1.0", | ||
74 | + "content-type": "~1.0.4", | ||
75 | + "debug": "2.6.9", | ||
76 | + "depd": "~1.1.2", | ||
77 | + "http-errors": "1.7.2", | ||
78 | + "iconv-lite": "0.4.24", | ||
79 | + "on-finished": "~2.3.0", | ||
80 | + "qs": "6.7.0", | ||
81 | + "raw-body": "2.4.0", | ||
82 | + "type-is": "~1.6.17" | ||
83 | + } | ||
84 | + }, | ||
85 | + "bytes": { | ||
86 | + "version": "3.1.0", | ||
87 | + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", | ||
88 | + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" | ||
89 | + }, | ||
90 | + "caseless": { | ||
91 | + "version": "0.12.0", | ||
92 | + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", | ||
93 | + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" | ||
94 | + }, | ||
95 | + "combined-stream": { | ||
96 | + "version": "1.0.8", | ||
97 | + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", | ||
98 | + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", | ||
99 | + "requires": { | ||
100 | + "delayed-stream": "~1.0.0" | ||
101 | + } | ||
102 | + }, | ||
103 | + "content-disposition": { | ||
104 | + "version": "0.5.3", | ||
105 | + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", | ||
106 | + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", | ||
107 | + "requires": { | ||
108 | + "safe-buffer": "5.1.2" | ||
109 | + } | ||
110 | + }, | ||
111 | + "content-type": { | ||
112 | + "version": "1.0.4", | ||
113 | + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", | ||
114 | + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" | ||
115 | + }, | ||
116 | + "cookie": { | ||
117 | + "version": "0.4.0", | ||
118 | + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", | ||
119 | + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" | ||
120 | + }, | ||
121 | + "cookie-signature": { | ||
122 | + "version": "1.0.6", | ||
123 | + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", | ||
124 | + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" | ||
125 | + }, | ||
126 | + "core-util-is": { | ||
127 | + "version": "1.0.2", | ||
128 | + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", | ||
129 | + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" | ||
130 | + }, | ||
131 | + "dashdash": { | ||
132 | + "version": "1.14.1", | ||
133 | + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", | ||
134 | + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", | ||
135 | + "requires": { | ||
136 | + "assert-plus": "^1.0.0" | ||
137 | + } | ||
138 | + }, | ||
139 | + "debug": { | ||
140 | + "version": "2.6.9", | ||
141 | + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", | ||
142 | + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", | ||
143 | + "requires": { | ||
144 | + "ms": "2.0.0" | ||
145 | + } | ||
146 | + }, | ||
147 | + "delayed-stream": { | ||
148 | + "version": "1.0.0", | ||
149 | + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", | ||
150 | + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" | ||
151 | + }, | ||
152 | + "depd": { | ||
153 | + "version": "1.1.2", | ||
154 | + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", | ||
155 | + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" | ||
156 | + }, | ||
157 | + "destroy": { | ||
158 | + "version": "1.0.4", | ||
159 | + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", | ||
160 | + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" | ||
161 | + }, | ||
162 | + "ecc-jsbn": { | ||
163 | + "version": "0.1.2", | ||
164 | + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", | ||
165 | + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", | ||
166 | + "requires": { | ||
167 | + "jsbn": "~0.1.0", | ||
168 | + "safer-buffer": "^2.1.0" | ||
169 | + } | ||
170 | + }, | ||
171 | + "ee-first": { | ||
172 | + "version": "1.1.1", | ||
173 | + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", | ||
174 | + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" | ||
175 | + }, | ||
176 | + "encodeurl": { | ||
177 | + "version": "1.0.2", | ||
178 | + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", | ||
179 | + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" | ||
180 | + }, | ||
181 | + "escape-html": { | ||
182 | + "version": "1.0.3", | ||
183 | + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", | ||
184 | + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" | ||
185 | + }, | ||
186 | + "etag": { | ||
187 | + "version": "1.8.1", | ||
188 | + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", | ||
189 | + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" | ||
190 | + }, | ||
191 | + "express": { | ||
192 | + "version": "4.17.1", | ||
193 | + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", | ||
194 | + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", | ||
195 | + "requires": { | ||
196 | + "accepts": "~1.3.7", | ||
197 | + "array-flatten": "1.1.1", | ||
198 | + "body-parser": "1.19.0", | ||
199 | + "content-disposition": "0.5.3", | ||
200 | + "content-type": "~1.0.4", | ||
201 | + "cookie": "0.4.0", | ||
202 | + "cookie-signature": "1.0.6", | ||
203 | + "debug": "2.6.9", | ||
204 | + "depd": "~1.1.2", | ||
205 | + "encodeurl": "~1.0.2", | ||
206 | + "escape-html": "~1.0.3", | ||
207 | + "etag": "~1.8.1", | ||
208 | + "finalhandler": "~1.1.2", | ||
209 | + "fresh": "0.5.2", | ||
210 | + "merge-descriptors": "1.0.1", | ||
211 | + "methods": "~1.1.2", | ||
212 | + "on-finished": "~2.3.0", | ||
213 | + "parseurl": "~1.3.3", | ||
214 | + "path-to-regexp": "0.1.7", | ||
215 | + "proxy-addr": "~2.0.5", | ||
216 | + "qs": "6.7.0", | ||
217 | + "range-parser": "~1.2.1", | ||
218 | + "safe-buffer": "5.1.2", | ||
219 | + "send": "0.17.1", | ||
220 | + "serve-static": "1.14.1", | ||
221 | + "setprototypeof": "1.1.1", | ||
222 | + "statuses": "~1.5.0", | ||
223 | + "type-is": "~1.6.18", | ||
224 | + "utils-merge": "1.0.1", | ||
225 | + "vary": "~1.1.2" | ||
226 | + } | ||
227 | + }, | ||
228 | + "extend": { | ||
229 | + "version": "3.0.2", | ||
230 | + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", | ||
231 | + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" | ||
232 | + }, | ||
233 | + "extsprintf": { | ||
234 | + "version": "1.3.0", | ||
235 | + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", | ||
236 | + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" | ||
237 | + }, | ||
238 | + "fast-deep-equal": { | ||
239 | + "version": "3.1.3", | ||
240 | + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", | ||
241 | + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" | ||
242 | + }, | ||
243 | + "fast-json-stable-stringify": { | ||
244 | + "version": "2.1.0", | ||
245 | + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", | ||
246 | + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" | ||
247 | + }, | ||
248 | + "finalhandler": { | ||
249 | + "version": "1.1.2", | ||
250 | + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", | ||
251 | + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", | ||
252 | + "requires": { | ||
253 | + "debug": "2.6.9", | ||
254 | + "encodeurl": "~1.0.2", | ||
255 | + "escape-html": "~1.0.3", | ||
256 | + "on-finished": "~2.3.0", | ||
257 | + "parseurl": "~1.3.3", | ||
258 | + "statuses": "~1.5.0", | ||
259 | + "unpipe": "~1.0.0" | ||
260 | + } | ||
261 | + }, | ||
262 | + "forever-agent": { | ||
263 | + "version": "0.6.1", | ||
264 | + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", | ||
265 | + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" | ||
266 | + }, | ||
267 | + "form-data": { | ||
268 | + "version": "2.3.3", | ||
269 | + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", | ||
270 | + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", | ||
271 | + "requires": { | ||
272 | + "asynckit": "^0.4.0", | ||
273 | + "combined-stream": "^1.0.6", | ||
274 | + "mime-types": "^2.1.12" | ||
275 | + } | ||
276 | + }, | ||
277 | + "forwarded": { | ||
278 | + "version": "0.1.2", | ||
279 | + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", | ||
280 | + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" | ||
281 | + }, | ||
282 | + "fresh": { | ||
283 | + "version": "0.5.2", | ||
284 | + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", | ||
285 | + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" | ||
286 | + }, | ||
287 | + "getpass": { | ||
288 | + "version": "0.1.7", | ||
289 | + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", | ||
290 | + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", | ||
291 | + "requires": { | ||
292 | + "assert-plus": "^1.0.0" | ||
293 | + } | ||
294 | + }, | ||
295 | + "har-schema": { | ||
296 | + "version": "2.0.0", | ||
297 | + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", | ||
298 | + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" | ||
299 | + }, | ||
300 | + "har-validator": { | ||
301 | + "version": "5.1.3", | ||
302 | + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", | ||
303 | + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", | ||
304 | + "requires": { | ||
305 | + "ajv": "^6.5.5", | ||
306 | + "har-schema": "^2.0.0" | ||
307 | + } | ||
308 | + }, | ||
309 | + "http-errors": { | ||
310 | + "version": "1.7.2", | ||
311 | + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", | ||
312 | + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", | ||
313 | + "requires": { | ||
314 | + "depd": "~1.1.2", | ||
315 | + "inherits": "2.0.3", | ||
316 | + "setprototypeof": "1.1.1", | ||
317 | + "statuses": ">= 1.5.0 < 2", | ||
318 | + "toidentifier": "1.0.0" | ||
319 | + } | ||
320 | + }, | ||
321 | + "http-signature": { | ||
322 | + "version": "1.2.0", | ||
323 | + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", | ||
324 | + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", | ||
325 | + "requires": { | ||
326 | + "assert-plus": "^1.0.0", | ||
327 | + "jsprim": "^1.2.2", | ||
328 | + "sshpk": "^1.7.0" | ||
329 | + } | ||
330 | + }, | ||
331 | + "iconv-lite": { | ||
332 | + "version": "0.4.24", | ||
333 | + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", | ||
334 | + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", | ||
335 | + "requires": { | ||
336 | + "safer-buffer": ">= 2.1.2 < 3" | ||
337 | + } | ||
338 | + }, | ||
339 | + "inherits": { | ||
340 | + "version": "2.0.3", | ||
341 | + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", | ||
342 | + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" | ||
343 | + }, | ||
344 | + "ipaddr.js": { | ||
345 | + "version": "1.9.1", | ||
346 | + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", | ||
347 | + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" | ||
348 | + }, | ||
349 | + "is-typedarray": { | ||
350 | + "version": "1.0.0", | ||
351 | + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", | ||
352 | + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" | ||
353 | + }, | ||
354 | + "isstream": { | ||
355 | + "version": "0.1.2", | ||
356 | + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", | ||
357 | + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" | ||
358 | + }, | ||
359 | + "jsbn": { | ||
360 | + "version": "0.1.1", | ||
361 | + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", | ||
362 | + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" | ||
363 | + }, | ||
364 | + "json-schema": { | ||
365 | + "version": "0.2.3", | ||
366 | + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", | ||
367 | + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" | ||
368 | + }, | ||
369 | + "json-schema-traverse": { | ||
370 | + "version": "0.4.1", | ||
371 | + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", | ||
372 | + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" | ||
373 | + }, | ||
374 | + "json-stringify-safe": { | ||
375 | + "version": "5.0.1", | ||
376 | + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", | ||
377 | + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" | ||
378 | + }, | ||
379 | + "jsprim": { | ||
380 | + "version": "1.4.1", | ||
381 | + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", | ||
382 | + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", | ||
383 | + "requires": { | ||
384 | + "assert-plus": "1.0.0", | ||
385 | + "extsprintf": "1.3.0", | ||
386 | + "json-schema": "0.2.3", | ||
387 | + "verror": "1.10.0" | ||
388 | + } | ||
389 | + }, | ||
390 | + "media-typer": { | ||
391 | + "version": "0.3.0", | ||
392 | + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", | ||
393 | + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" | ||
394 | + }, | ||
395 | + "merge-descriptors": { | ||
396 | + "version": "1.0.1", | ||
397 | + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", | ||
398 | + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" | ||
399 | + }, | ||
400 | + "methods": { | ||
401 | + "version": "1.1.2", | ||
402 | + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", | ||
403 | + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" | ||
404 | + }, | ||
405 | + "mime": { | ||
406 | + "version": "1.6.0", | ||
407 | + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", | ||
408 | + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" | ||
409 | + }, | ||
410 | + "mime-db": { | ||
411 | + "version": "1.44.0", | ||
412 | + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", | ||
413 | + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" | ||
414 | + }, | ||
415 | + "mime-types": { | ||
416 | + "version": "2.1.27", | ||
417 | + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", | ||
418 | + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", | ||
419 | + "requires": { | ||
420 | + "mime-db": "1.44.0" | ||
421 | + } | ||
422 | + }, | ||
423 | + "ms": { | ||
424 | + "version": "2.0.0", | ||
425 | + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", | ||
426 | + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" | ||
427 | + }, | ||
428 | + "negotiator": { | ||
429 | + "version": "0.6.2", | ||
430 | + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", | ||
431 | + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" | ||
432 | + }, | ||
433 | + "oauth-sign": { | ||
434 | + "version": "0.9.0", | ||
435 | + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", | ||
436 | + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" | ||
437 | + }, | ||
438 | + "on-finished": { | ||
439 | + "version": "2.3.0", | ||
440 | + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", | ||
441 | + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", | ||
442 | + "requires": { | ||
443 | + "ee-first": "1.1.1" | ||
444 | + } | ||
445 | + }, | ||
446 | + "parseurl": { | ||
447 | + "version": "1.3.3", | ||
448 | + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", | ||
449 | + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" | ||
450 | + }, | ||
451 | + "path-to-regexp": { | ||
452 | + "version": "0.1.7", | ||
453 | + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", | ||
454 | + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" | ||
455 | + }, | ||
456 | + "performance-now": { | ||
457 | + "version": "2.1.0", | ||
458 | + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", | ||
459 | + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" | ||
460 | + }, | ||
461 | + "proxy-addr": { | ||
462 | + "version": "2.0.6", | ||
463 | + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", | ||
464 | + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", | ||
465 | + "requires": { | ||
466 | + "forwarded": "~0.1.2", | ||
467 | + "ipaddr.js": "1.9.1" | ||
468 | + } | ||
469 | + }, | ||
470 | + "psl": { | ||
471 | + "version": "1.8.0", | ||
472 | + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", | ||
473 | + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" | ||
474 | + }, | ||
475 | + "punycode": { | ||
476 | + "version": "2.1.1", | ||
477 | + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", | ||
478 | + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" | ||
479 | + }, | ||
480 | + "qs": { | ||
481 | + "version": "6.7.0", | ||
482 | + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", | ||
483 | + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" | ||
484 | + }, | ||
485 | + "range-parser": { | ||
486 | + "version": "1.2.1", | ||
487 | + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", | ||
488 | + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" | ||
489 | + }, | ||
490 | + "raw-body": { | ||
491 | + "version": "2.4.0", | ||
492 | + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", | ||
493 | + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", | ||
494 | + "requires": { | ||
495 | + "bytes": "3.1.0", | ||
496 | + "http-errors": "1.7.2", | ||
497 | + "iconv-lite": "0.4.24", | ||
498 | + "unpipe": "1.0.0" | ||
499 | + } | ||
500 | + }, | ||
501 | + "request": { | ||
502 | + "version": "2.88.2", | ||
503 | + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", | ||
504 | + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", | ||
505 | + "requires": { | ||
506 | + "aws-sign2": "~0.7.0", | ||
507 | + "aws4": "^1.8.0", | ||
508 | + "caseless": "~0.12.0", | ||
509 | + "combined-stream": "~1.0.6", | ||
510 | + "extend": "~3.0.2", | ||
511 | + "forever-agent": "~0.6.1", | ||
512 | + "form-data": "~2.3.2", | ||
513 | + "har-validator": "~5.1.3", | ||
514 | + "http-signature": "~1.2.0", | ||
515 | + "is-typedarray": "~1.0.0", | ||
516 | + "isstream": "~0.1.2", | ||
517 | + "json-stringify-safe": "~5.0.1", | ||
518 | + "mime-types": "~2.1.19", | ||
519 | + "oauth-sign": "~0.9.0", | ||
520 | + "performance-now": "^2.1.0", | ||
521 | + "qs": "~6.5.2", | ||
522 | + "safe-buffer": "^5.1.2", | ||
523 | + "tough-cookie": "~2.5.0", | ||
524 | + "tunnel-agent": "^0.6.0", | ||
525 | + "uuid": "^3.3.2" | ||
526 | + }, | ||
527 | + "dependencies": { | ||
528 | + "qs": { | ||
529 | + "version": "6.5.2", | ||
530 | + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", | ||
531 | + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" | ||
532 | + } | ||
533 | + } | ||
534 | + }, | ||
535 | + "safe-buffer": { | ||
536 | + "version": "5.1.2", | ||
537 | + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", | ||
538 | + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" | ||
539 | + }, | ||
540 | + "safer-buffer": { | ||
541 | + "version": "2.1.2", | ||
542 | + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", | ||
543 | + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" | ||
544 | + }, | ||
545 | + "send": { | ||
546 | + "version": "0.17.1", | ||
547 | + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", | ||
548 | + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", | ||
549 | + "requires": { | ||
550 | + "debug": "2.6.9", | ||
551 | + "depd": "~1.1.2", | ||
552 | + "destroy": "~1.0.4", | ||
553 | + "encodeurl": "~1.0.2", | ||
554 | + "escape-html": "~1.0.3", | ||
555 | + "etag": "~1.8.1", | ||
556 | + "fresh": "0.5.2", | ||
557 | + "http-errors": "~1.7.2", | ||
558 | + "mime": "1.6.0", | ||
559 | + "ms": "2.1.1", | ||
560 | + "on-finished": "~2.3.0", | ||
561 | + "range-parser": "~1.2.1", | ||
562 | + "statuses": "~1.5.0" | ||
563 | + }, | ||
564 | + "dependencies": { | ||
565 | + "ms": { | ||
566 | + "version": "2.1.1", | ||
567 | + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", | ||
568 | + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" | ||
569 | + } | ||
570 | + } | ||
571 | + }, | ||
572 | + "serve-static": { | ||
573 | + "version": "1.14.1", | ||
574 | + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", | ||
575 | + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", | ||
576 | + "requires": { | ||
577 | + "encodeurl": "~1.0.2", | ||
578 | + "escape-html": "~1.0.3", | ||
579 | + "parseurl": "~1.3.3", | ||
580 | + "send": "0.17.1" | ||
581 | + } | ||
582 | + }, | ||
583 | + "setprototypeof": { | ||
584 | + "version": "1.1.1", | ||
585 | + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", | ||
586 | + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" | ||
587 | + }, | ||
588 | + "sshpk": { | ||
589 | + "version": "1.16.1", | ||
590 | + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", | ||
591 | + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", | ||
592 | + "requires": { | ||
593 | + "asn1": "~0.2.3", | ||
594 | + "assert-plus": "^1.0.0", | ||
595 | + "bcrypt-pbkdf": "^1.0.0", | ||
596 | + "dashdash": "^1.12.0", | ||
597 | + "ecc-jsbn": "~0.1.1", | ||
598 | + "getpass": "^0.1.1", | ||
599 | + "jsbn": "~0.1.0", | ||
600 | + "safer-buffer": "^2.0.2", | ||
601 | + "tweetnacl": "~0.14.0" | ||
602 | + } | ||
603 | + }, | ||
604 | + "statuses": { | ||
605 | + "version": "1.5.0", | ||
606 | + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", | ||
607 | + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" | ||
608 | + }, | ||
609 | + "toidentifier": { | ||
610 | + "version": "1.0.0", | ||
611 | + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", | ||
612 | + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" | ||
613 | + }, | ||
614 | + "tough-cookie": { | ||
615 | + "version": "2.5.0", | ||
616 | + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", | ||
617 | + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", | ||
618 | + "requires": { | ||
619 | + "psl": "^1.1.28", | ||
620 | + "punycode": "^2.1.1" | ||
621 | + } | ||
622 | + }, | ||
623 | + "tunnel-agent": { | ||
624 | + "version": "0.6.0", | ||
625 | + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", | ||
626 | + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", | ||
627 | + "requires": { | ||
628 | + "safe-buffer": "^5.0.1" | ||
629 | + } | ||
630 | + }, | ||
631 | + "tweetnacl": { | ||
632 | + "version": "0.14.5", | ||
633 | + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", | ||
634 | + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" | ||
635 | + }, | ||
636 | + "type-is": { | ||
637 | + "version": "1.6.18", | ||
638 | + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", | ||
639 | + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", | ||
640 | + "requires": { | ||
641 | + "media-typer": "0.3.0", | ||
642 | + "mime-types": "~2.1.24" | ||
643 | + } | ||
644 | + }, | ||
645 | + "unpipe": { | ||
646 | + "version": "1.0.0", | ||
647 | + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", | ||
648 | + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" | ||
649 | + }, | ||
650 | + "uri-js": { | ||
651 | + "version": "4.2.2", | ||
652 | + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", | ||
653 | + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", | ||
654 | + "requires": { | ||
655 | + "punycode": "^2.1.0" | ||
656 | + } | ||
657 | + }, | ||
658 | + "utils-merge": { | ||
659 | + "version": "1.0.1", | ||
660 | + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", | ||
661 | + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" | ||
662 | + }, | ||
663 | + "uuid": { | ||
664 | + "version": "3.4.0", | ||
665 | + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", | ||
666 | + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" | ||
667 | + }, | ||
668 | + "vary": { | ||
669 | + "version": "1.1.2", | ||
670 | + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", | ||
671 | + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" | ||
672 | + }, | ||
673 | + "verror": { | ||
674 | + "version": "1.10.0", | ||
675 | + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", | ||
676 | + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", | ||
677 | + "requires": { | ||
678 | + "assert-plus": "^1.0.0", | ||
679 | + "core-util-is": "1.0.2", | ||
680 | + "extsprintf": "^1.2.0" | ||
681 | + } | ||
682 | + } | ||
683 | + } | ||
684 | +} |
final/package.json
0 → 100644
1 | +{ | ||
2 | + "name": "secretbots", | ||
3 | + "version": "1.0.0", | ||
4 | + "description": "", | ||
5 | + "main": "index.js", | ||
6 | + "scripts": { | ||
7 | + "test": "echo \"Error: no test specified\" && exit 1" | ||
8 | + }, | ||
9 | + "author": "", | ||
10 | + "license": "ISC", | ||
11 | + "dependencies": { | ||
12 | + "body-parser": "^1.15.0", | ||
13 | + "express": "^4.13.4", | ||
14 | + "request": "^2.71.0" | ||
15 | + } | ||
16 | +} |
final/server.js
0 → 100644
1 | +"use strict"; | ||
2 | + | ||
3 | +const moment = require('moment'); | ||
4 | +const process = require('process'); | ||
5 | +const async = require('async'); | ||
6 | + | ||
7 | +const express = require('express'); | ||
8 | + | ||
9 | +const AWS = require('aws-sdk'); | ||
10 | +const s3 = new AWS.S3({ | ||
11 | + region: 'us-east-1', | ||
12 | + accessKeyId: 'ASIA265JP6JAKK4AAZFL', | ||
13 | + secretAccessKey: 'CuomIr3gEgtwPcOWSN94mC54iIMMWfXTVSFJXDUf', | ||
14 | + sessionToken: 'FwoGZXIvYXdzEMX//////////wEaDLD1Yk1xhWixp9ZSByK/AbDcW6LYR1EpPLNIqCteTtZtXCoIIFr7b2olxXkKyQUVQGUnf4YJVcmk8PD1/1T9O/MaJIuO0c0tAO2ir31Oa5nsc1QomKEFpAy/girMM3JML5azImFhjMKDGYahUiZWTfiP8oksDWoeEM+HjwEvWzSa5nBBoTlKxa33Gjo/15LL/oHDgN75K/fiHWAJ6Uvey0I1Lu26CjohLbEE9tQ61ymtq4GiO96DSjgSTW4gyrp5R+tn0oLH1A51oLNBfFxsKOvfxu8FMi2RufVY3HSu2JsFVyXMXkMrgDhVA5lZngj2ZW7SQx8No0vfb9zPKuHXxGKjkQY=' | ||
15 | + }); | ||
16 | +var BUCKET = 'oss-project-image-storage'; | ||
17 | +var S3UPparam = { | ||
18 | + Bucket:BUCKET, | ||
19 | + Key:null, | ||
20 | + ACL:'public-read', | ||
21 | + Body:null | ||
22 | +}; | ||
23 | +var S3DOWNparam = { | ||
24 | + Bucket:BUCKET, | ||
25 | + Prefix:null | ||
26 | +}; | ||
27 | + | ||
28 | +const multer = require('multer'); | ||
29 | + | ||
30 | +// tensorflow의 data folder에 저장할 때 multer 설정 | ||
31 | +var storage_data = multer.diskStorage({ | ||
32 | + destination: function (req, file, cb) { | ||
33 | + var dir = req.params.directoryName; | ||
34 | + cb(null, dataFolder + dir + '/'); | ||
35 | + }, | ||
36 | + filename: function (req, file, cb) { | ||
37 | + cb(null, new Date().valueOf() + "_" + file.originalname); | ||
38 | + } | ||
39 | +}); | ||
40 | +var upload_data = multer({ | ||
41 | + storage: storage_data | ||
42 | +}); | ||
43 | + | ||
44 | +// tensorflow의 test folder에 저장할 때 multer 설정 | ||
45 | +var storage_test = multer.diskStorage({ | ||
46 | + destination: function (req, file, cb) { | ||
47 | + var dir = req.params.directoryName; | ||
48 | + cb(null, testFolder); | ||
49 | + }, | ||
50 | + filename: function (req, file, cb) { | ||
51 | + cb(null, "test.jpg"); | ||
52 | + } | ||
53 | +}); | ||
54 | +var upload_test = multer({ | ||
55 | + storage: storage_test | ||
56 | +}); | ||
57 | + | ||
58 | +const bodyParser = require('body-parser'); | ||
59 | +const fs = require('fs'); | ||
60 | +const path = require('path'); | ||
61 | +const pyShell = require('python-shell'); | ||
62 | + | ||
63 | +const PORT = 8080; | ||
64 | +const HOST = '0.0.0.0'; | ||
65 | + | ||
66 | +const app = express(); | ||
67 | + | ||
68 | +const dataFolder = './tensorflow/data/'; | ||
69 | +const testFolder = './tensorflow/test/'; | ||
70 | + | ||
71 | + | ||
72 | +app.set('view engine', 'pug'); | ||
73 | +app.set('views', './views'); | ||
74 | +app.locals.pretty = true | ||
75 | + | ||
76 | +app.use(bodyParser.urlencoded({extended:false})); | ||
77 | + | ||
78 | + | ||
79 | +// tensorflow 학습을 완료한 시간 | ||
80 | +var LearnTime = undefined; | ||
81 | + | ||
82 | + | ||
83 | +// Redirect Root to Home | ||
84 | +app.get('/', (req, res) => { | ||
85 | + res.redirect('./home/'); | ||
86 | +}); | ||
87 | + | ||
88 | + | ||
89 | +// Main Page | ||
90 | +app.get('/home/', (req, res) => { | ||
91 | + // data 폴더 목록을 읽어서 home 화면에 넘겨줌 | ||
92 | + fs.readdir(dataFolder, function(error, filelist){ | ||
93 | + if(error) | ||
94 | + console.log(error); | ||
95 | + res.render('home', {fileList:filelist, learntime:LearnTime}); | ||
96 | + }); | ||
97 | +}); | ||
98 | + | ||
99 | + | ||
100 | +// Directory existence checking | ||
101 | +app.post('/directory_check', (req, res) => { | ||
102 | + | ||
103 | + var dir = req.body.directoryName; // 입력받은 새로운 directory name | ||
104 | + | ||
105 | + // Directory exists | ||
106 | + if(fs.existsSync(dataFolder + dir)) | ||
107 | + { | ||
108 | + // Go back page | ||
109 | + res.render('error_directoryAdd'); | ||
110 | + } | ||
111 | + // Directory doesn't exist | ||
112 | + else | ||
113 | + { | ||
114 | + // Make directory | ||
115 | + fs.mkdirSync(dataFolder + dir); | ||
116 | + console.log('Directory Create: ' + dir); | ||
117 | + res.redirect('/home/' + dir + '/'); | ||
118 | + } | ||
119 | +}); | ||
120 | + | ||
121 | + | ||
122 | +// Basic Directory Page | ||
123 | +app.get('/home/:directoryName/', (req, res) => { | ||
124 | + | ||
125 | + // 임시로 이미지 파일명 목록을 보여주는 코드 | ||
126 | + var directoryName = req.params.directoryName; // 접속한 directory name | ||
127 | + var filelist = new Array(); | ||
128 | + var imagelist = new Array(); | ||
129 | + | ||
130 | + // read directory's file list | ||
131 | + fs.readdirSync(dataFolder + directoryName).forEach(function(file, index){ | ||
132 | + // 확장자 추출 및 이미지 파일인지 체크 | ||
133 | + var fileType = path.extname(file); | ||
134 | + if(fileType == ".jpg" || fileType == ".jpeg") { | ||
135 | + filelist.push(file); | ||
136 | + } | ||
137 | + }); | ||
138 | + S3DOWNparam.Prefix = directoryName + '/'; | ||
139 | + s3.listObjects(S3DOWNparam, function (err, data) { | ||
140 | + if (err) | ||
141 | + console.log(err); | ||
142 | + else { | ||
143 | + data.Contents.forEach(function(image) { | ||
144 | + imagelist.push('https://' + BUCKET + '.s3.amazonaws.com/' + image.Key.replace(' ', '+')); | ||
145 | + }); | ||
146 | + } | ||
147 | + res.render('directory', {directoryName:directoryName, fileList:filelist, imageList:imagelist}); | ||
148 | + }); | ||
149 | +}); | ||
150 | + | ||
151 | + | ||
152 | +// Image Upload Directory Page | ||
153 | +app.post('/home/:directoryName/upload/', upload_data.array('userImage'), (req, res) => { | ||
154 | + var directoryName = req.params.directoryName; | ||
155 | + | ||
156 | + req.files.forEach(function(file) { | ||
157 | + S3UPparam.Key = directoryName + '/' + new Date().valueOf() + "_" + file.originalname; | ||
158 | + S3UPparam.Body = fs.createReadStream(file.path); | ||
159 | + s3.upload(S3UPparam, function (err, result) { | ||
160 | + if (err) | ||
161 | + console.log(err); | ||
162 | + }); | ||
163 | + }); | ||
164 | + | ||
165 | + res.redirect('/home/' + directoryName + '/'); | ||
166 | +}); | ||
167 | + | ||
168 | + | ||
169 | +// Modify Directory name | ||
170 | +app.get('/home/:directoryName/modify/', (req, res) => { | ||
171 | + var directoryName = req.params.directoryName; // 본래 directory 이름 | ||
172 | + var newName = req.query.newName; // 입력받은 수정할 이름 | ||
173 | + | ||
174 | + // exist query.newName | ||
175 | + if (req.query.newName) { | ||
176 | + // modify Directory name | ||
177 | + var Path = dataFolder + directoryName; | ||
178 | + fs.rename(Path, dataFolder + newName, function (err) { | ||
179 | + if (err) { | ||
180 | + console.log("Directory Rename error: " + err); | ||
181 | + } else { | ||
182 | + console.log("Directory Rename: " + directoryName + " -> " + newName); | ||
183 | + } | ||
184 | + }); | ||
185 | + s3.listObjects({Bucket:BUCKET, Prefix: directoryName + '/'}, function(err, data) { | ||
186 | + if (data.Contents.length) { | ||
187 | + async.each(data.Contents, function(file, cb) { | ||
188 | + s3.copyObject({ | ||
189 | + Bucket: BUCKET, | ||
190 | + CopySource: BUCKET + '/' + file.Key, | ||
191 | + Key: file.Key.replace(directoryName, newName) | ||
192 | + }) | ||
193 | + .promise() | ||
194 | + .then(() => | ||
195 | + s3.deleteObject({ | ||
196 | + Bucket: BUCKET, | ||
197 | + Key: file.Key | ||
198 | + }).promise() | ||
199 | + ).catch ((e) => console.error(e)); | ||
200 | + }); | ||
201 | + } | ||
202 | + s3.deleteObject({ | ||
203 | + Bucket: BUCKET, | ||
204 | + Key: directoryName | ||
205 | + }, function(err, data) { | ||
206 | + if (err) console.log(err); | ||
207 | + }); | ||
208 | + }); | ||
209 | + res.redirect('/'); | ||
210 | + } | ||
211 | + else { | ||
212 | + res.render('directoryModifyCheck', {directoryName:JSON.stringify(directoryName)}); | ||
213 | + } | ||
214 | +}); | ||
215 | + | ||
216 | + | ||
217 | +// Delete Directory | ||
218 | +app.get('/home/:directoryName/delete/', (req, res) => { | ||
219 | + var directoryName = req.params.directoryName; | ||
220 | + // exist query.real | ||
221 | + if (req.query.real) { | ||
222 | + // Remove Directory and Files | ||
223 | + var path = dataFolder + directoryName; | ||
224 | + fs.readdirSync(path).forEach(function(file,index){ | ||
225 | + var curPath = path + "/" + file; | ||
226 | + fs.unlinkSync(curPath); | ||
227 | + }); | ||
228 | + fs.rmdirSync(path); | ||
229 | + // Remove S3 Directory and Files | ||
230 | + s3.listObjects({Bucket:BUCKET, Prefix: directoryName + '/'}, function(err, data) { | ||
231 | + if (data.Contents.length) { | ||
232 | + async.each(data.Contents, function(file, cb) { | ||
233 | + s3.deleteObject({ | ||
234 | + Bucket: BUCKET, | ||
235 | + Key: file.Key | ||
236 | + }, function(err, data) { | ||
237 | + if (err) console.log(err); | ||
238 | + }); | ||
239 | + }); | ||
240 | + } | ||
241 | + }); | ||
242 | + | ||
243 | + console.log('Directory Delete: ' + directoryName); | ||
244 | + res.redirect('/'); | ||
245 | + } | ||
246 | + else { | ||
247 | + res.render('directoryDeleteCheck', {directoryName:JSON.stringify(directoryName)}); | ||
248 | + } | ||
249 | +}); | ||
250 | + | ||
251 | + | ||
252 | +// Image Test Page | ||
253 | +app.post('/test', upload_test.single('TestImage'), (req, res) => { | ||
254 | + var results = new Array(); | ||
255 | + | ||
256 | + console.log("Test Start"); | ||
257 | + process.chdir('./tensorflow/'); // move working directory | ||
258 | + | ||
259 | + // retrain_run_inference.py로 test | ||
260 | + pyShell.PythonShell.run('retrain_run_inference.py', null, function (err, result) { | ||
261 | + if (err) { | ||
262 | + console.log('Test error: '+ err); | ||
263 | + } | ||
264 | + else { | ||
265 | + var strArr = result.toString().split('\n'); // result를 줄바꿈 기준으로 자름 | ||
266 | + // 결과만 results에 저장 | ||
267 | + strArr.forEach(function(str) { | ||
268 | + if (str.indexOf('score') > -1) { | ||
269 | + str = str.split('\\n').join(''); | ||
270 | + results = str.split(','); | ||
271 | + for(var i = 0; i < results.length; i++){ | ||
272 | + results[i] = results[i].substring(1); | ||
273 | + } | ||
274 | + } | ||
275 | + }); | ||
276 | + } | ||
277 | + process.chdir('../'); // move working directory | ||
278 | + console.log(results); | ||
279 | + console.log("Test Complete"); | ||
280 | + res.render('testResult', {result:results}); | ||
281 | + }); | ||
282 | +}); | ||
283 | + | ||
284 | + | ||
285 | +// Image Learning | ||
286 | +app.get('/imageLearning', (req, res) => { | ||
287 | + var results = new Array(); | ||
288 | + var tmp = LearnTime; | ||
289 | + | ||
290 | + res.redirect('/home/'); | ||
291 | + | ||
292 | + console.log("Learning Start"); | ||
293 | + process.chdir('./tensorflow/'); // move working directory | ||
294 | + | ||
295 | + // retrain.py로 학습 | ||
296 | + LearnTime = "학습 중..."; | ||
297 | + pyShell.PythonShell.run('retrain.py', null, function (err, result) { | ||
298 | + if (err) { | ||
299 | + console.log('Learning error: '+ err); | ||
300 | + LearnTime = tmp + ', error: 다시 학습 버튼을 눌러주세요'; | ||
301 | + } | ||
302 | + else { | ||
303 | + LearnTime = moment().format('YYYY-MM-DD HH:mm:ss'); // 학습 완료한 시간 저장 | ||
304 | + console.log("Learning Complete"); | ||
305 | + } | ||
306 | + }); | ||
307 | + process.chdir('../'); // move working directory | ||
308 | +}); | ||
309 | + | ||
310 | + | ||
311 | +app.listen(PORT, HOST); | ||
312 | +console.log('Running on http://${HOST}:${POST}'); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
final/tensorflow/retrain.py
0 → 100644
1 | +# Copyright 2015 The TensorFlow Authors. All Rights Reserved. | ||
2 | +# | ||
3 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +# you may not use this file except in compliance with the License. | ||
5 | +# You may obtain a copy of the License at | ||
6 | +# | ||
7 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +# | ||
9 | +# Unless required by applicable law or agreed to in writing, software | ||
10 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +# See the License for the specific language governing permissions and | ||
13 | +# limitations under the License. | ||
14 | +# ============================================================================== | ||
15 | +"""Simple transfer learning with an Inception v3 architecture model which | ||
16 | +displays summaries in TensorBoard. | ||
17 | + | ||
18 | +This example shows how to take a Inception v3 architecture model trained on | ||
19 | +ImageNet images, and train a new top layer that can recognize other classes of | ||
20 | +images. | ||
21 | + | ||
22 | +The top layer receives as input a 2048-dimensional vector for each image. We | ||
23 | +train a softmax layer on top of this representation. Assuming the softmax layer | ||
24 | +contains N labels, this corresponds to learning N + 2048*N model parameters | ||
25 | +corresponding to the learned biases and weights. | ||
26 | + | ||
27 | +Here's an example, which assumes you have a folder containing class-named | ||
28 | +subfolders, each full of images for each label. The example folder flower_photos | ||
29 | +should have a structure like this: | ||
30 | + | ||
31 | +~/flower_photos/daisy/photo1.jpg | ||
32 | +~/flower_photos/daisy/photo2.jpg | ||
33 | +... | ||
34 | +~/flower_photos/rose/anotherphoto77.jpg | ||
35 | +... | ||
36 | +~/flower_photos/sunflower/somepicture.jpg | ||
37 | + | ||
38 | +The subfolder names are important, since they define what label is applied to | ||
39 | +each image, but the filenames themselves don't matter. Once your images are | ||
40 | +prepared, you can run the training with a command like this: | ||
41 | + | ||
42 | +bazel build tensorflow/examples/image_retraining:retrain && \ | ||
43 | +bazel-bin/tensorflow/examples/image_retraining/retrain \ | ||
44 | +--image_dir ~/flower_photos | ||
45 | + | ||
46 | +You can replace the image_dir argument with any folder containing subfolders of | ||
47 | +images. The label for each image is taken from the name of the subfolder it's | ||
48 | +in. | ||
49 | + | ||
50 | +This produces a new model file that can be loaded and run by any TensorFlow | ||
51 | +program, for example the label_image sample code. | ||
52 | + | ||
53 | + | ||
54 | +To use with TensorBoard: | ||
55 | + | ||
56 | +By default, this script will log summaries to /tmp/retrain_logs directory | ||
57 | + | ||
58 | +Visualize the summaries with this command: | ||
59 | + | ||
60 | +tensorboard --logdir /tmp/retrain_logs | ||
61 | + | ||
62 | +""" | ||
63 | +from __future__ import absolute_import | ||
64 | +from __future__ import division | ||
65 | +from __future__ import print_function | ||
66 | + | ||
67 | +import argparse | ||
68 | +from datetime import datetime | ||
69 | +import hashlib | ||
70 | +import os.path | ||
71 | +import random | ||
72 | +import re | ||
73 | +import struct | ||
74 | +import sys | ||
75 | +import tarfile | ||
76 | + | ||
77 | +import numpy as np | ||
78 | +from six.moves import urllib | ||
79 | +import tensorflow as tf | ||
80 | + | ||
81 | +from tensorflow.python.framework import graph_util | ||
82 | +from tensorflow.python.framework import tensor_shape | ||
83 | +from tensorflow.python.platform import gfile | ||
84 | +from tensorflow.python.util import compat | ||
85 | + | ||
86 | +FLAGS = None | ||
87 | + | ||
88 | +# These are all parameters that are tied to the particular model architecture | ||
89 | +# we're using for Inception v3. These include things like tensor names and their | ||
90 | +# sizes. If you want to adapt this script to work with another model, you will | ||
91 | +# need to update these to reflect the values in the network you're using. | ||
92 | +# pylint: disable=line-too-long | ||
93 | +DATA_URL = 'http://download.tensorflow.org/models/image/imagenet/inception-2015-12-05.tgz' | ||
94 | +# pylint: enable=line-too-long | ||
95 | +BOTTLENECK_TENSOR_NAME = 'pool_3/_reshape:0' | ||
96 | +BOTTLENECK_TENSOR_SIZE = 2048 | ||
97 | +MODEL_INPUT_WIDTH = 299 | ||
98 | +MODEL_INPUT_HEIGHT = 299 | ||
99 | +MODEL_INPUT_DEPTH = 3 | ||
100 | +JPEG_DATA_TENSOR_NAME = 'DecodeJpeg/contents:0' | ||
101 | +RESIZED_INPUT_TENSOR_NAME = 'ResizeBilinear:0' | ||
102 | +MAX_NUM_IMAGES_PER_CLASS = 2 ** 27 - 1 # ~134M | ||
103 | + | ||
104 | + | ||
105 | +def create_image_lists(image_dir, testing_percentage, validation_percentage): | ||
106 | + """Builds a list of training images from the file system. | ||
107 | + | ||
108 | + Analyzes the sub folders in the image directory, splits them into stable | ||
109 | + training, testing, and validation sets, and returns a data structure | ||
110 | + describing the lists of images for each label and their paths. | ||
111 | + | ||
112 | + Args: | ||
113 | + image_dir: String path to a folder containing subfolders of images. | ||
114 | + testing_percentage: Integer percentage of the images to reserve for tests. | ||
115 | + validation_percentage: Integer percentage of images reserved for validation. | ||
116 | + | ||
117 | + Returns: | ||
118 | + A dictionary containing an entry for each label subfolder, with images split | ||
119 | + into training, testing, and validation sets within each label. | ||
120 | + """ | ||
121 | + if not gfile.Exists(image_dir): | ||
122 | + print("Image directory '" + image_dir + "' not found.") | ||
123 | + return None | ||
124 | + result = {} | ||
125 | + sub_dirs = [x[0] for x in gfile.Walk(image_dir)] | ||
126 | + # The root directory comes first, so skip it. | ||
127 | + is_root_dir = True | ||
128 | + for sub_dir in sub_dirs: | ||
129 | + if is_root_dir: | ||
130 | + is_root_dir = False | ||
131 | + continue | ||
132 | + extensions = ['jpg', 'jpeg', 'JPG', 'JPEG'] | ||
133 | + file_list = [] | ||
134 | + dir_name = os.path.basename(sub_dir) | ||
135 | + if dir_name == image_dir: | ||
136 | + continue | ||
137 | + print("Looking for images in '" + dir_name + "'") | ||
138 | + for extension in extensions: | ||
139 | + file_glob = os.path.join(image_dir, dir_name, '*.' + extension) | ||
140 | + file_list.extend(gfile.Glob(file_glob)) | ||
141 | + if not file_list: | ||
142 | + print('No files found') | ||
143 | + continue | ||
144 | + if len(file_list) < 20: | ||
145 | + print('WARNING: Folder has less than 20 images, which may cause issues.') | ||
146 | + elif len(file_list) > MAX_NUM_IMAGES_PER_CLASS: | ||
147 | + print('WARNING: Folder {} has more than {} images. Some images will ' | ||
148 | + 'never be selected.'.format(dir_name, MAX_NUM_IMAGES_PER_CLASS)) | ||
149 | + label_name = re.sub(r'[^a-z0-9]+', ' ', dir_name.lower()) | ||
150 | + training_images = [] | ||
151 | + testing_images = [] | ||
152 | + validation_images = [] | ||
153 | + for file_name in file_list: | ||
154 | + base_name = os.path.basename(file_name) | ||
155 | + # We want to ignore anything after '_nohash_' in the file name when | ||
156 | + # deciding which set to put an image in, the data set creator has a way of | ||
157 | + # grouping photos that are close variations of each other. For example | ||
158 | + # this is used in the plant disease data set to group multiple pictures of | ||
159 | + # the same leaf. | ||
160 | + hash_name = re.sub(r'_nohash_.*$', '', file_name) | ||
161 | + # This looks a bit magical, but we need to decide whether this file should | ||
162 | + # go into the training, testing, or validation sets, and we want to keep | ||
163 | + # existing files in the same set even if more files are subsequently | ||
164 | + # added. | ||
165 | + # To do that, we need a stable way of deciding based on just the file name | ||
166 | + # itself, so we do a hash of that and then use that to generate a | ||
167 | + # probability value that we use to assign it. | ||
168 | + hash_name_hashed = hashlib.sha1(compat.as_bytes(hash_name)).hexdigest() | ||
169 | + percentage_hash = ((int(hash_name_hashed, 16) % | ||
170 | + (MAX_NUM_IMAGES_PER_CLASS + 1)) * | ||
171 | + (100.0 / MAX_NUM_IMAGES_PER_CLASS)) | ||
172 | + if percentage_hash < validation_percentage: | ||
173 | + validation_images.append(base_name) | ||
174 | + elif percentage_hash < (testing_percentage + validation_percentage): | ||
175 | + testing_images.append(base_name) | ||
176 | + else: | ||
177 | + training_images.append(base_name) | ||
178 | + result[label_name] = { | ||
179 | + 'dir': dir_name, | ||
180 | + 'training': training_images, | ||
181 | + 'testing': testing_images, | ||
182 | + 'validation': validation_images, | ||
183 | + } | ||
184 | + return result | ||
185 | + | ||
186 | + | ||
187 | +def get_image_path(image_lists, label_name, index, image_dir, category): | ||
188 | + """"Returns a path to an image for a label at the given index. | ||
189 | + | ||
190 | + Args: | ||
191 | + image_lists: Dictionary of training images for each label. | ||
192 | + label_name: Label string we want to get an image for. | ||
193 | + index: Int offset of the image we want. This will be moduloed by the | ||
194 | + available number of images for the label, so it can be arbitrarily large. | ||
195 | + image_dir: Root folder string of the subfolders containing the training | ||
196 | + images. | ||
197 | + category: Name string of set to pull images from - training, testing, or | ||
198 | + validation. | ||
199 | + | ||
200 | + Returns: | ||
201 | + File system path string to an image that meets the requested parameters. | ||
202 | + | ||
203 | + """ | ||
204 | + if label_name not in image_lists: | ||
205 | + tf.logging.fatal('Label does not exist %s.', label_name) | ||
206 | + label_lists = image_lists[label_name] | ||
207 | + if category not in label_lists: | ||
208 | + tf.logging.fatal('Category does not exist %s.', category) | ||
209 | + category_list = label_lists[category] | ||
210 | + if not category_list: | ||
211 | + tf.logging.fatal('Label %s has no images in the category %s.', | ||
212 | + label_name, category) | ||
213 | + mod_index = index % len(category_list) | ||
214 | + base_name = category_list[mod_index] | ||
215 | + sub_dir = label_lists['dir'] | ||
216 | + full_path = os.path.join(image_dir, sub_dir, base_name) | ||
217 | + return full_path | ||
218 | + | ||
219 | + | ||
220 | +def get_bottleneck_path(image_lists, label_name, index, bottleneck_dir, | ||
221 | + category): | ||
222 | + """"Returns a path to a bottleneck file for a label at the given index. | ||
223 | + | ||
224 | + Args: | ||
225 | + image_lists: Dictionary of training images for each label. | ||
226 | + label_name: Label string we want to get an image for. | ||
227 | + index: Integer offset of the image we want. This will be moduloed by the | ||
228 | + available number of images for the label, so it can be arbitrarily large. | ||
229 | + bottleneck_dir: Folder string holding cached files of bottleneck values. | ||
230 | + category: Name string of set to pull images from - training, testing, or | ||
231 | + validation. | ||
232 | + | ||
233 | + Returns: | ||
234 | + File system path string to an image that meets the requested parameters. | ||
235 | + """ | ||
236 | + return get_image_path(image_lists, label_name, index, bottleneck_dir, | ||
237 | + category) + '.txt' | ||
238 | + | ||
239 | + | ||
240 | +def create_inception_graph(): | ||
241 | + """"Creates a graph from saved GraphDef file and returns a Graph object. | ||
242 | + | ||
243 | + Returns: | ||
244 | + Graph holding the trained Inception network, and various tensors we'll be | ||
245 | + manipulating. | ||
246 | + """ | ||
247 | + with tf.Session() as sess: | ||
248 | + model_filename = os.path.join( | ||
249 | + FLAGS.model_dir, 'classify_image_graph_def.pb') | ||
250 | + with gfile.FastGFile(model_filename, 'rb') as f: | ||
251 | + graph_def = tf.GraphDef() | ||
252 | + graph_def.ParseFromString(f.read()) | ||
253 | + bottleneck_tensor, jpeg_data_tensor, resized_input_tensor = ( | ||
254 | + tf.import_graph_def(graph_def, name='', return_elements=[ | ||
255 | + BOTTLENECK_TENSOR_NAME, JPEG_DATA_TENSOR_NAME, | ||
256 | + RESIZED_INPUT_TENSOR_NAME])) | ||
257 | + return sess.graph, bottleneck_tensor, jpeg_data_tensor, resized_input_tensor | ||
258 | + | ||
259 | + | ||
260 | +def run_bottleneck_on_image(sess, image_data, image_data_tensor, | ||
261 | + bottleneck_tensor): | ||
262 | + """Runs inference on an image to extract the 'bottleneck' summary layer. | ||
263 | + | ||
264 | + Args: | ||
265 | + sess: Current active TensorFlow Session. | ||
266 | + image_data: String of raw JPEG data. | ||
267 | + image_data_tensor: Input data layer in the graph. | ||
268 | + bottleneck_tensor: Layer before the final softmax. | ||
269 | + | ||
270 | + Returns: | ||
271 | + Numpy array of bottleneck values. | ||
272 | + """ | ||
273 | + bottleneck_values = sess.run( | ||
274 | + bottleneck_tensor, | ||
275 | + {image_data_tensor: image_data}) | ||
276 | + bottleneck_values = np.squeeze(bottleneck_values) | ||
277 | + return bottleneck_values | ||
278 | + | ||
279 | + | ||
280 | +def maybe_download_and_extract(): | ||
281 | + """Download and extract model tar file. | ||
282 | + | ||
283 | + If the pretrained model we're using doesn't already exist, this function | ||
284 | + downloads it from the TensorFlow.org website and unpacks it into a directory. | ||
285 | + """ | ||
286 | + dest_directory = FLAGS.model_dir | ||
287 | + if not os.path.exists(dest_directory): | ||
288 | + os.makedirs(dest_directory) | ||
289 | + filename = DATA_URL.split('/')[-1] | ||
290 | + filepath = os.path.join(dest_directory, filename) | ||
291 | + if not os.path.exists(filepath): | ||
292 | + | ||
293 | + def _progress(count, block_size, total_size): | ||
294 | + sys.stdout.write('\r>> Downloading %s %.1f%%' % | ||
295 | + (filename, | ||
296 | + float(count * block_size) / float(total_size) * 100.0)) | ||
297 | + sys.stdout.flush() | ||
298 | + | ||
299 | + filepath, _ = urllib.request.urlretrieve(DATA_URL, | ||
300 | + filepath, | ||
301 | + _progress) | ||
302 | + print() | ||
303 | + statinfo = os.stat(filepath) | ||
304 | + print('Successfully downloaded', filename, statinfo.st_size, 'bytes.') | ||
305 | + tarfile.open(filepath, 'r:gz').extractall(dest_directory) | ||
306 | + | ||
307 | + | ||
308 | +def ensure_dir_exists(dir_name): | ||
309 | + """Makes sure the folder exists on disk. | ||
310 | + | ||
311 | + Args: | ||
312 | + dir_name: Path string to the folder we want to create. | ||
313 | + """ | ||
314 | + if not os.path.exists(dir_name): | ||
315 | + os.makedirs(dir_name) | ||
316 | + | ||
317 | + | ||
318 | +def write_list_of_floats_to_file(list_of_floats , file_path): | ||
319 | + """Writes a given list of floats to a binary file. | ||
320 | + | ||
321 | + Args: | ||
322 | + list_of_floats: List of floats we want to write to a file. | ||
323 | + file_path: Path to a file where list of floats will be stored. | ||
324 | + | ||
325 | + """ | ||
326 | + | ||
327 | + s = struct.pack('d' * BOTTLENECK_TENSOR_SIZE, *list_of_floats) | ||
328 | + with open(file_path, 'wb') as f: | ||
329 | + f.write(s) | ||
330 | + | ||
331 | + | ||
332 | +def read_list_of_floats_from_file(file_path): | ||
333 | + """Reads list of floats from a given file. | ||
334 | + | ||
335 | + Args: | ||
336 | + file_path: Path to a file where list of floats was stored. | ||
337 | + Returns: | ||
338 | + Array of bottleneck values (list of floats). | ||
339 | + | ||
340 | + """ | ||
341 | + | ||
342 | + with open(file_path, 'rb') as f: | ||
343 | + s = struct.unpack('d' * BOTTLENECK_TENSOR_SIZE, f.read()) | ||
344 | + return list(s) | ||
345 | + | ||
346 | + | ||
347 | +bottleneck_path_2_bottleneck_values = {} | ||
348 | + | ||
349 | +def create_bottleneck_file(bottleneck_path, image_lists, label_name, index, | ||
350 | + image_dir, category, sess, jpeg_data_tensor, bottleneck_tensor): | ||
351 | + print('Creating bottleneck at ' + bottleneck_path) | ||
352 | + image_path = get_image_path(image_lists, label_name, index, image_dir, category) | ||
353 | + if not gfile.Exists(image_path): | ||
354 | + tf.logging.fatal('File does not exist %s', image_path) | ||
355 | + image_data = gfile.FastGFile(image_path, 'rb').read() | ||
356 | + bottleneck_values = run_bottleneck_on_image(sess, image_data, jpeg_data_tensor, bottleneck_tensor) | ||
357 | + bottleneck_string = ','.join(str(x) for x in bottleneck_values) | ||
358 | + with open(bottleneck_path, 'w') as bottleneck_file: | ||
359 | + bottleneck_file.write(bottleneck_string) | ||
360 | + | ||
361 | +def get_or_create_bottleneck(sess, image_lists, label_name, index, image_dir, | ||
362 | + category, bottleneck_dir, jpeg_data_tensor, | ||
363 | + bottleneck_tensor): | ||
364 | + """Retrieves or calculates bottleneck values for an image. | ||
365 | + | ||
366 | + If a cached version of the bottleneck data exists on-disk, return that, | ||
367 | + otherwise calculate the data and save it to disk for future use. | ||
368 | + | ||
369 | + Args: | ||
370 | + sess: The current active TensorFlow Session. | ||
371 | + image_lists: Dictionary of training images for each label. | ||
372 | + label_name: Label string we want to get an image for. | ||
373 | + index: Integer offset of the image we want. This will be modulo-ed by the | ||
374 | + available number of images for the label, so it can be arbitrarily large. | ||
375 | + image_dir: Root folder string of the subfolders containing the training | ||
376 | + images. | ||
377 | + category: Name string of which set to pull images from - training, testing, | ||
378 | + or validation. | ||
379 | + bottleneck_dir: Folder string holding cached files of bottleneck values. | ||
380 | + jpeg_data_tensor: The tensor to feed loaded jpeg data into. | ||
381 | + bottleneck_tensor: The output tensor for the bottleneck values. | ||
382 | + | ||
383 | + Returns: | ||
384 | + Numpy array of values produced by the bottleneck layer for the image. | ||
385 | + """ | ||
386 | + label_lists = image_lists[label_name] | ||
387 | + sub_dir = label_lists['dir'] | ||
388 | + sub_dir_path = os.path.join(bottleneck_dir, sub_dir) | ||
389 | + ensure_dir_exists(sub_dir_path) | ||
390 | + bottleneck_path = get_bottleneck_path(image_lists, label_name, index, bottleneck_dir, category) | ||
391 | + if not os.path.exists(bottleneck_path): | ||
392 | + create_bottleneck_file(bottleneck_path, image_lists, label_name, index, image_dir, category, sess, jpeg_data_tensor, bottleneck_tensor) | ||
393 | + with open(bottleneck_path, 'r') as bottleneck_file: | ||
394 | + bottleneck_string = bottleneck_file.read() | ||
395 | + did_hit_error = False | ||
396 | + try: | ||
397 | + bottleneck_values = [float(x) for x in bottleneck_string.split(',')] | ||
398 | + except: | ||
399 | + print("Invalid float found, recreating bottleneck") | ||
400 | + did_hit_error = True | ||
401 | + if did_hit_error: | ||
402 | + create_bottleneck_file(bottleneck_path, image_lists, label_name, index, image_dir, category, sess, jpeg_data_tensor, bottleneck_tensor) | ||
403 | + with open(bottleneck_path, 'r') as bottleneck_file: | ||
404 | + bottleneck_string = bottleneck_file.read() | ||
405 | + # Allow exceptions to propagate here, since they shouldn't happen after a fresh creation | ||
406 | + bottleneck_values = [float(x) for x in bottleneck_string.split(',')] | ||
407 | + return bottleneck_values | ||
408 | + | ||
409 | +def cache_bottlenecks(sess, image_lists, image_dir, bottleneck_dir, | ||
410 | + jpeg_data_tensor, bottleneck_tensor): | ||
411 | + """Ensures all the training, testing, and validation bottlenecks are cached. | ||
412 | + | ||
413 | + Because we're likely to read the same image multiple times (if there are no | ||
414 | + distortions applied during training) it can speed things up a lot if we | ||
415 | + calculate the bottleneck layer values once for each image during | ||
416 | + preprocessing, and then just read those cached values repeatedly during | ||
417 | + training. Here we go through all the images we've found, calculate those | ||
418 | + values, and save them off. | ||
419 | + | ||
420 | + Args: | ||
421 | + sess: The current active TensorFlow Session. | ||
422 | + image_lists: Dictionary of training images for each label. | ||
423 | + image_dir: Root folder string of the subfolders containing the training | ||
424 | + images. | ||
425 | + bottleneck_dir: Folder string holding cached files of bottleneck values. | ||
426 | + jpeg_data_tensor: Input tensor for jpeg data from file. | ||
427 | + bottleneck_tensor: The penultimate output layer of the graph. | ||
428 | + | ||
429 | + Returns: | ||
430 | + Nothing. | ||
431 | + """ | ||
432 | + how_many_bottlenecks = 0 | ||
433 | + ensure_dir_exists(bottleneck_dir) | ||
434 | + for label_name, label_lists in image_lists.items(): | ||
435 | + for category in ['training', 'testing', 'validation']: | ||
436 | + category_list = label_lists[category] | ||
437 | + for index, unused_base_name in enumerate(category_list): | ||
438 | + get_or_create_bottleneck(sess, image_lists, label_name, index, | ||
439 | + image_dir, category, bottleneck_dir, | ||
440 | + jpeg_data_tensor, bottleneck_tensor) | ||
441 | + | ||
442 | + how_many_bottlenecks += 1 | ||
443 | + if how_many_bottlenecks % 100 == 0: | ||
444 | + print(str(how_many_bottlenecks) + ' bottleneck files created.') | ||
445 | + | ||
446 | + | ||
447 | +def get_random_cached_bottlenecks(sess, image_lists, how_many, category, | ||
448 | + bottleneck_dir, image_dir, jpeg_data_tensor, | ||
449 | + bottleneck_tensor): | ||
450 | + """Retrieves bottleneck values for cached images. | ||
451 | + | ||
452 | + If no distortions are being applied, this function can retrieve the cached | ||
453 | + bottleneck values directly from disk for images. It picks a random set of | ||
454 | + images from the specified category. | ||
455 | + | ||
456 | + Args: | ||
457 | + sess: Current TensorFlow Session. | ||
458 | + image_lists: Dictionary of training images for each label. | ||
459 | + how_many: If positive, a random sample of this size will be chosen. | ||
460 | + If negative, all bottlenecks will be retrieved. | ||
461 | + category: Name string of which set to pull from - training, testing, or | ||
462 | + validation. | ||
463 | + bottleneck_dir: Folder string holding cached files of bottleneck values. | ||
464 | + image_dir: Root folder string of the subfolders containing the training | ||
465 | + images. | ||
466 | + jpeg_data_tensor: The layer to feed jpeg image data into. | ||
467 | + bottleneck_tensor: The bottleneck output layer of the CNN graph. | ||
468 | + | ||
469 | + Returns: | ||
470 | + List of bottleneck arrays, their corresponding ground truths, and the | ||
471 | + relevant filenames. | ||
472 | + """ | ||
473 | + class_count = len(image_lists.keys()) | ||
474 | + bottlenecks = [] | ||
475 | + ground_truths = [] | ||
476 | + filenames = [] | ||
477 | + if how_many >= 0: | ||
478 | + # Retrieve a random sample of bottlenecks. | ||
479 | + for unused_i in range(how_many): | ||
480 | + label_index = random.randrange(class_count) | ||
481 | + label_name = list(image_lists.keys())[label_index] | ||
482 | + image_index = random.randrange(MAX_NUM_IMAGES_PER_CLASS + 1) | ||
483 | + image_name = get_image_path(image_lists, label_name, image_index, | ||
484 | + image_dir, category) | ||
485 | + bottleneck = get_or_create_bottleneck(sess, image_lists, label_name, | ||
486 | + image_index, image_dir, category, | ||
487 | + bottleneck_dir, jpeg_data_tensor, | ||
488 | + bottleneck_tensor) | ||
489 | + ground_truth = np.zeros(class_count, dtype=np.float32) | ||
490 | + ground_truth[label_index] = 1.0 | ||
491 | + bottlenecks.append(bottleneck) | ||
492 | + ground_truths.append(ground_truth) | ||
493 | + filenames.append(image_name) | ||
494 | + else: | ||
495 | + # Retrieve all bottlenecks. | ||
496 | + for label_index, label_name in enumerate(image_lists.keys()): | ||
497 | + for image_index, image_name in enumerate( | ||
498 | + image_lists[label_name][category]): | ||
499 | + image_name = get_image_path(image_lists, label_name, image_index, | ||
500 | + image_dir, category) | ||
501 | + bottleneck = get_or_create_bottleneck(sess, image_lists, label_name, | ||
502 | + image_index, image_dir, category, | ||
503 | + bottleneck_dir, jpeg_data_tensor, | ||
504 | + bottleneck_tensor) | ||
505 | + ground_truth = np.zeros(class_count, dtype=np.float32) | ||
506 | + ground_truth[label_index] = 1.0 | ||
507 | + bottlenecks.append(bottleneck) | ||
508 | + ground_truths.append(ground_truth) | ||
509 | + filenames.append(image_name) | ||
510 | + return bottlenecks, ground_truths, filenames | ||
511 | + | ||
512 | + | ||
513 | +def get_random_distorted_bottlenecks( | ||
514 | + sess, image_lists, how_many, category, image_dir, input_jpeg_tensor, | ||
515 | + distorted_image, resized_input_tensor, bottleneck_tensor): | ||
516 | + """Retrieves bottleneck values for training images, after distortions. | ||
517 | + | ||
518 | + If we're training with distortions like crops, scales, or flips, we have to | ||
519 | + recalculate the full model for every image, and so we can't use cached | ||
520 | + bottleneck values. Instead we find random images for the requested category, | ||
521 | + run them through the distortion graph, and then the full graph to get the | ||
522 | + bottleneck results for each. | ||
523 | + | ||
524 | + Args: | ||
525 | + sess: Current TensorFlow Session. | ||
526 | + image_lists: Dictionary of training images for each label. | ||
527 | + how_many: The integer number of bottleneck values to return. | ||
528 | + category: Name string of which set of images to fetch - training, testing, | ||
529 | + or validation. | ||
530 | + image_dir: Root folder string of the subfolders containing the training | ||
531 | + images. | ||
532 | + input_jpeg_tensor: The input layer we feed the image data to. | ||
533 | + distorted_image: The output node of the distortion graph. | ||
534 | + resized_input_tensor: The input node of the recognition graph. | ||
535 | + bottleneck_tensor: The bottleneck output layer of the CNN graph. | ||
536 | + | ||
537 | + Returns: | ||
538 | + List of bottleneck arrays and their corresponding ground truths. | ||
539 | + """ | ||
540 | + class_count = len(image_lists.keys()) | ||
541 | + bottlenecks = [] | ||
542 | + ground_truths = [] | ||
543 | + for unused_i in range(how_many): | ||
544 | + label_index = random.randrange(class_count) | ||
545 | + label_name = list(image_lists.keys())[label_index] | ||
546 | + image_index = random.randrange(MAX_NUM_IMAGES_PER_CLASS + 1) | ||
547 | + image_path = get_image_path(image_lists, label_name, image_index, image_dir, | ||
548 | + category) | ||
549 | + if not gfile.Exists(image_path): | ||
550 | + tf.logging.fatal('File does not exist %s', image_path) | ||
551 | + jpeg_data = gfile.FastGFile(image_path, 'rb').read() | ||
552 | + # Note that we materialize the distorted_image_data as a numpy array before | ||
553 | + # sending running inference on the image. This involves 2 memory copies and | ||
554 | + # might be optimized in other implementations. | ||
555 | + distorted_image_data = sess.run(distorted_image, | ||
556 | + {input_jpeg_tensor: jpeg_data}) | ||
557 | + bottleneck = run_bottleneck_on_image(sess, distorted_image_data, | ||
558 | + resized_input_tensor, | ||
559 | + bottleneck_tensor) | ||
560 | + ground_truth = np.zeros(class_count, dtype=np.float32) | ||
561 | + ground_truth[label_index] = 1.0 | ||
562 | + bottlenecks.append(bottleneck) | ||
563 | + ground_truths.append(ground_truth) | ||
564 | + return bottlenecks, ground_truths | ||
565 | + | ||
566 | + | ||
567 | +def should_distort_images(flip_left_right, random_crop, random_scale, | ||
568 | + random_brightness): | ||
569 | + """Whether any distortions are enabled, from the input flags. | ||
570 | + | ||
571 | + Args: | ||
572 | + flip_left_right: Boolean whether to randomly mirror images horizontally. | ||
573 | + random_crop: Integer percentage setting the total margin used around the | ||
574 | + crop box. | ||
575 | + random_scale: Integer percentage of how much to vary the scale by. | ||
576 | + random_brightness: Integer range to randomly multiply the pixel values by. | ||
577 | + | ||
578 | + Returns: | ||
579 | + Boolean value indicating whether any distortions should be applied. | ||
580 | + """ | ||
581 | + return (flip_left_right or (random_crop != 0) or (random_scale != 0) or | ||
582 | + (random_brightness != 0)) | ||
583 | + | ||
584 | + | ||
585 | +def add_input_distortions(flip_left_right, random_crop, random_scale, | ||
586 | + random_brightness): | ||
587 | + """Creates the operations to apply the specified distortions. | ||
588 | + | ||
589 | + During training it can help to improve the results if we run the images | ||
590 | + through simple distortions like crops, scales, and flips. These reflect the | ||
591 | + kind of variations we expect in the real world, and so can help train the | ||
592 | + model to cope with natural data more effectively. Here we take the supplied | ||
593 | + parameters and construct a network of operations to apply them to an image. | ||
594 | + | ||
595 | + Cropping | ||
596 | + ~~~~~~~~ | ||
597 | + | ||
598 | + Cropping is done by placing a bounding box at a random position in the full | ||
599 | + image. The cropping parameter controls the size of that box relative to the | ||
600 | + input image. If it's zero, then the box is the same size as the input and no | ||
601 | + cropping is performed. If the value is 50%, then the crop box will be half the | ||
602 | + width and height of the input. In a diagram it looks like this: | ||
603 | + | ||
604 | + < width > | ||
605 | + +---------------------+ | ||
606 | + | | | ||
607 | + | width - crop% | | ||
608 | + | < > | | ||
609 | + | +------+ | | ||
610 | + | | | | | ||
611 | + | | | | | ||
612 | + | | | | | ||
613 | + | +------+ | | ||
614 | + | | | ||
615 | + | | | ||
616 | + +---------------------+ | ||
617 | + | ||
618 | + Scaling | ||
619 | + ~~~~~~~ | ||
620 | + | ||
621 | + Scaling is a lot like cropping, except that the bounding box is always | ||
622 | + centered and its size varies randomly within the given range. For example if | ||
623 | + the scale percentage is zero, then the bounding box is the same size as the | ||
624 | + input and no scaling is applied. If it's 50%, then the bounding box will be in | ||
625 | + a random range between half the width and height and full size. | ||
626 | + | ||
627 | + Args: | ||
628 | + flip_left_right: Boolean whether to randomly mirror images horizontally. | ||
629 | + random_crop: Integer percentage setting the total margin used around the | ||
630 | + crop box. | ||
631 | + random_scale: Integer percentage of how much to vary the scale by. | ||
632 | + random_brightness: Integer range to randomly multiply the pixel values by. | ||
633 | + graph. | ||
634 | + | ||
635 | + Returns: | ||
636 | + The jpeg input layer and the distorted result tensor. | ||
637 | + """ | ||
638 | + | ||
639 | + jpeg_data = tf.placeholder(tf.string, name='DistortJPGInput') | ||
640 | + decoded_image = tf.image.decode_jpeg(jpeg_data, channels=MODEL_INPUT_DEPTH) | ||
641 | + decoded_image_as_float = tf.cast(decoded_image, dtype=tf.float32) | ||
642 | + decoded_image_4d = tf.expand_dims(decoded_image_as_float, 0) | ||
643 | + margin_scale = 1.0 + (random_crop / 100.0) | ||
644 | + resize_scale = 1.0 + (random_scale / 100.0) | ||
645 | + margin_scale_value = tf.constant(margin_scale) | ||
646 | + resize_scale_value = tf.random_uniform(tensor_shape.scalar(), | ||
647 | + minval=1.0, | ||
648 | + maxval=resize_scale) | ||
649 | + scale_value = tf.multiply(margin_scale_value, resize_scale_value) | ||
650 | + precrop_width = tf.multiply(scale_value, MODEL_INPUT_WIDTH) | ||
651 | + precrop_height = tf.multiply(scale_value, MODEL_INPUT_HEIGHT) | ||
652 | + precrop_shape = tf.stack([precrop_height, precrop_width]) | ||
653 | + precrop_shape_as_int = tf.cast(precrop_shape, dtype=tf.int32) | ||
654 | + precropped_image = tf.image.resize_bilinear(decoded_image_4d, | ||
655 | + precrop_shape_as_int) | ||
656 | + precropped_image_3d = tf.squeeze(precropped_image, squeeze_dims=[0]) | ||
657 | + cropped_image = tf.random_crop(precropped_image_3d, | ||
658 | + [MODEL_INPUT_HEIGHT, MODEL_INPUT_WIDTH, | ||
659 | + MODEL_INPUT_DEPTH]) | ||
660 | + if flip_left_right: | ||
661 | + flipped_image = tf.image.random_flip_left_right(cropped_image) | ||
662 | + else: | ||
663 | + flipped_image = cropped_image | ||
664 | + brightness_min = 1.0 - (random_brightness / 100.0) | ||
665 | + brightness_max = 1.0 + (random_brightness / 100.0) | ||
666 | + brightness_value = tf.random_uniform(tensor_shape.scalar(), | ||
667 | + minval=brightness_min, | ||
668 | + maxval=brightness_max) | ||
669 | + brightened_image = tf.multiply(flipped_image, brightness_value) | ||
670 | + distort_result = tf.expand_dims(brightened_image, 0, name='DistortResult') | ||
671 | + return jpeg_data, distort_result | ||
672 | + | ||
673 | + | ||
674 | +def variable_summaries(var): | ||
675 | + """Attach a lot of summaries to a Tensor (for TensorBoard visualization).""" | ||
676 | + with tf.name_scope('summaries'): | ||
677 | + mean = tf.reduce_mean(var) | ||
678 | + tf.summary.scalar('mean', mean) | ||
679 | + with tf.name_scope('stddev'): | ||
680 | + stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean))) | ||
681 | + tf.summary.scalar('stddev', stddev) | ||
682 | + tf.summary.scalar('max', tf.reduce_max(var)) | ||
683 | + tf.summary.scalar('min', tf.reduce_min(var)) | ||
684 | + tf.summary.histogram('histogram', var) | ||
685 | + | ||
686 | + | ||
687 | +def add_final_training_ops(class_count, final_tensor_name, bottleneck_tensor): | ||
688 | + """Adds a new softmax and fully-connected layer for training. | ||
689 | + | ||
690 | + We need to retrain the top layer to identify our new classes, so this function | ||
691 | + adds the right operations to the graph, along with some variables to hold the | ||
692 | + weights, and then sets up all the gradients for the backward pass. | ||
693 | + | ||
694 | + The set up for the softmax and fully-connected layers is based on: | ||
695 | + https://tensorflow.org/versions/master/tutorials/mnist/beginners/index.html | ||
696 | + | ||
697 | + Args: | ||
698 | + class_count: Integer of how many categories of things we're trying to | ||
699 | + recognize. | ||
700 | + final_tensor_name: Name string for the new final node that produces results. | ||
701 | + bottleneck_tensor: The output of the main CNN graph. | ||
702 | + | ||
703 | + Returns: | ||
704 | + The tensors for the training and cross entropy results, and tensors for the | ||
705 | + bottleneck input and ground truth input. | ||
706 | + """ | ||
707 | + with tf.name_scope('input'): | ||
708 | + bottleneck_input = tf.placeholder_with_default( | ||
709 | + bottleneck_tensor, shape=[None, BOTTLENECK_TENSOR_SIZE], | ||
710 | + name='BottleneckInputPlaceholder') | ||
711 | + | ||
712 | + ground_truth_input = tf.placeholder(tf.float32, | ||
713 | + [None, class_count], | ||
714 | + name='GroundTruthInput') | ||
715 | + | ||
716 | + # Organizing the following ops as `final_training_ops` so they're easier | ||
717 | + # to see in TensorBoard | ||
718 | + layer_name = 'final_training_ops' | ||
719 | + with tf.name_scope(layer_name): | ||
720 | + with tf.name_scope('weights'): | ||
721 | + layer_weights = tf.Variable(tf.truncated_normal([BOTTLENECK_TENSOR_SIZE, class_count], stddev=0.001), name='final_weights') | ||
722 | + variable_summaries(layer_weights) | ||
723 | + with tf.name_scope('biases'): | ||
724 | + layer_biases = tf.Variable(tf.zeros([class_count]), name='final_biases') | ||
725 | + variable_summaries(layer_biases) | ||
726 | + with tf.name_scope('Wx_plus_b'): | ||
727 | + logits = tf.matmul(bottleneck_input, layer_weights) + layer_biases | ||
728 | + tf.summary.histogram('pre_activations', logits) | ||
729 | + | ||
730 | + final_tensor = tf.nn.softmax(logits, name=final_tensor_name) | ||
731 | + tf.summary.histogram('activations', final_tensor) | ||
732 | + | ||
733 | + with tf.name_scope('cross_entropy'): | ||
734 | + cross_entropy = tf.nn.softmax_cross_entropy_with_logits( | ||
735 | + labels=ground_truth_input, logits=logits) | ||
736 | + with tf.name_scope('total'): | ||
737 | + cross_entropy_mean = tf.reduce_mean(cross_entropy) | ||
738 | + tf.summary.scalar('cross_entropy', cross_entropy_mean) | ||
739 | + | ||
740 | + with tf.name_scope('train'): | ||
741 | + train_step = tf.train.GradientDescentOptimizer(FLAGS.learning_rate).minimize( | ||
742 | + cross_entropy_mean) | ||
743 | + | ||
744 | + return (train_step, cross_entropy_mean, bottleneck_input, ground_truth_input, | ||
745 | + final_tensor) | ||
746 | + | ||
747 | + | ||
748 | +def add_evaluation_step(result_tensor, ground_truth_tensor): | ||
749 | + """Inserts the operations we need to evaluate the accuracy of our results. | ||
750 | + | ||
751 | + Args: | ||
752 | + result_tensor: The new final node that produces results. | ||
753 | + ground_truth_tensor: The node we feed ground truth data | ||
754 | + into. | ||
755 | + | ||
756 | + Returns: | ||
757 | + Tuple of (evaluation step, prediction). | ||
758 | + """ | ||
759 | + with tf.name_scope('accuracy'): | ||
760 | + with tf.name_scope('correct_prediction'): | ||
761 | + prediction = tf.argmax(result_tensor, 1) | ||
762 | + correct_prediction = tf.equal( | ||
763 | + prediction, tf.argmax(ground_truth_tensor, 1)) | ||
764 | + with tf.name_scope('accuracy'): | ||
765 | + evaluation_step = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) | ||
766 | + tf.summary.scalar('accuracy', evaluation_step) | ||
767 | + return evaluation_step, prediction | ||
768 | + | ||
769 | + | ||
770 | +def main(_): | ||
771 | + # Setup the directory we'll write summaries to for TensorBoard | ||
772 | + if tf.gfile.Exists(FLAGS.summaries_dir): | ||
773 | + tf.gfile.DeleteRecursively(FLAGS.summaries_dir) | ||
774 | + tf.gfile.MakeDirs(FLAGS.summaries_dir) | ||
775 | + | ||
776 | + # Set up the pre-trained graph. | ||
777 | + maybe_download_and_extract() | ||
778 | + graph, bottleneck_tensor, jpeg_data_tensor, resized_image_tensor = ( | ||
779 | + create_inception_graph()) | ||
780 | + | ||
781 | + # Look at the folder structure, and create lists of all the images. | ||
782 | + image_lists = create_image_lists(FLAGS.image_dir, FLAGS.testing_percentage, | ||
783 | + FLAGS.validation_percentage) | ||
784 | + class_count = len(image_lists.keys()) | ||
785 | + if class_count == 0: | ||
786 | + print('No valid folders of images found at ' + FLAGS.image_dir) | ||
787 | + return -1 | ||
788 | + if class_count == 1: | ||
789 | + print('Only one valid folder of images found at ' + FLAGS.image_dir + | ||
790 | + ' - multiple classes are needed for classification.') | ||
791 | + return -1 | ||
792 | + | ||
793 | + # See if the command-line flags mean we're applying any distortions. | ||
794 | + do_distort_images = should_distort_images( | ||
795 | + FLAGS.flip_left_right, FLAGS.random_crop, FLAGS.random_scale, | ||
796 | + FLAGS.random_brightness) | ||
797 | + sess = tf.Session() | ||
798 | + | ||
799 | + if do_distort_images: | ||
800 | + # We will be applying distortions, so setup the operations we'll need. | ||
801 | + distorted_jpeg_data_tensor, distorted_image_tensor = add_input_distortions( | ||
802 | + FLAGS.flip_left_right, FLAGS.random_crop, FLAGS.random_scale, | ||
803 | + FLAGS.random_brightness) | ||
804 | + else: | ||
805 | + # We'll make sure we've calculated the 'bottleneck' image summaries and | ||
806 | + # cached them on disk. | ||
807 | + cache_bottlenecks(sess, image_lists, FLAGS.image_dir, FLAGS.bottleneck_dir, | ||
808 | + jpeg_data_tensor, bottleneck_tensor) | ||
809 | + | ||
810 | + # Add the new layer that we'll be training. | ||
811 | + (train_step, cross_entropy, bottleneck_input, ground_truth_input, | ||
812 | + final_tensor) = add_final_training_ops(len(image_lists.keys()), | ||
813 | + FLAGS.final_tensor_name, | ||
814 | + bottleneck_tensor) | ||
815 | + | ||
816 | + # Create the operations we need to evaluate the accuracy of our new layer. | ||
817 | + evaluation_step, prediction = add_evaluation_step( | ||
818 | + final_tensor, ground_truth_input) | ||
819 | + | ||
820 | + # Merge all the summaries and write them out to /tmp/retrain_logs (by default) | ||
821 | + merged = tf.summary.merge_all() | ||
822 | + train_writer = tf.summary.FileWriter(FLAGS.summaries_dir + '/train', | ||
823 | + sess.graph) | ||
824 | + validation_writer = tf.summary.FileWriter(FLAGS.summaries_dir + '/validation') | ||
825 | + | ||
826 | + # Set up all our weights to their initial default values. | ||
827 | + init = tf.global_variables_initializer() | ||
828 | + sess.run(init) | ||
829 | + | ||
830 | + # Run the training for as many cycles as requested on the command line. | ||
831 | + for i in range(FLAGS.how_many_training_steps): | ||
832 | + # Get a batch of input bottleneck values, either calculated fresh every time | ||
833 | + # with distortions applied, or from the cache stored on disk. | ||
834 | + if do_distort_images: | ||
835 | + train_bottlenecks, train_ground_truth = get_random_distorted_bottlenecks( | ||
836 | + sess, image_lists, FLAGS.train_batch_size, 'training', | ||
837 | + FLAGS.image_dir, distorted_jpeg_data_tensor, | ||
838 | + distorted_image_tensor, resized_image_tensor, bottleneck_tensor) | ||
839 | + else: | ||
840 | + train_bottlenecks, train_ground_truth, _ = get_random_cached_bottlenecks( | ||
841 | + sess, image_lists, FLAGS.train_batch_size, 'training', | ||
842 | + FLAGS.bottleneck_dir, FLAGS.image_dir, jpeg_data_tensor, | ||
843 | + bottleneck_tensor) | ||
844 | + # Feed the bottlenecks and ground truth into the graph, and run a training | ||
845 | + # step. Capture training summaries for TensorBoard with the `merged` op. | ||
846 | + train_summary, _ = sess.run([merged, train_step], | ||
847 | + feed_dict={bottleneck_input: train_bottlenecks, | ||
848 | + ground_truth_input: train_ground_truth}) | ||
849 | + train_writer.add_summary(train_summary, i) | ||
850 | + | ||
851 | + # Every so often, print out how well the graph is training. | ||
852 | + is_last_step = (i + 1 == FLAGS.how_many_training_steps) | ||
853 | + if (i % FLAGS.eval_step_interval) == 0 or is_last_step: | ||
854 | + train_accuracy, cross_entropy_value = sess.run( | ||
855 | + [evaluation_step, cross_entropy], | ||
856 | + feed_dict={bottleneck_input: train_bottlenecks, | ||
857 | + ground_truth_input: train_ground_truth}) | ||
858 | + print('%s: Step %d: Train accuracy = %.1f%%' % (datetime.now(), i, | ||
859 | + train_accuracy * 100)) | ||
860 | + print('%s: Step %d: Cross entropy = %f' % (datetime.now(), i, | ||
861 | + cross_entropy_value)) | ||
862 | + validation_bottlenecks, validation_ground_truth, _ = ( | ||
863 | + get_random_cached_bottlenecks( | ||
864 | + sess, image_lists, FLAGS.validation_batch_size, 'validation', | ||
865 | + FLAGS.bottleneck_dir, FLAGS.image_dir, jpeg_data_tensor, | ||
866 | + bottleneck_tensor)) | ||
867 | + # Run a validation step and capture training summaries for TensorBoard | ||
868 | + # with the `merged` op. | ||
869 | + validation_summary, validation_accuracy = sess.run( | ||
870 | + [merged, evaluation_step], | ||
871 | + feed_dict={bottleneck_input: validation_bottlenecks, | ||
872 | + ground_truth_input: validation_ground_truth}) | ||
873 | + validation_writer.add_summary(validation_summary, i) | ||
874 | + print('%s: Step %d: Validation accuracy = %.1f%% (N=%d)' % | ||
875 | + (datetime.now(), i, validation_accuracy * 100, | ||
876 | + len(validation_bottlenecks))) | ||
877 | + | ||
878 | + # We've completed all our training, so run a final test evaluation on | ||
879 | + # some new images we haven't used before. | ||
880 | + test_bottlenecks, test_ground_truth, test_filenames = ( | ||
881 | + get_random_cached_bottlenecks(sess, image_lists, FLAGS.test_batch_size, | ||
882 | + 'testing', FLAGS.bottleneck_dir, | ||
883 | + FLAGS.image_dir, jpeg_data_tensor, | ||
884 | + bottleneck_tensor)) | ||
885 | + test_accuracy, predictions = sess.run( | ||
886 | + [evaluation_step, prediction], | ||
887 | + feed_dict={bottleneck_input: test_bottlenecks, | ||
888 | + ground_truth_input: test_ground_truth}) | ||
889 | + print('Final test accuracy = %.1f%% (N=%d)' % ( | ||
890 | + test_accuracy * 100, len(test_bottlenecks))) | ||
891 | + | ||
892 | + if FLAGS.print_misclassified_test_images: | ||
893 | + print('=== MISCLASSIFIED TEST IMAGES ===') | ||
894 | + for i, test_filename in enumerate(test_filenames): | ||
895 | + if predictions[i] != test_ground_truth[i].argmax(): | ||
896 | + print('%70s %s' % (test_filename, | ||
897 | + list(image_lists.keys())[predictions[i]])) | ||
898 | + | ||
899 | + # Write out the trained graph and labels with the weights stored as constants. | ||
900 | + output_graph_def = graph_util.convert_variables_to_constants( | ||
901 | + sess, graph.as_graph_def(), [FLAGS.final_tensor_name]) | ||
902 | + with gfile.FastGFile(FLAGS.output_graph, 'wb') as f: | ||
903 | + f.write(output_graph_def.SerializeToString()) | ||
904 | + with gfile.FastGFile(FLAGS.output_labels, 'w') as f: | ||
905 | + f.write('\n'.join(image_lists.keys()) + '\n') | ||
906 | + | ||
907 | + | ||
908 | +if __name__ == '__main__': | ||
909 | + parser = argparse.ArgumentParser() | ||
910 | + parser.add_argument( | ||
911 | + '--image_dir', | ||
912 | + type=str, | ||
913 | + default='./data/', | ||
914 | + help='Path to folders of labeled images.' | ||
915 | + ) | ||
916 | + parser.add_argument( | ||
917 | + '--output_graph', | ||
918 | + type=str, | ||
919 | + default='./test/output_graph.pb', | ||
920 | + help='Where to save the trained graph.' | ||
921 | + ) | ||
922 | + parser.add_argument( | ||
923 | + '--output_labels', | ||
924 | + type=str, | ||
925 | + default='./test/output_labels.txt', | ||
926 | + help='Where to save the trained graph\'s labels.' | ||
927 | + ) | ||
928 | + parser.add_argument( | ||
929 | + '--summaries_dir', | ||
930 | + type=str, | ||
931 | + default='./test/retrain_logs', | ||
932 | + help='Where to save summary logs for TensorBoard.' | ||
933 | + ) | ||
934 | + parser.add_argument( | ||
935 | + '--how_many_training_steps', | ||
936 | + type=int, | ||
937 | + default=1000, | ||
938 | + help='How many training steps to run before ending.' | ||
939 | + ) | ||
940 | + parser.add_argument( | ||
941 | + '--learning_rate', | ||
942 | + type=float, | ||
943 | + default=0.01, | ||
944 | + help='How large a learning rate to use when training.' | ||
945 | + ) | ||
946 | + parser.add_argument( | ||
947 | + '--testing_percentage', | ||
948 | + type=int, | ||
949 | + default=10, | ||
950 | + help='What percentage of images to use as a test set.' | ||
951 | + ) | ||
952 | + parser.add_argument( | ||
953 | + '--validation_percentage', | ||
954 | + type=int, | ||
955 | + default=10, | ||
956 | + help='What percentage of images to use as a validation set.' | ||
957 | + ) | ||
958 | + parser.add_argument( | ||
959 | + '--eval_step_interval', | ||
960 | + type=int, | ||
961 | + default=10, | ||
962 | + help='How often to evaluate the training results.' | ||
963 | + ) | ||
964 | + parser.add_argument( | ||
965 | + '--train_batch_size', | ||
966 | + type=int, | ||
967 | + default=100, | ||
968 | + help='How many images to train on at a time.' | ||
969 | + ) | ||
970 | + parser.add_argument( | ||
971 | + '--test_batch_size', | ||
972 | + type=int, | ||
973 | + default=-1, | ||
974 | + help="""\ | ||
975 | + How many images to test on. This test set is only used once, to evaluate | ||
976 | + the final accuracy of the model after training completes. | ||
977 | + A value of -1 causes the entire test set to be used, which leads to more | ||
978 | + stable results across runs.\ | ||
979 | + """ | ||
980 | + ) | ||
981 | + parser.add_argument( | ||
982 | + '--validation_batch_size', | ||
983 | + type=int, | ||
984 | + default=100, | ||
985 | + help="""\ | ||
986 | + How many images to use in an evaluation batch. This validation set is | ||
987 | + used much more often than the test set, and is an early indicator of how | ||
988 | + accurate the model is during training. | ||
989 | + A value of -1 causes the entire validation set to be used, which leads to | ||
990 | + more stable results across training iterations, but may be slower on large | ||
991 | + training sets.\ | ||
992 | + """ | ||
993 | + ) | ||
994 | + parser.add_argument( | ||
995 | + '--print_misclassified_test_images', | ||
996 | + default=False, | ||
997 | + help="""\ | ||
998 | + Whether to print out a list of all misclassified test images.\ | ||
999 | + """, | ||
1000 | + action='store_true' | ||
1001 | + ) | ||
1002 | + parser.add_argument( | ||
1003 | + '--model_dir', | ||
1004 | + type=str, | ||
1005 | + default='./test/imagenet', | ||
1006 | + help="""\ | ||
1007 | + Path to classify_image_graph_def.pb, | ||
1008 | + imagenet_synset_to_human_label_map.txt, and | ||
1009 | + imagenet_2012_challenge_label_map_proto.pbtxt.\ | ||
1010 | + """ | ||
1011 | + ) | ||
1012 | + parser.add_argument( | ||
1013 | + '--bottleneck_dir', | ||
1014 | + type=str, | ||
1015 | + default='./test/bottleneck', | ||
1016 | + help='Path to cache bottleneck layer values as files.' | ||
1017 | + ) | ||
1018 | + parser.add_argument( | ||
1019 | + '--final_tensor_name', | ||
1020 | + type=str, | ||
1021 | + default='final_result', | ||
1022 | + help="""\ | ||
1023 | + The name of the output classification layer in the retrained graph.\ | ||
1024 | + """ | ||
1025 | + ) | ||
1026 | + parser.add_argument( | ||
1027 | + '--flip_left_right', | ||
1028 | + default=False, | ||
1029 | + help="""\ | ||
1030 | + Whether to randomly flip half of the training images horizontally.\ | ||
1031 | + """, | ||
1032 | + action='store_true' | ||
1033 | + ) | ||
1034 | + parser.add_argument( | ||
1035 | + '--random_crop', | ||
1036 | + type=int, | ||
1037 | + default=0, | ||
1038 | + help="""\ | ||
1039 | + A percentage determining how much of a margin to randomly crop off the | ||
1040 | + training images.\ | ||
1041 | + """ | ||
1042 | + ) | ||
1043 | + parser.add_argument( | ||
1044 | + '--random_scale', | ||
1045 | + type=int, | ||
1046 | + default=0, | ||
1047 | + help="""\ | ||
1048 | + A percentage determining how much to randomly scale up the size of the | ||
1049 | + training images by.\ | ||
1050 | + """ | ||
1051 | + ) | ||
1052 | + parser.add_argument( | ||
1053 | + '--random_brightness', | ||
1054 | + type=int, | ||
1055 | + default=0, | ||
1056 | + help="""\ | ||
1057 | + A percentage determining how much to randomly multiply the training image | ||
1058 | + input pixels up or down by.\ | ||
1059 | + """ | ||
1060 | + ) | ||
1061 | + FLAGS, unparsed = parser.parse_known_args() | ||
1062 | + tf.app.run(main=main, argv=[sys.argv[0]] + unparsed) |
final/tensorflow/retrain_run_inference.py
0 → 100644
1 | +# -*- coding: utf-8 -*- | ||
2 | + | ||
3 | +"""Inception v3 architecture 모델을 retraining한 모델을 이용해서 이미지에 대한 추론(inference)을 진행하는 예제""" | ||
4 | + | ||
5 | +import sys | ||
6 | +import numpy as np | ||
7 | +import tensorflow as tf | ||
8 | + | ||
9 | +imagePath = '' | ||
10 | +modelFullPath = '' | ||
11 | +labelsFullPath = '' | ||
12 | + | ||
13 | +if (len(sys.argv) > 1): | ||
14 | + imagePath = sys.argv[1] + '/test/test.jpg' | ||
15 | + modelFullPath = sys.argv[1] + '/test/output_graph.pb' | ||
16 | + labelsFullPath = sys.argv[1] + '/test/output_labels.txt' | ||
17 | +else: | ||
18 | + imagePath = './test/test.jpg' # 추론을 진행할 이미지 경로 | ||
19 | + modelFullPath = './test/output_graph.pb' # 읽어들일 graph 파일 경로 | ||
20 | + labelsFullPath = './test/output_labels.txt' # 읽어들일 labels 파일 경로 | ||
21 | + | ||
22 | + | ||
23 | +def create_graph(): | ||
24 | + """저장된(saved) GraphDef 파일로부터 graph를 생성하고 saver를 반환한다.""" | ||
25 | + # 저장된(saved) graph_def.pb로부터 graph를 생성한다. | ||
26 | + with tf.gfile.FastGFile(modelFullPath, 'rb') as f: | ||
27 | + graph_def = tf.GraphDef() | ||
28 | + graph_def.ParseFromString(f.read()) | ||
29 | + _ = tf.import_graph_def(graph_def, name='') | ||
30 | + | ||
31 | + | ||
32 | +def run_inference_on_image(): | ||
33 | + answer = None | ||
34 | + | ||
35 | + if not tf.gfile.Exists(imagePath): | ||
36 | + tf.logging.fatal('File does not exist %s', imagePath) | ||
37 | + answer = 'File does not exist ' + imagePath | ||
38 | + return answer | ||
39 | + | ||
40 | + image_data = tf.gfile.FastGFile(imagePath, 'rb').read() | ||
41 | + | ||
42 | + # 저장된(saved) GraphDef 파일로부터 graph를 생성한다. | ||
43 | + create_graph() | ||
44 | + | ||
45 | + with tf.Session() as sess: | ||
46 | + | ||
47 | + softmax_tensor = sess.graph.get_tensor_by_name('final_result:0') | ||
48 | + predictions = sess.run(softmax_tensor, | ||
49 | + {'DecodeJpeg/contents:0': image_data}) | ||
50 | + predictions = np.squeeze(predictions) | ||
51 | + | ||
52 | + top_k = predictions.argsort()[-5:][::-1] # 가장 높은 확률을 가진 5개(top 5)의 예측값(predictions)을 얻는다. | ||
53 | + f = open(labelsFullPath, 'rb') | ||
54 | + lines = f.readlines() | ||
55 | + labels = [str(w).replace("\n", "") for w in lines] | ||
56 | + for node_id in top_k: | ||
57 | + human_string = labels[node_id] | ||
58 | + score = predictions[node_id] | ||
59 | + print('%s (score = %.5f)' % (human_string, score)) | ||
60 | + | ||
61 | + answer = labels[top_k[0]] | ||
62 | + return answer | ||
63 | + | ||
64 | + | ||
65 | +if __name__ == '__main__': | ||
66 | + run_inference_on_image() | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
final/views/directory.pug
0 → 100644
1 | +doctype html | ||
2 | +html | ||
3 | + head | ||
4 | + meta(charset='utf-8') | ||
5 | + title 파일 업로드 | ||
6 | + | ||
7 | + style | ||
8 | + img | ||
9 | + | display="block" | ||
10 | + | max-width="200px" | ||
11 | + | max-height="200px" | ||
12 | + | width="auto" | ||
13 | + | height="auto" | ||
14 | + | ||
15 | + | ||
16 | + body | ||
17 | + - var DirectoryName=directoryName; | ||
18 | + h1=DirectoryName | ||
19 | + br | ||
20 | + h2 파일 업로드 | ||
21 | + form(action="./upload/" method="POST" enctype="multipart/form-data") | ||
22 | + input(type="file", name="userImage", multiple="multiple", accept=".jpg, .jpeg") | ||
23 | + input(type="submit", value="업로드") | ||
24 | + br | ||
25 | + br | ||
26 | + ul | ||
27 | + - var ImageList=imageList | ||
28 | + - var filelist=fileList | ||
29 | + - var iter=0 | ||
30 | + each Image in ImageList | ||
31 | + div(style="margin-right:10px;") | ||
32 | + img(src=Image) | ||
33 | + li=filelist[iter] | ||
34 | + - iter=iter+1 | ||
35 | + br | ||
36 | + br | ||
37 | + | ||
38 | + | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
final/views/directoryDeleteCheck.pug
0 → 100644
final/views/directoryModifyCheck.pug
0 → 100644
1 | +script. | ||
2 | + var directoryName= !{directoryName}; | ||
3 | + var result = prompt('새 분류명을 입력하세요. (기존: ' + directoryName + ')'); | ||
4 | + | ||
5 | + if (result) { | ||
6 | + location.href = '.?newName=' + result; | ||
7 | + } else { | ||
8 | + alert('분류명을 수정하지 않습니다.'); | ||
9 | + history.back(); | ||
10 | + } | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
final/views/error_directoryAdd.pug
0 → 100644
final/views/home.pug
0 → 100644
1 | +doctype html | ||
2 | +html | ||
3 | + head | ||
4 | + meta(charset='utf-8') | ||
5 | + title 분류 리스트 | ||
6 | + | ||
7 | + | ||
8 | + | ||
9 | + body | ||
10 | + form(action="../directory_check" method="post") | ||
11 | + p 새로 만들 분류명: | ||
12 | + input(name="directoryName", type="text") | ||
13 | + input(type="submit", value="생성") | ||
14 | + br | ||
15 | + br | ||
16 | + | ||
17 | + form(action="../test" method="post" enctype="multipart/form-data") | ||
18 | + p 테스트할 이미지: | ||
19 | + input(name="TestImage", type="file", accept=".jpg, .jpeg") | ||
20 | + input(type="submit", value="테스트") | ||
21 | + br | ||
22 | + br | ||
23 | + br | ||
24 | + | ||
25 | + div(style="margin-right:10px; float:left;") | ||
26 | + form(action="../imageLearning" method="get") | ||
27 | + input(type="submit", value="이미지 학습시키기" style="font-size:24px; font-weight:bold") | ||
28 | + | ||
29 | + - var learnTime=learntime | ||
30 | + div(style="margin-right:30px;") | ||
31 | + p="(최근 학습 시간: " + learnTime + ")" | ||
32 | + | ||
33 | + br | ||
34 | + | ||
35 | + ul | ||
36 | + - var folderList=fileList | ||
37 | + each folder in folderList | ||
38 | + div(style="margin-right:30px; float:left;") | ||
39 | + li | ||
40 | + a(href="./"+folder+"/")=folder | ||
41 | + | ||
42 | + div(style="margin-right:5px; float:left;") | ||
43 | + form(action="./"+folder+"/modify/" method="get") | ||
44 | + input(type="submit", value="수정") | ||
45 | + | ||
46 | + div | ||
47 | + form(action="./"+folder+"/delete/" method="get") | ||
48 | + input(type="submit", value="삭제") | ||
49 | + br | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
final/views/testResult.pug
0 → 100644
... | @@ -2,10 +2,533 @@ | ... | @@ -2,10 +2,533 @@ |
2 | "requires": true, | 2 | "requires": true, |
3 | "lockfileVersion": 1, | 3 | "lockfileVersion": 1, |
4 | "dependencies": { | 4 | "dependencies": { |
5 | + "accepts": { | ||
6 | + "version": "1.3.7", | ||
7 | + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", | ||
8 | + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", | ||
9 | + "requires": { | ||
10 | + "mime-types": "~2.1.24", | ||
11 | + "negotiator": "0.6.2" | ||
12 | + } | ||
13 | + }, | ||
14 | + "append-field": { | ||
15 | + "version": "1.0.0", | ||
16 | + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", | ||
17 | + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" | ||
18 | + }, | ||
19 | + "array-flatten": { | ||
20 | + "version": "1.1.1", | ||
21 | + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", | ||
22 | + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" | ||
23 | + }, | ||
24 | + "body-parser": { | ||
25 | + "version": "1.19.0", | ||
26 | + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", | ||
27 | + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", | ||
28 | + "requires": { | ||
29 | + "bytes": "3.1.0", | ||
30 | + "content-type": "~1.0.4", | ||
31 | + "debug": "2.6.9", | ||
32 | + "depd": "~1.1.2", | ||
33 | + "http-errors": "1.7.2", | ||
34 | + "iconv-lite": "0.4.24", | ||
35 | + "on-finished": "~2.3.0", | ||
36 | + "qs": "6.7.0", | ||
37 | + "raw-body": "2.4.0", | ||
38 | + "type-is": "~1.6.17" | ||
39 | + } | ||
40 | + }, | ||
41 | + "buffer-from": { | ||
42 | + "version": "1.1.1", | ||
43 | + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", | ||
44 | + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" | ||
45 | + }, | ||
46 | + "busboy": { | ||
47 | + "version": "0.2.14", | ||
48 | + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", | ||
49 | + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", | ||
50 | + "requires": { | ||
51 | + "dicer": "0.2.5", | ||
52 | + "readable-stream": "1.1.x" | ||
53 | + } | ||
54 | + }, | ||
55 | + "bytes": { | ||
56 | + "version": "3.1.0", | ||
57 | + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", | ||
58 | + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" | ||
59 | + }, | ||
60 | + "concat-stream": { | ||
61 | + "version": "1.6.2", | ||
62 | + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", | ||
63 | + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", | ||
64 | + "requires": { | ||
65 | + "buffer-from": "^1.0.0", | ||
66 | + "inherits": "^2.0.3", | ||
67 | + "readable-stream": "^2.2.2", | ||
68 | + "typedarray": "^0.0.6" | ||
69 | + }, | ||
70 | + "dependencies": { | ||
71 | + "isarray": { | ||
72 | + "version": "1.0.0", | ||
73 | + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", | ||
74 | + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" | ||
75 | + }, | ||
76 | + "readable-stream": { | ||
77 | + "version": "2.3.7", | ||
78 | + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", | ||
79 | + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", | ||
80 | + "requires": { | ||
81 | + "core-util-is": "~1.0.0", | ||
82 | + "inherits": "~2.0.3", | ||
83 | + "isarray": "~1.0.0", | ||
84 | + "process-nextick-args": "~2.0.0", | ||
85 | + "safe-buffer": "~5.1.1", | ||
86 | + "string_decoder": "~1.1.1", | ||
87 | + "util-deprecate": "~1.0.1" | ||
88 | + } | ||
89 | + }, | ||
90 | + "string_decoder": { | ||
91 | + "version": "1.1.1", | ||
92 | + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", | ||
93 | + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", | ||
94 | + "requires": { | ||
95 | + "safe-buffer": "~5.1.0" | ||
96 | + } | ||
97 | + } | ||
98 | + } | ||
99 | + }, | ||
100 | + "content-disposition": { | ||
101 | + "version": "0.5.3", | ||
102 | + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", | ||
103 | + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", | ||
104 | + "requires": { | ||
105 | + "safe-buffer": "5.1.2" | ||
106 | + } | ||
107 | + }, | ||
108 | + "content-type": { | ||
109 | + "version": "1.0.4", | ||
110 | + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", | ||
111 | + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" | ||
112 | + }, | ||
113 | + "cookie": { | ||
114 | + "version": "0.4.0", | ||
115 | + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", | ||
116 | + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" | ||
117 | + }, | ||
118 | + "cookie-signature": { | ||
119 | + "version": "1.0.6", | ||
120 | + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", | ||
121 | + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" | ||
122 | + }, | ||
123 | + "core-util-is": { | ||
124 | + "version": "1.0.2", | ||
125 | + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", | ||
126 | + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" | ||
127 | + }, | ||
128 | + "debug": { | ||
129 | + "version": "2.6.9", | ||
130 | + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", | ||
131 | + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", | ||
132 | + "requires": { | ||
133 | + "ms": "2.0.0" | ||
134 | + } | ||
135 | + }, | ||
136 | + "depd": { | ||
137 | + "version": "1.1.2", | ||
138 | + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", | ||
139 | + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" | ||
140 | + }, | ||
141 | + "destroy": { | ||
142 | + "version": "1.0.4", | ||
143 | + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", | ||
144 | + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" | ||
145 | + }, | ||
146 | + "dicer": { | ||
147 | + "version": "0.2.5", | ||
148 | + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", | ||
149 | + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", | ||
150 | + "requires": { | ||
151 | + "readable-stream": "1.1.x", | ||
152 | + "streamsearch": "0.1.2" | ||
153 | + } | ||
154 | + }, | ||
155 | + "ee-first": { | ||
156 | + "version": "1.1.1", | ||
157 | + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", | ||
158 | + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" | ||
159 | + }, | ||
160 | + "encodeurl": { | ||
161 | + "version": "1.0.2", | ||
162 | + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", | ||
163 | + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" | ||
164 | + }, | ||
165 | + "escape-html": { | ||
166 | + "version": "1.0.3", | ||
167 | + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", | ||
168 | + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" | ||
169 | + }, | ||
170 | + "etag": { | ||
171 | + "version": "1.8.1", | ||
172 | + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", | ||
173 | + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" | ||
174 | + }, | ||
175 | + "express": { | ||
176 | + "version": "4.17.1", | ||
177 | + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", | ||
178 | + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", | ||
179 | + "requires": { | ||
180 | + "accepts": "~1.3.7", | ||
181 | + "array-flatten": "1.1.1", | ||
182 | + "body-parser": "1.19.0", | ||
183 | + "content-disposition": "0.5.3", | ||
184 | + "content-type": "~1.0.4", | ||
185 | + "cookie": "0.4.0", | ||
186 | + "cookie-signature": "1.0.6", | ||
187 | + "debug": "2.6.9", | ||
188 | + "depd": "~1.1.2", | ||
189 | + "encodeurl": "~1.0.2", | ||
190 | + "escape-html": "~1.0.3", | ||
191 | + "etag": "~1.8.1", | ||
192 | + "finalhandler": "~1.1.2", | ||
193 | + "fresh": "0.5.2", | ||
194 | + "merge-descriptors": "1.0.1", | ||
195 | + "methods": "~1.1.2", | ||
196 | + "on-finished": "~2.3.0", | ||
197 | + "parseurl": "~1.3.3", | ||
198 | + "path-to-regexp": "0.1.7", | ||
199 | + "proxy-addr": "~2.0.5", | ||
200 | + "qs": "6.7.0", | ||
201 | + "range-parser": "~1.2.1", | ||
202 | + "safe-buffer": "5.1.2", | ||
203 | + "send": "0.17.1", | ||
204 | + "serve-static": "1.14.1", | ||
205 | + "setprototypeof": "1.1.1", | ||
206 | + "statuses": "~1.5.0", | ||
207 | + "type-is": "~1.6.18", | ||
208 | + "utils-merge": "1.0.1", | ||
209 | + "vary": "~1.1.2" | ||
210 | + } | ||
211 | + }, | ||
212 | + "finalhandler": { | ||
213 | + "version": "1.1.2", | ||
214 | + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", | ||
215 | + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", | ||
216 | + "requires": { | ||
217 | + "debug": "2.6.9", | ||
218 | + "encodeurl": "~1.0.2", | ||
219 | + "escape-html": "~1.0.3", | ||
220 | + "on-finished": "~2.3.0", | ||
221 | + "parseurl": "~1.3.3", | ||
222 | + "statuses": "~1.5.0", | ||
223 | + "unpipe": "~1.0.0" | ||
224 | + } | ||
225 | + }, | ||
226 | + "forwarded": { | ||
227 | + "version": "0.1.2", | ||
228 | + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", | ||
229 | + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" | ||
230 | + }, | ||
231 | + "fresh": { | ||
232 | + "version": "0.5.2", | ||
233 | + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", | ||
234 | + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" | ||
235 | + }, | ||
236 | + "http-errors": { | ||
237 | + "version": "1.7.2", | ||
238 | + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", | ||
239 | + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", | ||
240 | + "requires": { | ||
241 | + "depd": "~1.1.2", | ||
242 | + "inherits": "2.0.3", | ||
243 | + "setprototypeof": "1.1.1", | ||
244 | + "statuses": ">= 1.5.0 < 2", | ||
245 | + "toidentifier": "1.0.0" | ||
246 | + } | ||
247 | + }, | ||
248 | + "iconv-lite": { | ||
249 | + "version": "0.4.24", | ||
250 | + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", | ||
251 | + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", | ||
252 | + "requires": { | ||
253 | + "safer-buffer": ">= 2.1.2 < 3" | ||
254 | + } | ||
255 | + }, | ||
256 | + "inherits": { | ||
257 | + "version": "2.0.3", | ||
258 | + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", | ||
259 | + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" | ||
260 | + }, | ||
261 | + "ipaddr.js": { | ||
262 | + "version": "1.9.1", | ||
263 | + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", | ||
264 | + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" | ||
265 | + }, | ||
266 | + "isarray": { | ||
267 | + "version": "0.0.1", | ||
268 | + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", | ||
269 | + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" | ||
270 | + }, | ||
271 | + "media-typer": { | ||
272 | + "version": "0.3.0", | ||
273 | + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", | ||
274 | + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" | ||
275 | + }, | ||
276 | + "merge-descriptors": { | ||
277 | + "version": "1.0.1", | ||
278 | + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", | ||
279 | + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" | ||
280 | + }, | ||
281 | + "methods": { | ||
282 | + "version": "1.1.2", | ||
283 | + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", | ||
284 | + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" | ||
285 | + }, | ||
286 | + "mime": { | ||
287 | + "version": "1.6.0", | ||
288 | + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", | ||
289 | + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" | ||
290 | + }, | ||
291 | + "mime-db": { | ||
292 | + "version": "1.44.0", | ||
293 | + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", | ||
294 | + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" | ||
295 | + }, | ||
296 | + "mime-types": { | ||
297 | + "version": "2.1.27", | ||
298 | + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", | ||
299 | + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", | ||
300 | + "requires": { | ||
301 | + "mime-db": "1.44.0" | ||
302 | + } | ||
303 | + }, | ||
304 | + "minimist": { | ||
305 | + "version": "1.2.5", | ||
306 | + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", | ||
307 | + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" | ||
308 | + }, | ||
309 | + "mkdirp": { | ||
310 | + "version": "0.5.5", | ||
311 | + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", | ||
312 | + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", | ||
313 | + "requires": { | ||
314 | + "minimist": "^1.2.5" | ||
315 | + } | ||
316 | + }, | ||
5 | "moment": { | 317 | "moment": { |
6 | "version": "2.26.0", | 318 | "version": "2.26.0", |
7 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz", | 319 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz", |
8 | "integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==" | 320 | "integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==" |
321 | + }, | ||
322 | + "ms": { | ||
323 | + "version": "2.0.0", | ||
324 | + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", | ||
325 | + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" | ||
326 | + }, | ||
327 | + "multer": { | ||
328 | + "version": "1.4.2", | ||
329 | + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", | ||
330 | + "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", | ||
331 | + "requires": { | ||
332 | + "append-field": "^1.0.0", | ||
333 | + "busboy": "^0.2.11", | ||
334 | + "concat-stream": "^1.5.2", | ||
335 | + "mkdirp": "^0.5.1", | ||
336 | + "object-assign": "^4.1.1", | ||
337 | + "on-finished": "^2.3.0", | ||
338 | + "type-is": "^1.6.4", | ||
339 | + "xtend": "^4.0.0" | ||
340 | + } | ||
341 | + }, | ||
342 | + "negotiator": { | ||
343 | + "version": "0.6.2", | ||
344 | + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", | ||
345 | + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" | ||
346 | + }, | ||
347 | + "object-assign": { | ||
348 | + "version": "4.1.1", | ||
349 | + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", | ||
350 | + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" | ||
351 | + }, | ||
352 | + "on-finished": { | ||
353 | + "version": "2.3.0", | ||
354 | + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", | ||
355 | + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", | ||
356 | + "requires": { | ||
357 | + "ee-first": "1.1.1" | ||
358 | + } | ||
359 | + }, | ||
360 | + "parseurl": { | ||
361 | + "version": "1.3.3", | ||
362 | + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", | ||
363 | + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" | ||
364 | + }, | ||
365 | + "path-to-regexp": { | ||
366 | + "version": "0.1.7", | ||
367 | + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", | ||
368 | + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" | ||
369 | + }, | ||
370 | + "process-nextick-args": { | ||
371 | + "version": "2.0.1", | ||
372 | + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", | ||
373 | + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" | ||
374 | + }, | ||
375 | + "proxy-addr": { | ||
376 | + "version": "2.0.6", | ||
377 | + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", | ||
378 | + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", | ||
379 | + "requires": { | ||
380 | + "forwarded": "~0.1.2", | ||
381 | + "ipaddr.js": "1.9.1" | ||
382 | + } | ||
383 | + }, | ||
384 | + "python-shell": { | ||
385 | + "version": "2.0.1", | ||
386 | + "resolved": "https://registry.npmjs.org/python-shell/-/python-shell-2.0.1.tgz", | ||
387 | + "integrity": "sha512-Ys+SiCinY9JrldIJxGWd2AMQSQZLU7PFzrCWY7HTawZ73tIthFdlLLU1Y6Y40Hwdutc+TmfMe5TXNU73s07Xyg==" | ||
388 | + }, | ||
389 | + "qs": { | ||
390 | + "version": "6.7.0", | ||
391 | + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", | ||
392 | + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" | ||
393 | + }, | ||
394 | + "range-parser": { | ||
395 | + "version": "1.2.1", | ||
396 | + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", | ||
397 | + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" | ||
398 | + }, | ||
399 | + "raw-body": { | ||
400 | + "version": "2.4.0", | ||
401 | + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", | ||
402 | + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", | ||
403 | + "requires": { | ||
404 | + "bytes": "3.1.0", | ||
405 | + "http-errors": "1.7.2", | ||
406 | + "iconv-lite": "0.4.24", | ||
407 | + "unpipe": "1.0.0" | ||
408 | + } | ||
409 | + }, | ||
410 | + "readable-stream": { | ||
411 | + "version": "1.1.14", | ||
412 | + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", | ||
413 | + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", | ||
414 | + "requires": { | ||
415 | + "core-util-is": "~1.0.0", | ||
416 | + "inherits": "~2.0.1", | ||
417 | + "isarray": "0.0.1", | ||
418 | + "string_decoder": "~0.10.x" | ||
419 | + } | ||
420 | + }, | ||
421 | + "safe-buffer": { | ||
422 | + "version": "5.1.2", | ||
423 | + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", | ||
424 | + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" | ||
425 | + }, | ||
426 | + "safer-buffer": { | ||
427 | + "version": "2.1.2", | ||
428 | + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", | ||
429 | + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" | ||
430 | + }, | ||
431 | + "send": { | ||
432 | + "version": "0.17.1", | ||
433 | + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", | ||
434 | + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", | ||
435 | + "requires": { | ||
436 | + "debug": "2.6.9", | ||
437 | + "depd": "~1.1.2", | ||
438 | + "destroy": "~1.0.4", | ||
439 | + "encodeurl": "~1.0.2", | ||
440 | + "escape-html": "~1.0.3", | ||
441 | + "etag": "~1.8.1", | ||
442 | + "fresh": "0.5.2", | ||
443 | + "http-errors": "~1.7.2", | ||
444 | + "mime": "1.6.0", | ||
445 | + "ms": "2.1.1", | ||
446 | + "on-finished": "~2.3.0", | ||
447 | + "range-parser": "~1.2.1", | ||
448 | + "statuses": "~1.5.0" | ||
449 | + }, | ||
450 | + "dependencies": { | ||
451 | + "ms": { | ||
452 | + "version": "2.1.1", | ||
453 | + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", | ||
454 | + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" | ||
455 | + } | ||
456 | + } | ||
457 | + }, | ||
458 | + "serve-static": { | ||
459 | + "version": "1.14.1", | ||
460 | + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", | ||
461 | + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", | ||
462 | + "requires": { | ||
463 | + "encodeurl": "~1.0.2", | ||
464 | + "escape-html": "~1.0.3", | ||
465 | + "parseurl": "~1.3.3", | ||
466 | + "send": "0.17.1" | ||
467 | + } | ||
468 | + }, | ||
469 | + "setprototypeof": { | ||
470 | + "version": "1.1.1", | ||
471 | + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", | ||
472 | + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" | ||
473 | + }, | ||
474 | + "statuses": { | ||
475 | + "version": "1.5.0", | ||
476 | + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", | ||
477 | + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" | ||
478 | + }, | ||
479 | + "streamsearch": { | ||
480 | + "version": "0.1.2", | ||
481 | + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", | ||
482 | + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" | ||
483 | + }, | ||
484 | + "string_decoder": { | ||
485 | + "version": "0.10.31", | ||
486 | + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", | ||
487 | + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" | ||
488 | + }, | ||
489 | + "toidentifier": { | ||
490 | + "version": "1.0.0", | ||
491 | + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", | ||
492 | + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" | ||
493 | + }, | ||
494 | + "type-is": { | ||
495 | + "version": "1.6.18", | ||
496 | + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", | ||
497 | + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", | ||
498 | + "requires": { | ||
499 | + "media-typer": "0.3.0", | ||
500 | + "mime-types": "~2.1.24" | ||
501 | + } | ||
502 | + }, | ||
503 | + "typedarray": { | ||
504 | + "version": "0.0.6", | ||
505 | + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", | ||
506 | + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" | ||
507 | + }, | ||
508 | + "unpipe": { | ||
509 | + "version": "1.0.0", | ||
510 | + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", | ||
511 | + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" | ||
512 | + }, | ||
513 | + "util-deprecate": { | ||
514 | + "version": "1.0.2", | ||
515 | + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", | ||
516 | + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" | ||
517 | + }, | ||
518 | + "utils-merge": { | ||
519 | + "version": "1.0.1", | ||
520 | + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", | ||
521 | + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" | ||
522 | + }, | ||
523 | + "vary": { | ||
524 | + "version": "1.1.2", | ||
525 | + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", | ||
526 | + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" | ||
527 | + }, | ||
528 | + "xtend": { | ||
529 | + "version": "4.0.2", | ||
530 | + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", | ||
531 | + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" | ||
9 | } | 532 | } |
10 | } | 533 | } |
11 | } | 534 | } | ... | ... |
-
Please register or login to post a comment