retold-data-service 2.0.21 → 2.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.quackage-comprehension-loader.json +19 -0
- package/bin/retold-data-service-clone.js +4 -1
- package/generate-bookstore-comprehension.js +645 -0
- package/package.json +7 -7
- package/source/Retold-Data-Service.js +30 -2
- package/source/services/comprehension-loader/ComprehensionLoader-Command-Load.js +345 -0
- package/source/services/comprehension-loader/ComprehensionLoader-Command-Schema.js +97 -0
- package/source/services/comprehension-loader/ComprehensionLoader-Command-Session.js +221 -0
- package/source/services/comprehension-loader/ComprehensionLoader-Command-WebUI.js +57 -0
- package/source/services/comprehension-loader/Retold-Data-Service-ComprehensionLoader.js +536 -0
- package/source/services/comprehension-loader/pict-app/Pict-Application-ComprehensionLoader-Configuration.json +9 -0
- package/source/services/comprehension-loader/pict-app/Pict-Application-ComprehensionLoader.js +86 -0
- package/source/services/comprehension-loader/pict-app/Pict-ComprehensionLoader-Bundle.js +6 -0
- package/source/services/comprehension-loader/pict-app/providers/Pict-Provider-ComprehensionLoader.js +760 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Layout.js +360 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Load.js +472 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Schema.js +119 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Session.js +269 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Source.js +330 -0
- package/source/services/comprehension-loader/web/comprehension-loader.js +6794 -0
- package/source/services/comprehension-loader/web/comprehension-loader.js.map +1 -0
- package/source/services/comprehension-loader/web/comprehension-loader.min.js +2 -0
- package/source/services/comprehension-loader/web/comprehension-loader.min.js.map +1 -0
- package/source/services/comprehension-loader/web/index.html +17 -0
- package/source/services/data-cloner/DataCloner-Command-Schema.js +407 -15
- package/source/services/data-cloner/Retold-Data-Service-DataCloner.js +59 -1
- package/source/services/data-cloner/pict-app/Pict-Application-DataCloner.js +1 -0
- package/source/services/data-cloner/pict-app/providers/Pict-Provider-DataCloner.js +125 -5
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Connection.js +18 -8
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Deploy.js +104 -1
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Export.js +1 -1
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Layout.js +12 -0
- package/source/services/data-cloner/web/data-cloner.js +201 -139
- package/source/services/data-cloner/web/data-cloner.js.map +1 -1
- package/source/services/data-cloner/web/data-cloner.min.js +1 -1
- package/source/services/data-cloner/web/data-cloner.min.js.map +1 -1
- package/test/RetoldDataService_tests.js +225 -0
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Retold Bookstore Comprehension Generator
|
|
4
|
+
*
|
|
5
|
+
* Generates a stand-alone comprehension JSON file containing:
|
|
6
|
+
* - 10,000 Authors
|
|
7
|
+
* - 10,000 Books
|
|
8
|
+
* - ~15,000 BookAuthorJoin records
|
|
9
|
+
* - ~200 hilarious fictitious Reviewers (Users)
|
|
10
|
+
* - ~25,000 Reviews
|
|
11
|
+
*
|
|
12
|
+
* Shaped for the retold-harness schema (Book, Author, BookAuthorJoin, Review, User).
|
|
13
|
+
*/
|
|
14
|
+
'use strict';
|
|
15
|
+
|
|
16
|
+
const libFS = require('fs');
|
|
17
|
+
const libPath = require('path');
|
|
18
|
+
const libCrypto = require('crypto');
|
|
19
|
+
|
|
20
|
+
// ================================================================
|
|
21
|
+
// Deterministic PRNG (xorshift128+) so output is reproducible
|
|
22
|
+
// ================================================================
|
|
23
|
+
let _Seed0 = BigInt('0xBEEFCAFEDEAD1234');
|
|
24
|
+
let _Seed1 = BigInt('0xFACEFEED42424242');
|
|
25
|
+
|
|
26
|
+
function nextRandom()
|
|
27
|
+
{
|
|
28
|
+
let tmpS1 = _Seed0;
|
|
29
|
+
let tmpS0 = _Seed1;
|
|
30
|
+
_Seed0 = tmpS0;
|
|
31
|
+
tmpS1 ^= (tmpS1 << 23n) & 0xFFFFFFFFFFFFFFFFn;
|
|
32
|
+
tmpS1 ^= tmpS1 >> 17n;
|
|
33
|
+
tmpS1 ^= tmpS0;
|
|
34
|
+
tmpS1 ^= tmpS0 >> 26n;
|
|
35
|
+
_Seed1 = tmpS1;
|
|
36
|
+
let tmpResult = Number((_Seed0 + _Seed1) & 0x7FFFFFFFFFFFFFFFn);
|
|
37
|
+
return tmpResult / 0x7FFFFFFFFFFFFFFF;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function randInt(pMin, pMax)
|
|
41
|
+
{
|
|
42
|
+
return Math.floor(nextRandom() * (pMax - pMin + 1)) + pMin;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function pick(pArray)
|
|
46
|
+
{
|
|
47
|
+
return pArray[randInt(0, pArray.length - 1)];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function pickN(pArray, pN)
|
|
51
|
+
{
|
|
52
|
+
let tmpCopy = pArray.slice();
|
|
53
|
+
let tmpResult = [];
|
|
54
|
+
for (let i = 0; i < pN && tmpCopy.length > 0; i++)
|
|
55
|
+
{
|
|
56
|
+
let tmpIdx = randInt(0, tmpCopy.length - 1);
|
|
57
|
+
tmpResult.push(tmpCopy[tmpIdx]);
|
|
58
|
+
tmpCopy.splice(tmpIdx, 1);
|
|
59
|
+
}
|
|
60
|
+
return tmpResult;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function uuid()
|
|
64
|
+
{
|
|
65
|
+
return libCrypto.randomUUID();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function isoDate(pYear, pMonth, pDay)
|
|
69
|
+
{
|
|
70
|
+
let tmpM = String(pMonth).padStart(2, '0');
|
|
71
|
+
let tmpD = String(pDay).padStart(2, '0');
|
|
72
|
+
return `${pYear}-${tmpM}-${tmpD}T00:00:00.000Z`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function randomDate(pStartYear, pEndYear)
|
|
76
|
+
{
|
|
77
|
+
let tmpYear = randInt(pStartYear, pEndYear);
|
|
78
|
+
let tmpMonth = randInt(1, 12);
|
|
79
|
+
let tmpDay = randInt(1, 28);
|
|
80
|
+
let tmpHour = randInt(0, 23);
|
|
81
|
+
let tmpMin = randInt(0, 59);
|
|
82
|
+
let tmpSec = randInt(0, 59);
|
|
83
|
+
return `${tmpYear}-${String(tmpMonth).padStart(2, '0')}-${String(tmpDay).padStart(2, '0')}T${String(tmpHour).padStart(2, '0')}:${String(tmpMin).padStart(2, '0')}:${String(tmpSec).padStart(2, '0')}.000Z`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ================================================================
|
|
87
|
+
// Name Data
|
|
88
|
+
// ================================================================
|
|
89
|
+
const FIRST_NAMES = [
|
|
90
|
+
'Abigail', 'Alaric', 'Amara', 'Augustus', 'Barnaby', 'Beatrix', 'Benedict', 'Bernadette',
|
|
91
|
+
'Calliope', 'Cassandra', 'Cedric', 'Clementine', 'Cornelius', 'Dagmar', 'Delphine', 'Desmond',
|
|
92
|
+
'Ebenezer', 'Eloise', 'Erasmus', 'Esmeralda', 'Evangeline', 'Fabian', 'Felicity', 'Ferdinand',
|
|
93
|
+
'Geraldine', 'Gideon', 'Griselda', 'Guinevere', 'Hadrian', 'Hortense', 'Ignatius', 'Imogen',
|
|
94
|
+
'Jasper', 'Josephine', 'Juniper', 'Lavinia', 'Leopold', 'Lucinda', 'Magnus', 'Marguerite',
|
|
95
|
+
'Maximilian', 'Millicent', 'Montgomery', 'Mortimer', 'Narcissa', 'Neville', 'Octavia', 'Ophelia',
|
|
96
|
+
'Percival', 'Petronella', 'Philomena', 'Prudence', 'Quentin', 'Reginald', 'Rosalind', 'Rupert',
|
|
97
|
+
'Sebastian', 'Seraphina', 'Sigmund', 'Sophronia', 'Thaddeus', 'Theodora', 'Ulysses', 'Valentina',
|
|
98
|
+
'Winifred', 'Wolfgang', 'Xenophon', 'Yolanda', 'Zachariah', 'Zenobia', 'Aldous', 'Anastasia',
|
|
99
|
+
'Bartholomew', 'Bianca', 'Cosimo', 'Dorothea', 'Edmund', 'Flora', 'Godfrey', 'Hermione',
|
|
100
|
+
'Isadora', 'Jerome', 'Katarina', 'Lysander', 'Minerva', 'Nigel', 'Ottilie', 'Penelope',
|
|
101
|
+
'Raphael', 'Sybil', 'Tobias', 'Ursula', 'Vivienne', 'Wilfred', 'Xander', 'Yvette'
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
const LAST_NAMES = [
|
|
105
|
+
'Abernathy', 'Blackwood', 'Carmichael', 'Devereaux', 'Elderberry', 'Featherbottom', 'Grimshaw',
|
|
106
|
+
'Hawthorne', 'Inglesworth', 'Jabberwock', 'Knickerbocker', 'Lumpkin', 'Muttonchop', 'Noodlebrain',
|
|
107
|
+
'Obfuscate', 'Pumpernickel', 'Quagmire', 'Rumblebottom', 'Snorkeldink', 'Thistlethwaite',
|
|
108
|
+
'Underpants', 'Vonderschmidt', 'Whipplesnaith', 'Xylophone', 'Yammerschnook', 'Zeppelinski',
|
|
109
|
+
'Bumblesnort', 'Crumpetface', 'Dingleberry', 'Flapdoodle', 'Goosegrease', 'Humblebrag',
|
|
110
|
+
'Jigglewhistle', 'Kettledrum', 'Lollygag', 'Mumblechops', 'Niffleheim', 'Oozelfinch',
|
|
111
|
+
'Piddlesworth', 'Quibbleston', 'Rigmarole', 'Snotwhistle', 'Twaddlecock', 'Umbridge',
|
|
112
|
+
'Wigglesworth', 'Bamboozle', 'Codswallop', 'Dillydally', 'Flibbertigibbet', 'Gobsmack',
|
|
113
|
+
'Hullabaloo', 'Jumblefudge', 'Kerfuffle', 'Lollipop', 'Malarkey', 'Nincompoop',
|
|
114
|
+
'Paddywack', 'Ramshackle', 'Skedaddle', 'Taradiddle', 'Whatchamacallit', 'Brouhaha',
|
|
115
|
+
'Cattywampus', 'Discombobulate', 'Flummox', 'Gazump', 'Hodgepodge', 'Lickspittle',
|
|
116
|
+
'Mudpuddle', 'Nubbins', 'Poppycock', 'Ragamuffin', 'Slapdash', 'Tomfoolery',
|
|
117
|
+
'Widdershins', 'Zigzag', 'Ballyhoo', 'Claptrap', 'Donnybrook', 'Fiddle-faddle',
|
|
118
|
+
'Gibberish', 'Hobbledehoy', 'Lickety-split', 'Muckraker', 'Namby-pamby', 'Piffle',
|
|
119
|
+
'Razzmatazz', 'Skullduggery', 'Thingamajig', 'Whirligig', 'Balderdash', 'Canoodle',
|
|
120
|
+
'Doohickey', 'Fandango', 'Gadabout', 'Higgledy-piggledy', 'Inglenook', 'Jalopy'
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
const AUTHOR_FIRST_NAMES = [
|
|
124
|
+
'Agatha', 'Albert', 'Alice', 'Anton', 'Arthur', 'Barbara', 'Bertrand', 'Boris',
|
|
125
|
+
'Charlotte', 'Clara', 'Dashiell', 'Doris', 'Dorothy', 'Edgar', 'Edith', 'Eleanor',
|
|
126
|
+
'Emily', 'Ernest', 'Evelyn', 'Frances', 'Franz', 'Fyodor', 'Gabriel', 'George',
|
|
127
|
+
'Gertrude', 'Graham', 'Gustave', 'Hannah', 'Harper', 'Harriet', 'Heinrich', 'Henry',
|
|
128
|
+
'Herman', 'Hugo', 'Iris', 'Isaac', 'Ivan', 'Jack', 'James', 'Jane',
|
|
129
|
+
'Jean-Paul', 'Jorge', 'Joseph', 'Joyce', 'Julio', 'Katherine', 'Kurt', 'Leo',
|
|
130
|
+
'Louisa', 'Marcel', 'Margaret', 'Mark', 'Mary', 'Maya', 'Milan', 'Muriel',
|
|
131
|
+
'Naguib', 'Nikolai', 'Octavio', 'Oscar', 'Pablo', 'Patricia', 'Philip', 'Ray',
|
|
132
|
+
'Rebecca', 'Robert', 'Rosa', 'Salman', 'Samuel', 'Simone', 'Sylvia', 'Thomas',
|
|
133
|
+
'Toni', 'Umberto', 'Ursula', 'Virginia', 'Vladimir', 'Voltaire', 'Walt', 'Willa',
|
|
134
|
+
'William', 'Zadie', 'Zora', 'Chinua', 'Chimamanda', 'Orhan', 'Kazuo', 'Haruki',
|
|
135
|
+
'Isabel', 'Italo', 'Yukio', 'Murasaki', 'Rabindranath', 'Rumi', 'Khalil', 'Nikos'
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
const AUTHOR_LAST_NAMES = [
|
|
139
|
+
'Achebe', 'Adichie', 'Allende', 'Amis', 'Angelou', 'Asimov', 'Atwood', 'Austen',
|
|
140
|
+
'Ballard', 'Balzac', 'Beckett', 'Borges', 'Bradbury', 'Brontë', 'Bulgakov', 'Calvino',
|
|
141
|
+
'Camus', 'Capote', 'Carver', 'Cervantes', 'Chandler', 'Chekhov', 'Christie', 'Coelho',
|
|
142
|
+
'Collins', 'Conrad', 'Cortázar', 'de Beauvoir', 'Dickens', 'Dostoevsky', 'Dumas', 'Eco',
|
|
143
|
+
'Eliot', 'Ellison', 'Faulkner', 'Fitzgerald', 'Flaubert', 'Forster', 'Franzen', 'García Márquez',
|
|
144
|
+
'Gibran', 'Goethe', 'Gogol', 'Golding', 'Grass', 'Greene', 'Hammett', 'Hardy',
|
|
145
|
+
'Heller', 'Hemingway', 'Hesse', 'Hugo', 'Hurston', 'Huxley', 'Irving', 'Ishiguro',
|
|
146
|
+
'James', 'Joyce', 'Kafka', 'Kazantzakis', 'Keats', 'Kerouac', 'Kipling', 'Kundera',
|
|
147
|
+
'Le Guin', 'Lee', 'Lessing', 'Lewis', 'London', 'Lovecraft', 'Mahfouz', 'Mailer',
|
|
148
|
+
'Mann', 'Maugham', 'McCarthy', 'McEwan', 'Melville', 'Miller', 'Mishima', 'Morrison',
|
|
149
|
+
'Murakami', 'Nabokov', 'Naipaul', 'Neruda', 'Oates', 'Orwell', 'Pamuk', 'Pasternak',
|
|
150
|
+
'Plath', 'Poe', 'Proust', 'Pynchon', 'Roth', 'Rushdie', 'Salinger', 'Saramago',
|
|
151
|
+
'Sartre', 'Shelley', 'Shikibu', 'Smith', 'Solzhenitsyn', 'Steinbeck', 'Stendhal', 'Tagore',
|
|
152
|
+
'Tolstoy', 'Twain', 'Updike', 'Verne', 'Vonnegut', 'Walker', 'Waugh', 'Welty',
|
|
153
|
+
'Wharton', 'Whitman', 'Wilde', 'Woolf', 'Wright', 'Yeats', 'Zola'
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
const GENRES = [
|
|
157
|
+
'Fiction', 'Literary Fiction', 'Science Fiction', 'Fantasy', 'Mystery', 'Thriller',
|
|
158
|
+
'Romance', 'Horror', 'Historical Fiction', 'Adventure', 'Dystopian', 'Gothic',
|
|
159
|
+
'Magical Realism', 'Satire', 'Western', 'Crime', 'Noir', 'Cyberpunk',
|
|
160
|
+
'Steampunk', 'Post-Apocalyptic', 'Absurdist', 'Philosophical', 'Humor',
|
|
161
|
+
'Young Adult', 'Graphic Novel', 'Memoir', 'Biography', 'True Crime',
|
|
162
|
+
'Self-Help', 'Poetry', 'Drama', 'Essay Collection'
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
const BOOK_TYPES = ['Hardcover', 'Paperback', 'E-Book', 'Audiobook'];
|
|
166
|
+
|
|
167
|
+
const LANGUAGES = ['en', 'es', 'fr', 'de', 'it', 'pt', 'ru', 'ja', 'zh', 'ko', 'ar', 'hi'];
|
|
168
|
+
|
|
169
|
+
// ================================================================
|
|
170
|
+
// Hilarious Book Title Generation
|
|
171
|
+
// ================================================================
|
|
172
|
+
function gerund(pVerb)
|
|
173
|
+
{
|
|
174
|
+
if (pVerb.endsWith('e')) return pVerb.slice(0, -1) + 'ing';
|
|
175
|
+
return pVerb + 'ing';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const TITLE_PATTERNS = [
|
|
179
|
+
() => `The ${pick(TITLE_ADJ)} ${pick(TITLE_NOUN)}`,
|
|
180
|
+
() => `${pick(TITLE_NOUN)} of the ${pick(TITLE_ADJ)} ${pick(TITLE_NOUN2)}`,
|
|
181
|
+
() => `A Brief History of ${pick(TITLE_ABSURD)}`,
|
|
182
|
+
() => `${gerund(pick(TITLE_VERB))} the ${pick(TITLE_NOUN)}`,
|
|
183
|
+
() => `The ${pick(TITLE_ADJ)} ${pick(TITLE_NOUN)}'s Guide to ${pick(TITLE_ABSURD)}`,
|
|
184
|
+
() => `Why ${pick(TITLE_NOUN2)}s ${pick(TITLE_VERB)} at Night`,
|
|
185
|
+
() => `${pick(TITLE_NUMBER)} Shades of ${pick(TITLE_COLOR)}`,
|
|
186
|
+
() => `The Secret Life of ${pick(TITLE_NOUN2)}s`,
|
|
187
|
+
() => `${pick(TITLE_NOUN2)} and ${pick(TITLE_NOUN2)}: A Love Story`,
|
|
188
|
+
() => `How to ${pick(TITLE_VERB)} Your ${pick(TITLE_NOUN2)} in ${pick(TITLE_NUMBER)} Easy Steps`,
|
|
189
|
+
() => `The ${pick(TITLE_ADJ)} ${pick(TITLE_NOUN2)} Who ${pick(TITLE_VERB_PAST)} Too Much`,
|
|
190
|
+
() => `Confessions of a ${pick(TITLE_ADJ)} ${pick(TITLE_NOUN2)}`,
|
|
191
|
+
() => `Don't ${pick(TITLE_VERB)} the ${pick(TITLE_NOUN2)}`,
|
|
192
|
+
() => `The ${pick(TITLE_NOUN2)} Who ${pick(TITLE_VERB_PAST)} ${pick(TITLE_PLACE)}`,
|
|
193
|
+
() => `${pick(TITLE_NOUN2)}: The Untold Story`,
|
|
194
|
+
() => `Everything You Wanted to Know About ${pick(TITLE_ABSURD)} But Were Afraid to Ask`,
|
|
195
|
+
() => `${pick(TITLE_ADJ)} ${pick(TITLE_ABSURD)} and Other Lies I Tell Myself`,
|
|
196
|
+
() => `The ${pick(TITLE_ADJ)} Art of ${gerund(pick(TITLE_VERB))} ${pick(TITLE_ABSURD)}`,
|
|
197
|
+
() => `${pick(TITLE_EXCLAIM)}! A ${pick(TITLE_NOUN2)}'s Memoir`,
|
|
198
|
+
() => `My ${pick(TITLE_NOUN2)} Is a ${pick(TITLE_ADJ)} ${pick(TITLE_NOUN)}`,
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
const TITLE_ADJ = [
|
|
202
|
+
'Melancholy', 'Effervescent', 'Lugubrious', 'Phantasmagorical', 'Quixotic', 'Indolent',
|
|
203
|
+
'Thunderous', 'Preposterous', 'Magnificent', 'Bewildered', 'Flatulent', 'Suspicious',
|
|
204
|
+
'Cantankerous', 'Discombobulated', 'Flamboyant', 'Querulous', 'Inscrutable', 'Luminous',
|
|
205
|
+
'Persnickety', 'Obstreperous', 'Crepuscular', 'Sesquipedalian', 'Defenestrated', 'Bamboozled',
|
|
206
|
+
'Flabbergasted', 'Gobsmacked', 'Hornswoggled', 'Befuddled', 'Perpendicular', 'Translucent'
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
const TITLE_NOUN = [
|
|
210
|
+
'Dinghy', 'Archipelago', 'Quandary', 'Haberdashery', 'Catacombs', 'Brouhaha',
|
|
211
|
+
'Pandemonium', 'Labyrinth', 'Contraption', 'Kerfuffle', 'Rigmarole', 'Hullabaloo',
|
|
212
|
+
'Phenomenon', 'Colosseum', 'Symposium', 'Conspiracy', 'Kaleidoscope', 'Anthology',
|
|
213
|
+
'Emporium', 'Compendium', 'Conundrum', 'Extravaganza', 'Fiasco', 'Jamboree'
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
const TITLE_NOUN2 = [
|
|
217
|
+
'Badger', 'Platypus', 'Walrus', 'Accountant', 'Pirate', 'Librarian', 'Iguana',
|
|
218
|
+
'Astronaut', 'Gondolier', 'Yeti', 'Penguin', 'Taxidermist', 'Sausage', 'Kumquat',
|
|
219
|
+
'Narwhal', 'Flamingo', 'Hedgehog', 'Beekeeper', 'Cartographer', 'Sommelier',
|
|
220
|
+
'Wombat', 'Capybara', 'Sasquatch', 'Pudding', 'Hamster', 'Philosopher',
|
|
221
|
+
'Lumberjack', 'Manatee', 'Barista', 'Zeppelin', 'Turnip', 'Octopus'
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
const TITLE_VERB = [
|
|
225
|
+
'Juggle', 'Contemplate', 'Serenade', 'Befriend', 'Investigate', 'Negotiate',
|
|
226
|
+
'Interrogate', 'Catapult', 'Marinate', 'Alphabetize', 'Domesticate', 'Hypnotize',
|
|
227
|
+
'Procrastinate', 'Discombobulate', 'Bamboozle', 'Exfoliate', 'Gallivant', 'Perambulate'
|
|
228
|
+
];
|
|
229
|
+
|
|
230
|
+
const TITLE_VERB_PAST = [
|
|
231
|
+
'Juggled', 'Contemplated', 'Serenaded', 'Befriended', 'Investigated', 'Negotiated',
|
|
232
|
+
'Catapulted', 'Marinated', 'Alphabetized', 'Domesticated', 'Hypnotized', 'Vanquished',
|
|
233
|
+
'Bamboozled', 'Exfoliated', 'Gallivanted', 'Perambulated', 'Discombobulated', 'Yodeled'
|
|
234
|
+
];
|
|
235
|
+
|
|
236
|
+
const TITLE_ABSURD = [
|
|
237
|
+
'Quantum Socks', 'Interdimensional Cheese', 'Existential Laundry', 'Competitive Napping',
|
|
238
|
+
'Artisanal Dust', 'Gluten-Free Darkness', 'Organic Regret', 'Dehydrated Water',
|
|
239
|
+
'Bureaucratic Ecstasy', 'Weaponized Politeness', 'Industrial Whimsy', 'Tactical Knitting',
|
|
240
|
+
'Philosophical Plumbing', 'Militant Optimism', 'Acoustic Gravity', 'Vintage Nonsense',
|
|
241
|
+
'Performative Confusion', 'Recreational Mathematics', 'Theoretical Sandwiches', 'Applied Daydreaming',
|
|
242
|
+
'Aggressive Tranquility', 'Passive-Aggressive Furniture', 'Hyperbolic Geometry', 'Sentient Yogurt'
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
const TITLE_COLOR = [
|
|
246
|
+
'Beige', 'Taupe', 'Chartreuse', 'Puce', 'Mauve', 'Ecru', 'Cerulean', 'Vermillion',
|
|
247
|
+
'Ochre', 'Magenta', 'Periwinkle', 'Maroon', 'Turquoise', 'Burgundy'
|
|
248
|
+
];
|
|
249
|
+
|
|
250
|
+
const TITLE_NUMBER = [
|
|
251
|
+
'Seventeen', 'Forty-Two', 'Three-and-a-Half', 'Ninety-Nine', 'Seven Hundred',
|
|
252
|
+
'Approximately Twelve', 'Pi', 'A Disturbing Number of', 'Far Too Many', 'Negative Six'
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
const TITLE_PLACE = [
|
|
256
|
+
'at Midnight', 'in Prague', 'Under the Stairs', 'Behind the Fridge', 'in a Canoe',
|
|
257
|
+
'on the Moon', 'During Brunch', 'in the Produce Aisle', 'at the DMV', 'Underwater'
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
const TITLE_EXCLAIM = [
|
|
261
|
+
'Zounds', 'Great Scott', 'Good Grief', 'Thunderation', 'Egads', 'Crikey',
|
|
262
|
+
'Balderdash', 'Fiddlesticks', 'Blimey', 'Sacré Bleu', 'Heavens to Betsy'
|
|
263
|
+
];
|
|
264
|
+
|
|
265
|
+
// ================================================================
|
|
266
|
+
// Hilarious Reviewer Personas and Review Templates
|
|
267
|
+
// ================================================================
|
|
268
|
+
const REVIEWER_PERSONAS = [
|
|
269
|
+
{ first: 'Karen', last: 'Managerdemander', bio: 'Professional complainer, amateur sommelier' },
|
|
270
|
+
{ first: 'Chad', last: 'Thunderflex', bio: 'Reads exclusively while doing bicep curls' },
|
|
271
|
+
{ first: 'Gertrude', last: 'Miffington', bio: 'Has been personally offended by every book since 1987' },
|
|
272
|
+
{ first: 'Bartholomew', last: 'Snifflebutt', bio: 'PhD in Unnecessarily Long Reviews' },
|
|
273
|
+
{ first: 'Dolores', last: 'Umbridge-Adjacent', bio: 'Gives 1 star if the font displeases her' },
|
|
274
|
+
{ first: 'Reginald', last: 'Pompington III', bio: 'Only reads leather-bound first editions, darling' },
|
|
275
|
+
{ first: 'Brenda', last: 'Spoilerman', bio: 'Has never not spoiled an ending in a review' },
|
|
276
|
+
{ first: 'Eugene', last: 'Tangentsworth', bio: 'Starts reviewing the book but ends up talking about soup' },
|
|
277
|
+
{ first: 'Mildred', last: 'Catladyton', bio: 'Reviews all books from the perspective of her 17 cats' },
|
|
278
|
+
{ first: 'Derek', last: 'One-Star', bio: 'Has literally never given anything more than one star' },
|
|
279
|
+
{ first: 'Penelope', last: 'Plottwist', bio: 'Insists every book should have ended differently' },
|
|
280
|
+
{ first: 'Harold', last: 'Didnt-Read', bio: 'Reviews books based solely on the cover' },
|
|
281
|
+
{ first: 'Francesca', last: 'Dramática', bio: 'Describes every book as if it changed her molecular structure' },
|
|
282
|
+
{ first: 'Norman', last: 'Footnote', bio: 'Reviews consist entirely of pedantic corrections' },
|
|
283
|
+
{ first: 'Agatha', last: 'Conspiracy', bio: 'Finds hidden messages in every ISBN number' },
|
|
284
|
+
{ first: 'Chester', last: 'Audiobook', bio: 'Only listens at 4x speed, very confused always' },
|
|
285
|
+
{ first: 'Patricia', last: 'Wrong-Genre', bio: 'Keeps reviewing sci-fi books as if they were cookbooks' },
|
|
286
|
+
{ first: 'Mortimer', last: 'Lengthy', bio: 'His reviews are always longer than the actual book' },
|
|
287
|
+
{ first: 'Daphne', last: 'Emoji-Only', bio: 'Expresses all literary criticism through emoji descriptions' },
|
|
288
|
+
{ first: 'Cornelius', last: 'Off-Topic', bio: 'Every review somehow becomes about his neighbor\'s lawn' },
|
|
289
|
+
{ first: 'Gladys', last: 'Humblebrag', bio: 'Every review mentions her beach house in the Hamptons' },
|
|
290
|
+
{ first: 'Lester', last: 'Misprint', bio: 'Found a typo on page 347 and will never recover' },
|
|
291
|
+
{ first: 'Wanda', last: 'Five-Stars', bio: 'Gives everything five stars including parking tickets' },
|
|
292
|
+
{ first: 'Frank', last: 'Spooked', bio: 'Is frightened by all genres including self-help' },
|
|
293
|
+
{ first: 'Eunice', last: 'Metaphor', bio: 'Speaks exclusively in increasingly strained metaphors' },
|
|
294
|
+
{ first: 'Percival', last: 'Snooze-Critic', bio: 'Rates books by how well they put him to sleep' },
|
|
295
|
+
{ first: 'Tabitha', last: 'Bookclub', bio: 'Her bookclub is a front for her wine drinking operation' },
|
|
296
|
+
{ first: 'Vincent', last: 'Margins', bio: 'Has strong opinions exclusively about kerning and margins' },
|
|
297
|
+
{ first: 'Maude', last: 'Nostalgia', bio: 'Everything was better in a decade she wasn\'t alive for' },
|
|
298
|
+
{ first: 'Nigel', last: 'Pedantic', bio: 'Actually, it\'s not a novel, it\'s a novella. Actually...' },
|
|
299
|
+
{ first: 'Bunny', last: 'Chaos', bio: 'Returns library books to random shelves for sport' },
|
|
300
|
+
{ first: 'Gerald', last: 'Overthink', bio: 'Still processing the implications of Green Eggs and Ham' },
|
|
301
|
+
{ first: 'Helga', last: 'Booksmell', bio: 'Rates books 40% on content, 60% on the smell of the pages' },
|
|
302
|
+
{ first: 'Clive', last: 'Clickbait', bio: 'You WON\'T BELIEVE what he thinks about chapter seven' },
|
|
303
|
+
{ first: 'Doris', last: 'Dewey-Decimal', bio: 'Has strong feelings about the classification of this work' },
|
|
304
|
+
{ first: 'Rufus', last: 'Dog-Ears', bio: 'Dog-ears every single page, fights anyone who objects' },
|
|
305
|
+
{ first: 'Priscilla', last: 'Microphone', bio: 'Reads aloud in coffee shops to assert dominance' },
|
|
306
|
+
{ first: 'Ambrose', last: 'Thesaurus', bio: 'Never uses a simple word when a grandiloquent one will suffice' },
|
|
307
|
+
{ first: 'Blanche', last: 'Snack-Reader', bio: 'Cannot separate the reading experience from the snacks consumed' },
|
|
308
|
+
{ first: 'Octavius', last: 'Sequel-Demand', bio: 'Demands a sequel to every book, including dictionaries' },
|
|
309
|
+
];
|
|
310
|
+
|
|
311
|
+
const REVIEW_TEMPLATES_POSITIVE = [
|
|
312
|
+
(pBook) => `This book spoke to my soul. My soul then asked it to keep it down because it was trying to nap.`,
|
|
313
|
+
(pBook) => `I laughed, I cried, I accidentally read the whole thing in the bathtub and now my copy is wavy.`,
|
|
314
|
+
(pBook) => `Absolutely magnificent. I've recommended it to everyone I know, and several people I don't.`,
|
|
315
|
+
(pBook) => `Five stars. Would have given six but the rating system is an oppressive construct.`,
|
|
316
|
+
(pBook) => `I finished this book three weeks ago and I still can't talk about it without getting emotional. My therapist is concerned.`,
|
|
317
|
+
(pBook) => `Better than anything I could write, and I once wrote a strongly worded letter to my HOA that made a grown man cry.`,
|
|
318
|
+
(pBook) => `This is the book I didn't know I needed, much like that third slice of pie at 2am.`,
|
|
319
|
+
(pBook) => `I was so engrossed I missed my subway stop. Twice. I don't even take the subway.`,
|
|
320
|
+
(pBook) => `A tour de force. A chef-d'oeuvre. A magnum opus. I've been reading the thesaurus again, can you tell?`,
|
|
321
|
+
(pBook) => `Every sentence is a gift. Every paragraph, a revelation. Every chapter, an excuse to ignore my responsibilities.`,
|
|
322
|
+
(pBook) => `I read this in one sitting. My chiropractor says that was a mistake, but my heart says otherwise.`,
|
|
323
|
+
(pBook) => `Changed my perspective on life, love, and the correct way to load a dishwasher.`,
|
|
324
|
+
(pBook) => `Gave this to my book club. We were supposed to discuss it for an hour. It's been six months. We're still going.`,
|
|
325
|
+
(pBook) => `My cat walked across the keyboard to type this: kkkkkjjjjj. She agrees it's phenomenal.`,
|
|
326
|
+
(pBook) => `I highlighted so many passages my book is now entirely yellow. It's basically a legal pad at this point.`,
|
|
327
|
+
];
|
|
328
|
+
|
|
329
|
+
const REVIEW_TEMPLATES_NEGATIVE = [
|
|
330
|
+
(pBook) => `I've read better prose on shampoo bottles. At least those have clear instructions.`,
|
|
331
|
+
(pBook) => `One star. The only thing this book is good for is leveling my wobbly table, and even then the table complained.`,
|
|
332
|
+
(pBook) => `I want the hours of my life back. And a formal apology. In writing. Notarized.`,
|
|
333
|
+
(pBook) => `My neighbor's lawn looks better than this plot, and his lawn is TERRIBLE. Jeff, if you're reading this, mow it.`,
|
|
334
|
+
(pBook) => `DNF. Did Not Finish. Did Not Enjoy. Did Not understand why this exists.`,
|
|
335
|
+
(pBook) => `The author clearly wrote this during a fever dream and then just... published it? In THIS economy?`,
|
|
336
|
+
(pBook) => `I found a typo on page 12 and honestly I never recovered. The trust was broken.`,
|
|
337
|
+
(pBook) => `This book is the literary equivalent of stepping on a LEGO in the dark.`,
|
|
338
|
+
(pBook) => `My reading group selected this. We are no longer a reading group. We are a support group.`,
|
|
339
|
+
(pBook) => `I've had more compelling narratives from my dishwasher's error codes.`,
|
|
340
|
+
(pBook) => `Put me to sleep faster than melatonin. Actually, I should thank the author. I haven't slept that well in years.`,
|
|
341
|
+
(pBook) => `I got this for free and I still feel overcharged.`,
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
const REVIEW_TEMPLATES_MEDIUM = [
|
|
345
|
+
(pBook) => `Three stars. It was fine. Like airport sushi — technically edible but you have questions.`,
|
|
346
|
+
(pBook) => `A solid meh. Not bad enough to be interesting, not good enough to be memorable.`,
|
|
347
|
+
(pBook) => `I've read worse. I've also read better. I've also read the back of cereal boxes, which was comparable.`,
|
|
348
|
+
(pBook) => `The first half was brilliant. The second half was... present. It was definitely there.`,
|
|
349
|
+
(pBook) => `It's the kind of book you read once, go "huh," and then forget about until someone mentions it at a party.`,
|
|
350
|
+
(pBook) => `Three stars. Lost one star for the plot twist I saw coming from chapter two. Lost another because my sandwich got soggy while reading.`,
|
|
351
|
+
(pBook) => `Some parts made me think. Other parts made me think about what I was going to have for dinner.`,
|
|
352
|
+
(pBook) => `I'll say this: it's a book. It has pages. Words are on those pages. Some of them are in the right order.`,
|
|
353
|
+
(pBook) => `Perfectly adequate. Like a beige wall. You don't hate it, but you're not hanging a frame around it either.`,
|
|
354
|
+
(pBook) => `Would I recommend it? Maybe. Would I recommend it enthusiastically? I'd recommend it at room temperature.`,
|
|
355
|
+
];
|
|
356
|
+
|
|
357
|
+
const REVIEW_TEMPLATES_CHAOTIC = [
|
|
358
|
+
(pBook) => `I haven't read this but the cover is blue and I'm a Sagittarius so three stars.`,
|
|
359
|
+
(pBook) => `Great product! Shipping was fast. Oh wait, this is a book review? Same rating though.`,
|
|
360
|
+
(pBook) => `I bought this thinking it was a cookbook. It is NOT a cookbook. Made the worst soup of my life from chapter 3.`,
|
|
361
|
+
(pBook) => `I listened to this audiobook at 4x speed. I now speak exclusively in plot summaries and I can't stop.`,
|
|
362
|
+
(pBook) => `My dog ate this book. Literally. He seemed to enjoy it more than I did. Giving him the review account.`,
|
|
363
|
+
(pBook) => `Is this the one with the dragon? No? Then why did I read the whole thing thinking there'd be a dragon?`,
|
|
364
|
+
(pBook) => `I read this during a power outage by candlelight and it was much more dramatic. Five stars for ambiance.`,
|
|
365
|
+
(pBook) => `The ISBN contains the number 7 three times. Coincidence? I think not. There's something going on here.`,
|
|
366
|
+
(pBook) => `My book club told me to read this. Joke's on them, I went to the wrong meeting and reviewed a different book entirely. Keeping the review.`,
|
|
367
|
+
(pBook) => `I sneezed on page 47 and the book didn't say bless you. Rude. Deducting a star.`,
|
|
368
|
+
(pBook) => `Accidentally brought this to jury duty instead of my actual reading. The defendant's lawyer was interested in my thoughts.`,
|
|
369
|
+
(pBook) => `If this book were a sandwich, it would be one of those sandwiches where the bread is also a sandwich.`,
|
|
370
|
+
(pBook) => `The font on page 193 is slightly different and now I can't unsee it. This is a cry for help.`,
|
|
371
|
+
];
|
|
372
|
+
|
|
373
|
+
// ================================================================
|
|
374
|
+
// Generator
|
|
375
|
+
// ================================================================
|
|
376
|
+
|
|
377
|
+
console.log('Generating bookstore comprehension data...');
|
|
378
|
+
let tmpStart = Date.now();
|
|
379
|
+
|
|
380
|
+
// --- Authors (10,000) ---
|
|
381
|
+
console.log(' Generating 10,000 authors...');
|
|
382
|
+
let tmpAuthors = [];
|
|
383
|
+
let tmpAuthorNameSet = new Set();
|
|
384
|
+
|
|
385
|
+
for (let i = 1; i <= 10000; i++)
|
|
386
|
+
{
|
|
387
|
+
let tmpFirst, tmpLast, tmpFullName;
|
|
388
|
+
do
|
|
389
|
+
{
|
|
390
|
+
tmpFirst = pick(AUTHOR_FIRST_NAMES);
|
|
391
|
+
tmpLast = pick(AUTHOR_LAST_NAMES);
|
|
392
|
+
// Add an optional suffix for uniqueness
|
|
393
|
+
if (i > 2000)
|
|
394
|
+
{
|
|
395
|
+
let tmpSuffixes = ['', ' Jr.', ' Sr.', ' II', ' III', ' IV', ' von ' + pick(LAST_NAMES)];
|
|
396
|
+
tmpLast = tmpLast + pick(tmpSuffixes);
|
|
397
|
+
}
|
|
398
|
+
tmpFullName = tmpFirst + ' ' + tmpLast;
|
|
399
|
+
}
|
|
400
|
+
while (tmpAuthorNameSet.has(tmpFullName));
|
|
401
|
+
tmpAuthorNameSet.add(tmpFullName);
|
|
402
|
+
|
|
403
|
+
tmpAuthors.push(
|
|
404
|
+
{
|
|
405
|
+
IDAuthor: i,
|
|
406
|
+
GUIDAuthor: uuid(),
|
|
407
|
+
CreateDate: randomDate(2015, 2024),
|
|
408
|
+
CreatingIDUser: 1,
|
|
409
|
+
UpdateDate: randomDate(2024, 2025),
|
|
410
|
+
UpdatingIDUser: 1,
|
|
411
|
+
Deleted: 0,
|
|
412
|
+
DeleteDate: null,
|
|
413
|
+
DeletingIDUser: 0,
|
|
414
|
+
Name: tmpFullName,
|
|
415
|
+
IDUser: 0,
|
|
416
|
+
IDCustomer: 1
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// --- Books (10,000) ---
|
|
421
|
+
console.log(' Generating 10,000 books...');
|
|
422
|
+
let tmpBooks = [];
|
|
423
|
+
let tmpBookTitleSet = new Set();
|
|
424
|
+
|
|
425
|
+
for (let i = 1; i <= 10000; i++)
|
|
426
|
+
{
|
|
427
|
+
let tmpTitle;
|
|
428
|
+
do
|
|
429
|
+
{
|
|
430
|
+
tmpTitle = pick(TITLE_PATTERNS)();
|
|
431
|
+
}
|
|
432
|
+
while (tmpBookTitleSet.has(tmpTitle));
|
|
433
|
+
tmpBookTitleSet.add(tmpTitle);
|
|
434
|
+
|
|
435
|
+
let tmpGenre = pick(GENRES);
|
|
436
|
+
let tmpType = pick(BOOK_TYPES);
|
|
437
|
+
let tmpLang = pick(LANGUAGES);
|
|
438
|
+
let tmpYear = randInt(1850, 2025);
|
|
439
|
+
let tmpISBN = '978-' + randInt(0, 9) + '-' + String(randInt(100, 9999)).padStart(4, '0') + '-' + String(randInt(1000, 9999)) + '-' + randInt(0, 9);
|
|
440
|
+
|
|
441
|
+
tmpBooks.push(
|
|
442
|
+
{
|
|
443
|
+
IDBook: i,
|
|
444
|
+
GUIDBook: uuid(),
|
|
445
|
+
CreateDate: randomDate(2015, 2024),
|
|
446
|
+
CreatingIDUser: 1,
|
|
447
|
+
UpdateDate: randomDate(2024, 2025),
|
|
448
|
+
UpdatingIDUser: 1,
|
|
449
|
+
Deleted: 0,
|
|
450
|
+
DeleteDate: null,
|
|
451
|
+
DeletingIDUser: 0,
|
|
452
|
+
Title: tmpTitle,
|
|
453
|
+
Type: tmpType,
|
|
454
|
+
Genre: tmpGenre,
|
|
455
|
+
ISBN: tmpISBN,
|
|
456
|
+
Language: tmpLang,
|
|
457
|
+
ImageURL: `https://covers.example.com/books/${i}.jpg`,
|
|
458
|
+
PublicationYear: tmpYear,
|
|
459
|
+
IDCustomer: 1
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// --- BookAuthorJoin (~15,000) ---
|
|
464
|
+
console.log(' Generating ~15,000 book-author joins...');
|
|
465
|
+
let tmpJoins = [];
|
|
466
|
+
let tmpJoinID = 1;
|
|
467
|
+
|
|
468
|
+
for (let i = 0; i < tmpBooks.length; i++)
|
|
469
|
+
{
|
|
470
|
+
let tmpBook = tmpBooks[i];
|
|
471
|
+
// Most books have 1 author, some have 2-3 co-authors
|
|
472
|
+
let tmpAuthorCount = 1;
|
|
473
|
+
let tmpRoll = nextRandom();
|
|
474
|
+
if (tmpRoll > 0.65) tmpAuthorCount = 2;
|
|
475
|
+
if (tmpRoll > 0.90) tmpAuthorCount = 3;
|
|
476
|
+
|
|
477
|
+
let tmpAuthorIDs = pickN(
|
|
478
|
+
Array.from({ length: 10000 }, (_, k) => k + 1),
|
|
479
|
+
tmpAuthorCount
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
for (let a = 0; a < tmpAuthorIDs.length; a++)
|
|
483
|
+
{
|
|
484
|
+
tmpJoins.push(
|
|
485
|
+
{
|
|
486
|
+
IDBookAuthorJoin: tmpJoinID++,
|
|
487
|
+
GUIDBookAuthorJoin: uuid(),
|
|
488
|
+
IDBook: tmpBook.IDBook,
|
|
489
|
+
IDAuthor: tmpAuthorIDs[a],
|
|
490
|
+
IDCustomer: 1
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// --- Hilarious Reviewers (Users) ---
|
|
496
|
+
console.log(' Generating hilarious reviewer personas...');
|
|
497
|
+
let tmpUsers = [];
|
|
498
|
+
let tmpReviewerCount = REVIEWER_PERSONAS.length;
|
|
499
|
+
|
|
500
|
+
// Generate the named personas first
|
|
501
|
+
for (let i = 0; i < tmpReviewerCount; i++)
|
|
502
|
+
{
|
|
503
|
+
let tmpPersona = REVIEWER_PERSONAS[i];
|
|
504
|
+
tmpUsers.push(
|
|
505
|
+
{
|
|
506
|
+
IDUser: i + 1,
|
|
507
|
+
GUIDUser: i + 1,
|
|
508
|
+
LoginID: (tmpPersona.first.toLowerCase() + '.' + tmpPersona.last.toLowerCase()).replace(/[^a-z.]/g, ''),
|
|
509
|
+
Password: '',
|
|
510
|
+
NameFirst: tmpPersona.first,
|
|
511
|
+
NameLast: tmpPersona.last,
|
|
512
|
+
FullName: tmpPersona.first + ' ' + tmpPersona.last,
|
|
513
|
+
Config: '',
|
|
514
|
+
IDCustomer: 1,
|
|
515
|
+
Email: tmpPersona.first.toLowerCase() + '@' + tmpPersona.last.toLowerCase().replace(/[^a-z]/g, '') + '.reviews',
|
|
516
|
+
Phone: '',
|
|
517
|
+
Address: '',
|
|
518
|
+
City: '',
|
|
519
|
+
State: '',
|
|
520
|
+
Postal: '',
|
|
521
|
+
Country: ''
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Generate 160 more silly randomized reviewers to reach ~200
|
|
526
|
+
for (let i = tmpReviewerCount; i < 200; i++)
|
|
527
|
+
{
|
|
528
|
+
let tmpFirst = pick(FIRST_NAMES);
|
|
529
|
+
let tmpLast = pick(LAST_NAMES);
|
|
530
|
+
tmpUsers.push(
|
|
531
|
+
{
|
|
532
|
+
IDUser: i + 1,
|
|
533
|
+
GUIDUser: i + 1,
|
|
534
|
+
LoginID: (tmpFirst.toLowerCase() + '.' + tmpLast.toLowerCase()).replace(/[^a-z.]/g, ''),
|
|
535
|
+
Password: '',
|
|
536
|
+
NameFirst: tmpFirst,
|
|
537
|
+
NameLast: tmpLast,
|
|
538
|
+
FullName: tmpFirst + ' ' + tmpLast,
|
|
539
|
+
Config: '',
|
|
540
|
+
IDCustomer: 1,
|
|
541
|
+
Email: tmpFirst.toLowerCase() + '@' + tmpLast.toLowerCase().replace(/[^a-z]/g, '') + '.reviews',
|
|
542
|
+
Phone: '',
|
|
543
|
+
Address: '',
|
|
544
|
+
City: '',
|
|
545
|
+
State: '',
|
|
546
|
+
Postal: '',
|
|
547
|
+
Country: ''
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// --- Reviews (~25,000) ---
|
|
552
|
+
console.log(' Generating ~25,000 reviews...');
|
|
553
|
+
let tmpReviews = [];
|
|
554
|
+
let tmpReviewID = 1;
|
|
555
|
+
let tmpAllReviewTemplates = [].concat(
|
|
556
|
+
REVIEW_TEMPLATES_POSITIVE,
|
|
557
|
+
REVIEW_TEMPLATES_NEGATIVE,
|
|
558
|
+
REVIEW_TEMPLATES_MEDIUM,
|
|
559
|
+
REVIEW_TEMPLATES_CHAOTIC
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
for (let i = 0; i < tmpBooks.length; i++)
|
|
563
|
+
{
|
|
564
|
+
let tmpBook = tmpBooks[i];
|
|
565
|
+
// Each book gets 0-5 reviews, averaging ~2.5
|
|
566
|
+
let tmpReviewCount = randInt(0, 5);
|
|
567
|
+
|
|
568
|
+
for (let r = 0; r < tmpReviewCount; r++)
|
|
569
|
+
{
|
|
570
|
+
let tmpUserID = randInt(1, 200);
|
|
571
|
+
let tmpRating;
|
|
572
|
+
let tmpTemplateSet;
|
|
573
|
+
|
|
574
|
+
let tmpTemplateRoll = nextRandom();
|
|
575
|
+
if (tmpTemplateRoll < 0.30)
|
|
576
|
+
{
|
|
577
|
+
tmpRating = randInt(4, 5);
|
|
578
|
+
tmpTemplateSet = REVIEW_TEMPLATES_POSITIVE;
|
|
579
|
+
}
|
|
580
|
+
else if (tmpTemplateRoll < 0.50)
|
|
581
|
+
{
|
|
582
|
+
tmpRating = randInt(1, 2);
|
|
583
|
+
tmpTemplateSet = REVIEW_TEMPLATES_NEGATIVE;
|
|
584
|
+
}
|
|
585
|
+
else if (tmpTemplateRoll < 0.75)
|
|
586
|
+
{
|
|
587
|
+
tmpRating = 3;
|
|
588
|
+
tmpTemplateSet = REVIEW_TEMPLATES_MEDIUM;
|
|
589
|
+
}
|
|
590
|
+
else
|
|
591
|
+
{
|
|
592
|
+
tmpRating = randInt(1, 5);
|
|
593
|
+
tmpTemplateSet = REVIEW_TEMPLATES_CHAOTIC;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
let tmpTemplate = pick(tmpTemplateSet);
|
|
597
|
+
let tmpText = tmpTemplate(tmpBook);
|
|
598
|
+
|
|
599
|
+
tmpReviews.push(
|
|
600
|
+
{
|
|
601
|
+
IDReview: tmpReviewID++,
|
|
602
|
+
GUIDReview: uuid(),
|
|
603
|
+
CreateDate: randomDate(2018, 2025),
|
|
604
|
+
CreatingIDUser: tmpUserID,
|
|
605
|
+
UpdateDate: randomDate(2024, 2025),
|
|
606
|
+
UpdatingIDUser: tmpUserID,
|
|
607
|
+
Deleted: 0,
|
|
608
|
+
DeleteDate: null,
|
|
609
|
+
DeletingIDUser: 0,
|
|
610
|
+
Text: tmpText,
|
|
611
|
+
Rating: tmpRating,
|
|
612
|
+
IDBook: tmpBook.IDBook,
|
|
613
|
+
IDUser: tmpUserID,
|
|
614
|
+
IDCustomer: 1
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// ================================================================
|
|
620
|
+
// Assemble Comprehension
|
|
621
|
+
// ================================================================
|
|
622
|
+
let tmpComprehension =
|
|
623
|
+
{
|
|
624
|
+
Author: tmpAuthors,
|
|
625
|
+
Book: tmpBooks,
|
|
626
|
+
BookAuthorJoin: tmpJoins,
|
|
627
|
+
User: tmpUsers,
|
|
628
|
+
Review: tmpReviews
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
let tmpElapsed = ((Date.now() - tmpStart) / 1000).toFixed(1);
|
|
632
|
+
console.log(`\nComprehension generated in ${tmpElapsed}s:`);
|
|
633
|
+
console.log(` Authors: ${tmpAuthors.length.toLocaleString()}`);
|
|
634
|
+
console.log(` Books: ${tmpBooks.length.toLocaleString()}`);
|
|
635
|
+
console.log(` BookAuthorJoins: ${tmpJoins.length.toLocaleString()}`);
|
|
636
|
+
console.log(` Users (Reviewers):${tmpUsers.length.toLocaleString()}`);
|
|
637
|
+
console.log(` Reviews: ${tmpReviews.length.toLocaleString()}`);
|
|
638
|
+
console.log(` Total records: ${(tmpAuthors.length + tmpBooks.length + tmpJoins.length + tmpUsers.length + tmpReviews.length).toLocaleString()}`);
|
|
639
|
+
|
|
640
|
+
let tmpOutputPath = libPath.join(__dirname, 'bookstore-comprehension.json');
|
|
641
|
+
console.log(`\nWriting to ${tmpOutputPath}...`);
|
|
642
|
+
libFS.writeFileSync(tmpOutputPath, JSON.stringify(tmpComprehension, null, '\t'));
|
|
643
|
+
|
|
644
|
+
let tmpFileSizeMB = (libFS.statSync(tmpOutputPath).size / 1048576).toFixed(1);
|
|
645
|
+
console.log(`Done! (${tmpFileSizeMB} MB)`);
|