// Copyright (c) 2015, 16 Walter Bender // // This program is free software; you can redistribute it and/or // modify it under the terms of the The GNU Affero General Public // License as published by the Free Software Foundation; either // version 3 of the License, or (at your option) any later version. // // You should have received a copy of the GNU Affero General Public // License along with this library; if not, write to the Free Software // Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA // Based on ta-stats.py by Walter Bender // TODO: CLEAN UP THIS LIST // Assign each block to a bin. const TACAT = { 'clear': 'forward', 'forward': 'forward', 'back': 'forward', 'left': 'forward', 'right': 'forward', 'arc': 'arc', 'x': 'coord', 'y': 'coord', 'heading': 'coord', 'settranslucency': 'pen', 'sethue': 'pen', 'setxy': 'setxy', 'seth': 'setxy', 'penup': 'pen', 'setpensize': 'pen', 'setcolor': 'pen', 'pensize': 'pen', 'color': 'pen', 'setshade': 'pen', 'setgray': 'pen', 'gray': 'pen', 'fillscreen': 'pen', 'beginfill': 'fill', 'fill': 'fill', 'setfont': 'fill', 'hollowline': 'fill', 'endfill': 'fill', 'plus': 'number', 'minus': 'number', 'multiply': 'number', 'power': 'number', 'divide': 'number', 'oneOf': 'number', 'pendown': 'pen', 'shade': 'pen', 'mod': 'number', 'int': 'number', 'sqrt': 'number', 'identity': 'number', 'and': 'boolean', 'or': 'boolean', 'not': 'boolean', 'greater': 'boolean', 'less': 'boolean', 'equal': 'boolean', 'random': 'random', 'repeat': 'repeat', 'forever': 'repeat', 'if': 'ifthen', 'ifthenelse': 'ifthen', 'while': 'ifthen', 'until': 'ifthen', 'action': 'action', 'do': 'action', 'nameddo': 'action', 'listen': 'action', 'broadcast': 'action', 'calc': 'action', 'doArg': 'action', 'calcArg': 'action', 'dispatch': 'action', 'namedcalc': 'action', 'nameddoArg': 'action', 'namedcalcArg': 'action', 'storein': 'box', 'namedbox': 'box', 'incrementOne': 'box', 'luminance': 'sensor', 'mousex': 'sensor', 'mousey': 'sensor', 'drum': 'action', 'start': 'action', 'mousebutton': 'sensor', 'keyboard': 'sensor', 'readpixel': 'sensor', 'see': 'sensor', 'time': 'sensor', 'sound': 'sensor', 'volume': 'sensor', 'resistance': 'sensor', 'voltage': 'sensor', 'video': 'media', 'wait': 'media', 'camera': 'media', 'journal': 'media', 'audio': 'media', 'show': 'media', 'setscale': 'media', 'savepix': 'media', 'savesvg': 'media', 'mediawait': 'media', 'mediapause': 'media', 'mediastop': 'media', 'mediaplay': 'media', 'speak': 'media', 'sinewave': 'media', 'description': 'media', 'push': 'extras', 'pop': 'extras', 'printheap': 'extras', 'clearheap': 'extras', 'isheapempty': 'extras', 'chr': 'extras', 'int': 'number', 'myfunction': 'python', 'userdefined': 'python', 'box': 'box', 'kbinput': 'sensor', 'showHeap': 'extras', 'emptyHeap': 'extras', 'loadblock': 'python', 'loadpalette': 'python', 'matrix': 'ignore', 'hidden': 'ignore', 'rest2': 'ignore', 'rhythm': 'ignore', 'text': 'ignore', 'number': 'ignore', 'vspace': 'ignore'}; // Assign each bin to a palette. const TAPAL = { 'forward': 'turtlep', 'arc': 'turtlep', 'coord': 'turtlep', 'setxy': 'turtlep', 'pen': 'penp', 'fill': 'penp', 'random': 'numberp', 'boolean': 'numberp', 'repeat': 'flowp', 'ifthen': 'flowp', 'action': 'boxp', 'box': 'boxp', 'sensor': 'sensorp', 'media': 'mediap', 'extras': 'extrasp', 'number': 'numberp', 'python': 'extrasp', 'ignore': 'numberp'}; // Assign a score for each bin. const TASCORE = { 'forward': 3, 'arc': 3, 'setxy': 2.5, 'coord': 4, 'turtlep': 5, 'pen': 2.5, 'fill': 2.5, 'penp': 5, 'number': 2.5, 'boolean': 2.5, 'random': 2.5, 'numberp': 0, 'repeat': 2.5, 'ifthen': 7.5, 'flowp': 10, 'box': 7.5, 'action': 7.5, 'boxp': 0, 'media': 5, 'mediap': 0, 'python': 5, 'extras': 5, 'extrasp': 0, 'ignore': 0, 'sensor': 5, 'sensorp': 0}; // The list of palettes. const PALS = ['turtlep', 'penp', 'numberp', 'flowp', 'boxp', 'sensorp', 'mediap', 'extrasp']; const PALLABELS = [_('turtle'), _('pen'), _('number'), _('flow'), _('action'), _('sensors'), _('media'), _('extras')]; function analyzeProject(blocks) { // Parse block data and generate score based on rubric var blockList = []; for (var blk = 0; blk < blocks.blockList.length; blk++) { if (blocks.blockList[blk].trash) { continue; } switch(blocks.blockList[blk].name) { case 'start': case 'fill': case 'hollowline': if (blocks.blockList[blk].connections[1] == null) { continue; } break; case 'action': if (blocks.blockList[blk].connections[2] == null) { continue; } break; default: if (blocks.blockList[blk].connections[0] == null && last(blocks.blockList[blk].connections) == null) { continue; } break; } blockList.push(blocks.blockList[blk].name); } scores = []; for (var i = 0; i < PALS.length; i++) { scores.push(0); } cats = []; pals = []; for (var b = 0; b < blockList.length; b++) { if (blockList[b] in TACAT) { if (!(TACAT[blockList[b]] in cats)) { cats.push(TACAT[blockList[b]]); } } else { console.log(blockList[b] + ' not in catalog'); } } for (var c = 0; c < cats.length; c++) { if (cats[c] in TAPAL) { if (!(TAPAL[cats[c]] in pals)) { pals.push(TAPAL[cats[c]]); } } } for (var c = 0; c < cats.length; c++) { if (cats[c] in TASCORE) { scores[PALS.indexOf(TAPAL[cats[c]])] += TASCORE[cats[c]]; } } for (var p = 0; p < pals.length; p++) { if (pals[p] in TASCORE) { scores[PALS.indexOf(pals[p])] += TASCORE[pals[p]]; } } return scores; } function scoreToChartData(scores) { var normalizedScores = []; var maxScore = 0; var scale; for (var i = 0; i < scores.length; i++) { if (scores[i] > maxScore) { maxScore = scores[i]; } } if (maxScore > 0) { scale = 100 / maxScore; } else { scale = 1; } if (scale > 1) { scale = 1; } for (var i = 0; i < scores.length; i++) { normalizedScores.push(scores[i] * scale); } var data = { labels: PALLABELS, datasets: [ { label: '', fillColor: 'rgba(220,220,220,0.4)', strokeColor: 'rgba(220,220,220,1)', pointColor: 'rgba(220,220,220,1)', pointStrokeColor: '#fff', pointHighlightFill: '#fff', pointHighlightStroke: 'rgba(220,220,220,1)', data: normalizedScores, }, ] }; return data; } function getChartOptions(callback) { return { // Callback for rendering chart into a bitmap onAnimationComplete: callback, //Boolean - Whether to show lines for each scale point scaleShowLine : true, //Boolean - Whether we show the angle lines out of the radar angleShowLineOut : true, //Boolean - Whether to show labels on the scale scaleShowLabels : false, // Boolean - Whether the scale should begin at zero scaleBeginAtZero : true, //String - Colour of the angle line angleLineColor : 'rgba(0,0,0,.2)', //Number - Pixel width of the angle line angleLineWidth : 10, //String - Point label font declaration pointLabelFontFamily : 'Arial', //String - Point label font weight pointLabelFontStyle : 'normal', //Number - Point label font size in pixels pointLabelFontSize : 30, //String - Point label font colour pointLabelFontColor : '#666', //Boolean - Whether to show a dot for each point pointDot : true, //Number - Radius of each point dot in pixels pointDotRadius : 18, //Number - Pixel width of point dot stroke pointDotStrokeWidth : 6, //Number - amount extra to add to the radius to cater for hit detection outside the drawn point pointHitDetectionRadius : 20, //Boolean - Whether to show a stroke for datasets datasetStroke : true, //Number - Pixel width of dataset stroke datasetStrokeWidth : 12, //Boolean - Whether to fill the dataset with a colour datasetFill : true, //String - A legend template legendTemplate : "