Commit 547146b2 by Иван Кубота

Add lots of tags

General questionBits: clustering by tags getUniqComponents(Products[]) subdivideComponents(from: Products[], sub: Products[]) 2 new question types Multiword intersecting filtering in UI FLAG for using subsubcategory instead of tags D.join now pass index to called fn
parent d7c6d211
Pipeline #511 canceled with stage
......@@ -1018,5 +1018,575 @@
"type": 0,
"eid": 78,
"tag": 29
},
{
"cid": 173,
"type": 0,
"eid": 1,
"tag": 30
},
{
"cid": 174,
"type": 0,
"eid": 2,
"tag": 30
},
{
"cid": 175,
"type": 0,
"eid": 3,
"tag": 30
},
{
"cid": 176,
"type": 0,
"eid": 4,
"tag": 30
},
{
"cid": 177,
"type": 0,
"eid": 5,
"tag": 30
},
{
"cid": 178,
"type": 0,
"eid": 1,
"tag": 31
},
{
"cid": 179,
"type": 0,
"eid": 3,
"tag": 31
},
{
"cid": 180,
"type": 0,
"eid": 3,
"tag": 32
},
{
"cid": 181,
"type": 0,
"eid": 31,
"tag": 32
},
{
"cid": 182,
"type": 0,
"eid": 34,
"tag": 32
},
{
"cid": 183,
"type": 0,
"eid": 35,
"tag": 32
},
{
"cid": 184,
"type": 0,
"eid": 58,
"tag": 32
},
{
"cid": 185,
"type": 0,
"eid": 61,
"tag": 32
},
{
"cid": 186,
"type": 0,
"eid": 70,
"tag": 32
},
{
"cid": 187,
"type": 0,
"eid": 73,
"tag": 32
},
{
"cid": 188,
"type": 0,
"eid": 74,
"tag": 32
},
{
"cid": 189,
"type": 0,
"eid": 1,
"tag": 33
},
{
"cid": 190,
"type": 0,
"eid": 2,
"tag": 33
},
{
"cid": 191,
"type": 0,
"eid": 11,
"tag": 33
},
{
"cid": 192,
"type": 0,
"eid": 15,
"tag": 33
},
{
"cid": 193,
"type": 0,
"eid": 43,
"tag": 33
},
{
"cid": 195,
"type": 0,
"eid": 64,
"tag": 33
},
{
"cid": 196,
"type": 0,
"eid": 70,
"tag": 33
},
{
"cid": 197,
"type": 0,
"eid": 77,
"tag": 33
},
{
"cid": 198,
"type": 0,
"eid": 87,
"tag": 33
},
{
"cid": 199,
"type": 0,
"eid": 11,
"tag": 34
},
{
"cid": 200,
"type": 0,
"eid": 86,
"tag": 34
},
{
"cid": 201,
"type": 0,
"eid": 87,
"tag": 34
},
{
"cid": 202,
"type": 0,
"eid": 88,
"tag": 34
},
{
"cid": 203,
"type": 0,
"eid": 96,
"tag": 34
},
{
"cid": 204,
"type": 0,
"eid": 99,
"tag": 34
},
{
"cid": 205,
"type": 0,
"eid": 6,
"tag": 6
},
{
"cid": 206,
"type": 0,
"eid": 8,
"tag": 6
},
{
"cid": 207,
"type": 0,
"eid": 9,
"tag": 6
},
{
"cid": 208,
"type": 0,
"eid": 10,
"tag": 6
},
{
"cid": 209,
"type": 0,
"eid": 7,
"tag": 35
},
{
"cid": 210,
"type": 0,
"eid": 100,
"tag": 35
},
{
"cid": 211,
"type": 0,
"eid": 9,
"tag": 36
},
{
"cid": 212,
"type": 0,
"eid": 6,
"tag": 37
},
{
"cid": 213,
"type": 0,
"eid": 44,
"tag": 37
},
{
"cid": 214,
"type": 0,
"eid": 44,
"tag": 34
},
{
"cid": 215,
"type": 0,
"eid": 98,
"tag": 38
},
{
"cid": 216,
"type": 0,
"eid": 5,
"tag": 39
},
{
"cid": 217,
"type": 0,
"eid": 98,
"tag": 39
},
{
"cid": 218,
"type": 0,
"eid": 8,
"tag": 40
},
{
"cid": 219,
"type": 0,
"eid": 85,
"tag": 41
},
{
"cid": 220,
"type": 0,
"eid": 85,
"tag": 42
},
{
"cid": 221,
"type": 0,
"eid": 85,
"tag": 43
},
{
"cid": 222,
"type": 0,
"eid": 85,
"tag": 44
},
{
"cid": 223,
"type": 0,
"eid": 85,
"tag": 45
},
{
"cid": 224,
"type": 0,
"eid": 13,
"tag": 8
},
{
"cid": 225,
"type": 0,
"eid": 13,
"tag": 26
},
{
"cid": 226,
"type": 0,
"eid": 13,
"tag": 46
},
{
"cid": 227,
"type": 0,
"eid": 13,
"tag": 9
},
{
"cid": 228,
"type": 0,
"eid": 7,
"tag": 6
},
{
"cid": 229,
"type": 0,
"eid": 12,
"tag": 47
},
{
"cid": 230,
"type": 0,
"eid": 12,
"tag": 48
},
{
"cid": 231,
"type": 0,
"eid": 11,
"tag": 49
},
{
"cid": 232,
"type": 0,
"eid": 15,
"tag": 49
},
{
"cid": 233,
"type": 0,
"eid": 17,
"tag": 50
},
{
"cid": 234,
"type": 0,
"eid": 19,
"tag": 50
},
{
"cid": 235,
"type": 0,
"eid": 76,
"tag": 51
},
{
"cid": 236,
"type": 0,
"eid": 76,
"tag": 52
},
{
"cid": 237,
"type": 0,
"eid": 76,
"tag": 53
},
{
"cid": 238,
"type": 0,
"eid": 14,
"tag": 48
},
{
"cid": 239,
"type": 0,
"eid": 14,
"tag": 53
},
{
"cid": 240,
"type": 0,
"eid": 16,
"tag": 54
},
{
"cid": 241,
"type": 0,
"eid": 16,
"tag": 50
},
{
"cid": 242,
"type": 0,
"eid": 18,
"tag": 50
},
{
"cid": 243,
"type": 0,
"eid": 20,
"tag": 6
},
{
"cid": 244,
"type": 0,
"eid": 22,
"tag": 55
},
{
"cid": 245,
"type": 0,
"eid": 24,
"tag": 55
},
{
"cid": 246,
"type": 0,
"eid": 25,
"tag": 55
},
{
"cid": 247,
"type": 0,
"eid": 23,
"tag": 47
},
{
"cid": 248,
"type": 0,
"eid": 14,
"tag": 56
},
{
"cid": 249,
"type": 0,
"eid": 32,
"tag": 57
},
{
"cid": 250,
"type": 0,
"eid": 33,
"tag": 57
},
{
"cid": 251,
"type": 0,
"eid": 57,
"tag": 57
},
{
"cid": 252,
"type": 0,
"eid": 59,
"tag": 57
},
{
"cid": 253,
"type": 0,
"eid": 63,
"tag": 57
},
{
"cid": 254,
"type": 0,
"eid": 73,
"tag": 57
},
{
"cid": 255,
"type": 0,
"eid": 31,
"tag": 58
},
{
"cid": 256,
"type": 0,
"eid": 32,
"tag": 58
},
{
"cid": 257,
"type": 0,
"eid": 33,
"tag": 58
},
{
"cid": 258,
"type": 0,
"eid": 34,
"tag": 58
},
{
"cid": 259,
"type": 0,
"eid": 35,
"tag": 58
},
{
"cid": 260,
"type": 0,
"eid": 26,
"tag": 59
},
{
"cid": 261,
"type": 0,
"eid": 27,
"tag": 60
},
{
"cid": 262,
"type": 0,
"eid": 28,
"tag": 60
},
{
"cid": 263,
"type": 0,
"eid": 27,
"tag": 61
},
{
"cid": 264,
"type": 0,
"eid": 28,
"tag": 61
},
{
"cid": 265,
"type": 0,
"eid": 100,
"tag": 61
},
{
"cid": 266,
"type": 0,
"eid": 30,
"tag": 62
},
{
"cid": 267,
"type": 0,
"eid": 30,
"tag": 63
},
{
"cid": 268,
"type": 0,
"eid": 30,
"tag": 64
}
]
\ No newline at end of file
......@@ -114,5 +114,141 @@
{
"id": 29,
"name": "суп"
},
{
"id": 30,
"name": "блины"
},
{
"id": 31,
"name": "ветчина"
},
{
"id": 32,
"name": "свинина"
},
{
"id": 33,
"name": "курица"
},
{
"id": 34,
"name": "яйцо"
},
{
"id": 35,
"name": "малина"
},
{
"id": 36,
"name": "черника"
},
{
"id": 38,
"name": "смородина"
},
{
"id": 39,
"name": "постное"
},
{
"id": 40,
"name": "слива"
},
{
"id": 41,
"name": "сернослив"
},
{
"id": 42,
"name": "свекла"
},
{
"id": 43,
"name": "яблоко"
},
{
"id": 44,
"name": "арахис"
},
{
"id": 45,
"name": "мята"
},
{
"id": 46,
"name": "котлеты"
},
{
"id": 47,
"name": "креветки"
},
{
"id": 48,
"name": "макароны"
},
{
"id": 49,
"name": "капуста"
},
{
"id": 50,
"name": "хлеб"
},
{
"id": 51,
"name": "борщ"
},
{
"id": 52,
"name": "первое"
},
{
"id": 53,
"name": "готовая еда"
},
{
"id": 54,
"name": "кунжут"
},
{
"id": 55,
"name": "кальмар"
},
{
"id": 56,
"name": "лосось"
},
{
"id": 57,
"name": "индейка"
},
{
"id": 58,
"name": "колбаса"
},
{
"id": 59,
"name": "зелень"
},
{
"id": 60,
"name": "зеленый горошек"
},
{
"id": 61,
"name": "кукуруза"
},
{
"id": 62,
"name": "лук"
},
{
"id": 63,
"name": "перец"
},
{
"id": 64,
"name": "сельдерей"
}
]
\ No newline at end of file
......@@ -37,8 +37,8 @@
<script src="js/view/page/components.js"></script>
<script src="js/controller/exportLogic.js"></script>
<script src="js/controller/quizGenerator.js"></script>
<script src="js/controller/quizBits/main.js"></script>
<script src="js/controller/quizGenerator.js"></script>
......
......@@ -67,12 +67,18 @@ const qB = {
});
similarClusters = Object.values(similarTags)
.filter( a => a.length >= minAmount);
similarClusters = Object.keys(similarTags)
.map(k=>({k, v:similarTags[k]}))
.filter( a => a.v.length >= minAmount);
log.push('Similar clustered components by tags: '+similarClusters.title);
filtered = rand(similarClusters);
log.push(`Clusters that have >= ${minSimilarTags} similar tags: `+Object.values(similarClusters).length);
let randed = rand(similarClusters);
log.push(`Used cluster tags: ${randed.k.split(',').map(id=>dP.tagsHash[id].name).join(', ')}`);
filtered = randed.v;
}
......@@ -96,9 +102,72 @@ const qB = {
return result;
},
subdivideComponents: function(from, sub) {
const hash = {};
from.forEach(c=>hash[normalizeText(c.name)] = c);
sub.forEach(c=>delete hash[normalizeText(c.name)]);
return Object.values(hash);
},
getUniqComponents: function(products) {
const hash = {};
products.forEach(
p=>
p
.getComponents()
.forEach(c=>
hash[normalizeText(c.name)] = c
)
);
return Object.values(hash);
},
prebuild: {
similarTaggedProducts: {
// minimal components in product
questionCmpAmount: 4,
// amount of matched products
products: {from: 4, to: 8},
// product similarity (minimal matched tags count)
minSimilarTags: 2,
getComponents: function(product, ) {
fn(log){
let products = qB.randomProduct({
minComponents: this.questionCmpAmount,
amount: this.products,
connectedByTags: true,
minSimilarTags: this.minSimilarTags
}, log);
for( let i = 0, _i = products.length; i < _i; i++ ){
const product = products[ i ],
cmps = product
.getComponents()
.filter(cmp => products.filter(p=>p.containsComponent(cmp)).length === 1)
if(cmps.length>=this.questionCmpAmount){
log.push(`Product ${lapk(product.title)} have >= ${this.questionCmpAmount} uniq components (${cmps.length}):`);
const shuffled = shuffle(cmps.slice()).slice(0, this.questionCmpAmount);
cmps.forEach(c=>log.push(`${shuffled.indexOf(c)>-1?'+':' '} > ${c.name}`));
let wrong = products.slice();
wrong.splice(i,1);
return {
correct: product,
wrong: wrong,
uniq: shuffled
}
}
}
return false;
}
}
}
};
\ No newline at end of file
......@@ -41,50 +41,62 @@ const quizTypes = {
questionCmpAmount: 4,
probability: 10,
type: 'Product contains 4 components',
from(log){
let products = qB.randomProduct({
minComponents: this.questionCmpAmount,
amount: {from: 4, to: 8},
connectedByTags: true,
minSimilarTags: 2
}, log);
for( let i = 0, _i = products.length; i < _i; i++ ){
const product = products[ i ],
cmps = product
.getComponents()
.filter(cmp => products.filter(p=>p.containsComponent(cmp)).length === 1)
if(cmps.length>=this.questionCmpAmount){
log.push(`Product ${lapk(product.title)} have >= ${this.questionCmpAmount} uniq components (${cmps.length}):`);
const shuffled = shuffle(cmps.slice()).slice(0, this.questionCmpAmount);
cmps.forEach(c=>log.push(`${shuffled.indexOf(c)>-1?'+':' '} > ${c.name}`));
let wrong = products.slice();
wrong.splice(i,1);
return {
correct: product,
wrong: wrong,
uniq: shuffled
}
}
products: {from: 4, to: 8},
minSimilarTags: 1,
from: qB.prebuild.similarTaggedProducts.fn,
question(income, log){
return 'В какой из продуктов входят данные ингредиенты: '+income.uniq.map(a=> textFormat(a.name)).join(', ');//products.map(p=>p.title)
},
answer( income, log ){
return shuffle(
[new Answer.Correct(textFormat(income.correct.title))]
.concat(income.wrong.map(p=>new Answer.Wrong(textFormat(p.title)))))
}
shuffle(products);
},
{
questionCmpAmount: 2,
probability: 1011,
type: 'Component in product',
products: {from: 4, to: 6},
minSimilarTags: 1,
from: qB.prebuild.similarTaggedProducts.fn,
question(income, log){
return `Выберите один ингредиент, который входит в продукт ${lapk(income.correct.title)}`
},
answer( income, log ){
console.log(products.map(p=>p.getComponents()))
return shuffle(
[new Answer.Correct(textFormat(shuffle(income.uniq)[0].name))]
.concat(
shuffle(
qB.subdivideComponents( qB.getUniqComponents(income.wrong), income.correct.getComponents() )
)
.slice(0, rand(this.products.from-1, this.products.to-1))
.map(c=>new Answer.Wrong(textFormat(c.name)))
)
)
}
},
{
questionCmpAmount: 2,
probability: 10,
type: 'Select product description',
products: {from: 3, to: 5},
minSimilarTags: 1,
from: qB.prebuild.similarTaggedProducts.fn,
question(income, log){
return 'В какой из продуктов входят данные ингредиенты: '+income.uniq.map(a=> textFormat(a.name)).join(', ');//products.map(p=>p.title)
return `Какое из этих описаний относится к продукту ${lapk(income.correct.title)}`
},
answer( income, log ){
return shuffle(
[new Answer.Correct(textFormat(income.correct.title))]
.concat(income.wrong.map(c=>new Answer.Wrong(textFormat(c.title)))))
[new Answer.Correct(textFormat(income.correct.description))]
.concat(
income.wrong.map(
p => new Answer.Wrong(textFormat(p.description))
)
))
}
},
{
......@@ -226,7 +238,7 @@ const quizGenerator = function(type, photo, subType) {
return false;
}
answers = shuffle(cfg.answer.call(cfg, source, log));
let answers = shuffle(cfg.answer.call(cfg, source, log));
if(answers === false){
// давай по новой
return quizGenerator( type, photo );
......
......@@ -6,15 +6,15 @@ const textFilterMatched = function(where, what) {
textCache[what] = prepared = tokens.map(token => token[0] === '-' ? {has: false, text: token.substr(1)}: {has: true, text: token});
}
var yep = false;
var yep = true;
for( var i = 0, _i = prepared.length; i < _i; i++ ){
var preparedElement = prepared[ i ];
var matched = where.indexOf(preparedElement.text)>-1;
if(preparedElement.has === false && matched){
return false
}
if(matched)
yep = true;
if(!matched)
yep = false;
}
return yep;
......@@ -65,7 +65,7 @@ const textFormat = function(text, html) {
.replace(/([а-я\?\.])(\d+\.)/g, '$1 $2')
.replace(/ [-–—] /g, '&nbsp;— ')
.replace(/ [-–—] /g, (html?'&nbsp;':' ')+'— ')
.replace(/-(\d)/g, '–$1')
......@@ -88,7 +88,7 @@ const textFormat = function(text, html) {
return text.length > 2 || text === 'НЕ' ? '<span class="important">'+text.toLowerCase()+'</span>': text;
})
.replace(/, ([А-Я])/g, (f, a)=>', '+a.toLowerCase())
[html?'replace':'trim'](/, ([А-Я])/g, (f, a)=>', '+a.toLowerCase())
.replace(/\|\|\|\|\|/g,'\n\n')
[html?'replace':'trim'](/\*\*([^*]+)\*\*/g,'<span class="notificate-text">$1</span>')
......
const useSubSub = true;
const dataProvider = {
maxConnection: 0,
maxTagID: 0,
......@@ -33,19 +34,30 @@ const dataProvider = {
(dP.componentsListHashByProduct[cmp.id] || (dP.componentsListHashByProduct[cmp.id] = [])).push(cmp);
}
dP.tagsHash = data.tags.reduce((store, item)=>{
dP.maxTagID = Math.max(dP.maxTagID, item.id);
store[item.id] = item;
return store
}, {});
if(useSubSub){
data.tags = [];
data.connections = [];
dP.tagsHash = {};
Object.values( data.products ).forEach( function( item ){
const tag = dP.Tag.getOrCreate( item.subsubcat );
dP.Tag.connectToProduct( dP.Product.getByID( item.id ), tag );
} );
}else{
data.connections.forEach((connection) => {
dP.maxConnection = Math.max(dP.maxConnection, connection.cid);
if(connection.type === 0){
data.products[connection.eid].tags.push(connection.tag)
}
});
dP.tagsHash = data.tags.reduce( ( store, item ) => {
dP.maxTagID = Math.max( dP.maxTagID, item.id );
store[ item.id ] = item;
return store
}, {} );
data.connections.forEach( ( connection ) => {
dP.maxConnection = Math.max( dP.maxConnection, connection.cid );
if( connection.type === 0 ){
data.products[ connection.eid ].tags.push( connection.tag )
}
} );
}
};
......@@ -161,7 +161,7 @@
var out = [], isFn = typeof delimiter === 'function';
for( var i = 0, _i = arr.length - 1; i < _i; i++ ){
out.push(arr[i], isFn?delimiter():delimiter);
out.push(arr[i], isFn?delimiter(i):delimiter);
}
if(i < _i+1)
out.push(arr[i]);
......
......@@ -21,7 +21,7 @@ view.cmp.Table.prototype = {
afterFilter: ()=>true,
filter: function(text) {
this.filterText = text;
this.filterRegExp = new RegExp(text.replace(/[\[\]\(\)\?\*\.\+]/g,''), 'ig')
this.filterRegExp = new RegExp(text.replace(/[\[\]\(\)\?\*\.\+]/g,'').replace(/\s+/g,'|'), 'ig')
},
fetch: function() {
const field = Object.keys(this.sort[0])[0]
......@@ -65,8 +65,13 @@ view.cmp.Table.prototype = {
highlight: function(text) {
if(!this.filterText)
return text;
const txts = [];
;
return D.join(text.split(this.filterRegExp), ()=>D.span({cls: 'highlighted'}, this.filterText))
return D.join(text.replace(this.filterRegExp, function(match) {
txts.push(match);
return '|||||'
}).split('|||||'), (i)=>D.span({cls: 'highlighted'}, txts[i]))
},
getActionDelegate: function() {
var me = this;
......
......@@ -11,15 +11,18 @@ view.cmp.Answer = function(answer, type) {
view.page.Generate = function() {
const update = function() {
const photo = store.get('generatePhoto') === 'photo',
type = store.get('generateType'),
result = quizGenerator(type, photo);
title.innerHTML = textFormat(result.question);
debug.value = result.log.join('\n')
D.removeChildren(answers);
D.appendChild(answers, result.answers.map((a)=>view.cmp.Answer(a, type)));
type = store.get('generateType');
try{
const result = quizGenerator( type, photo );
title.innerHTML = textFormat( result.question );
debug.value = result.log.join( '\n' )
D.removeChildren( answers );
D.appendChild( answers, result.answers.map( ( a ) => view.cmp.Answer( a, type ) ) );
}catch( e ){
console.error(e)
}
};
let title, answers, debug;
......
......@@ -125,7 +125,7 @@ view.page.Products = function() {
( item.tags || [] ).map( t => me.highlight( dP.tagsHash[ t ].name ) ).map( view.cmp.Tag ),
bonus.full && D.html( { cls: 'product-description' }, textFormat(item.description) ),
bonus.full && D.html( { cls: 'product-description' }, textFormat(item.description, true) ),
div(
{ cls: 'table-item__product-components' },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment