Commits

Marc Delorme committed 5e3c2e5

Add start location stats and distance stat

Comments (0)

Files changed (5)

stats/resources/css/webapp.css

     -webkit-box-align: stretch; 
 }
 
-#filter-type {
+.filter-parent {
     display: -webkit-box; 
     -webkit-box-orient: horizontal;
 }
 }
 
 .filter {
-   text-align: center;
+   text-align: center;
+   -webkit-box-flex: 1;
 }
 
-#filter-type-1 {
-    -webkit-box-flex: 1;
-}
-
-#filter-type-1 select{
+.filter-1 select{
 	color: white;
     background-color: #3366cc;
 }
 
-#filter-type-2 {
-    -webkit-box-flex: 1;
-
-}
-
-#filter-type-2 select{
+.filter-2 select{
     background-color: #dc3912;
     color: white;
 }

stats/resources/js/webapp.js

 	tag: 'winpielength',
 	section: 'Game Statistics'
 }, {
+	name: 'Win - Start distance (Bar)',
+	tag: 'windistance',
+	section: 'Game Statistics'
+}, {
+	name: 'Win - Start distance (Pie)',
+	tag: 'winpiedistance',
+	section: 'Game Statistics'
+}, {
 	name: 'Duel',
 	tag: 'duel',
 	section: 'Game Statistics'
 	tag: 'performance'
 }];
 
-var configVersions = [{
-	name: '189'
-}, {
-	name: '190'
-}, {
-	name: '191'
-}, {
-	name: '192'
-}];
-
 var configMaps = [{
 	name: 'ns2_mineshaft'
 }, {
 
 var FilterItemCollection = Backbone.Collection.extend({
 	model: FilterItemModel,
+	
+	initialize: function(options) {
+		if (options && options.url) {
+			this.url = options.url;
+		}
+	},
+	
 	getActive: function() {
 		var actives = [];
 		
 		this.trigger('select');
 	},
 	getValue : function() {
-		var el = $('select option:selected', this.el);
-		return el.attr('value');
+		var el = $('select option:selected', this.el),
+			value = el.attr('value');
+		return value === "null" ? null : value;
+	}
+});
+
+var FilterSelectLocationView = FilterSelectView.extend({
+	initialize: function(options) {
+		FilterSelectView.prototype.initialize.apply(this);
+		
+		if (options.maps) {
+			this.maps = options.maps;
+			this.maps.bind('change', this.onChange);
+		}
+	},
+	
+	onChange: function() {
+		var GET = {};
+		 
+		// Filter
+		if (this.maps && this.maps.getActive().length > 0) {
+			GET.map = $.toJSON( this.maps.getActive() );
+		}
+		
+		this.collection.fetch({
+			data: GET
+		});
 	}
 });
 
 		if (this.type2View) {
 			this.type2View.off('select', this.onChange);
 		}
+		if (this.location1View) {
+			this.location1View.off('select', this.onChange);
+		}
+		if (this.location2View) {
+			this.location2View.off('select', this.onChange);
+		}
 	    
 	    this.$el.empty();
 	}
 			this.type2View = options.type2View;
 			this.type2View.bind('select', this.onChange);
 		}
+		if (options.location1View) {
+			this.location1View = options.location1View;
+			this.location1View.bind('select', this.onChange);
+		}
+		if (options.location2View) {
+			this.location2View = options.location2View;
+			this.location2View.bind('select', this.onChange);
+		}
 		
 		this.render();
 	},
 		if (this.type2View) {
 			GET.type2 = this.type2View.getValue();
 		}
+		if (this.location1View && this.location1View.getValue()) {
+			GET.location1 = this.location1View.getValue();
+		}
+		if (this.location2View && this.location2View.getValue()) {
+			GET.location2 = this.location2View.getValue();
+		}
 
 		// Render the pie
 		$.ajax(this.url, {
 			this.type2View = options.type2View;
 			this.type2View.bind('select', this.onChange);
 		}
+		if (options.location1View) {
+			this.location1View = options.location1View;
+			this.location1View.bind('select', this.onChange);
+		}
+		if (options.location2View) {
+			this.location2View = options.location2View;
+			this.location2View.bind('select', this.onChange);
+		}
 		
 		this.render();
 	},
 		if (this.type2View) {
 			GET.type2 = this.type2View.getValue();
 		}
+		if (this.location1View && this.location1View.getValue()) {
+			GET.location1 = this.location1View.getValue();
+		}
+		if (this.location2View && this.location2View.getValue()) {
+			GET.location2 = this.location2View.getValue();
+		}
 
 		// Render the pie
 		$.ajax(this.url, {
 			this.maps = options.maps;
 			this.maps.bind('change', this.onChange);
 		}
+		if (options.location1View) {
+			this.location1View = options.location1View;
+			this.location1View.bind('select', this.onChange);
+		}
+		if (options.location2View) {
+			this.location2View = options.location2View;
+			this.location2View.bind('select', this.onChange);
+		}
 		
 		this.render();
 	},
 		if (this.maps && this.maps.getActive().length > 0) {
 			GET.map = $.toJSON( this.maps.getActive() );
 		}
+		if (this.location1View && this.location1View.getValue()) {
+			GET.location1 = this.location1View.getValue();
+		}
+		if (this.location2View && this.location2View.getValue()) {
+			GET.location2 = this.location2View.getValue();
+		}
 
 		// Render the pie
 		$.ajax(this.url, {
 		// Filter
 		if (this.versions && this.versions.getActive().length > 0) {
 			GET.version = $.toJSON( this.versions.getActive() );
-			console.log($.toJSON( this.versions.getActive() ));
 		}
 		if (this.maps && this.maps.getActive().length > 0) {
 			GET.map = $.toJSON( this.maps.getActive() );
 				    chart = new google.visualization.AreaChart( $('.pieChart', me.el)[0] ),
 				    options = {};
 				    
-				console.log(response);
 				
     			data.addColumn('string', 'Build');
-    			data.addColumn('number', 'low');
-    			data.addColumn('number', 'mid');
-    			data.addColumn('number', 'high');
-    			data.addColumn('number', 'very-high');
+    			data.addColumn('number', 'low <20');
+    			data.addColumn('number', 'mid 20-30');
+    			data.addColumn('number', 'high 30-45');
+    			data.addColumn('number', 'very-high >45');
     			/*data.addRows(response.data);
                 
                 if (response.colors) {
 		// Filter
 		if (this.versions && this.versions.getActive().length > 0) {
 			GET.version = $.toJSON( this.versions.getActive() );
-			console.log($.toJSON( this.versions.getActive() ));
 		}
 		if (this.maps && this.maps.getActive().length > 0) {
 			GET.map = $.toJSON( this.maps.getActive() );
 		'duelweapon': 'duelweapon',
 		'winlength': 'winlength',
 		'winpielength': 'winpielength',
+		'windistance': 'windistance',
+		'winpiedistance': 'winpiedistance',
 		'resolution': 'resolution',
 		'performance': 'performance'
 	},
+	
 	notImplemented: function() {
 		this.filterView.hide();
 		this.currentView.remove();
 			el: $('#content')
 		});
 	},
+	
 	duelweapon : function() {
 		this.filterView.show(['maps','versions','type1','type2']);
 		
 			type2View: this.filterType2View
 		});
 	},
+	
 	duel : function() {
 		this.filterView.show(['maps','versions','type1','type2']);
 		
 			type2View: this.filterType2View
 		});
 	},
+	
 	lifetime : function() {
 		this.filterView.show(['maps','versions']);
 		
 			maps: this.mapCollection
 		});
 	},
+	
 	win : function() {
-		this.filterView.show(['maps','versions']);
+		this.filterView.show(['maps','versions','location1','location2']);
 		
 		this.currentView.remove();		
 		this.currentView = new PieView({
 			name: 'Win',
 			url: '/win/pie',
 			versions: this.versionCollection,
-			maps: this.mapCollection
+			maps: this.mapCollection,
+			location1View: this.filterLocation1View,
+			location2View: this.filterLocation2View
 		});
 	},
+	
 	kill : function() {
 		this.filterView.show(['maps','versions']);
 		
 			maps: this.mapCollection
 		});
 	},
+	
 	winlength: function() {
-		this.filterView.show(['maps','versions']);
+		this.filterView.show(['maps','versions','location1','location2']);
 		
 		this.currentView.remove();		
 		this.currentView = new DoubleBarView({
 			name: 'Win - Game Length (Bar)',
 			url: '/win/bar',
 			versions: this.versionCollection,
-			maps: this.mapCollection
+			maps: this.mapCollection,
+			location1View: this.filterLocation1View,
+			location2View: this.filterLocation2View
 		});
 	},
+	
+	windistance: function() {
+		this.filterView.show(['maps','versions','location1','location2']);
+		
+		this.currentView.remove();		
+		this.currentView = new DoubleBarView({
+			el: $('#content'),
+			name: 'Win - Start distance (Bar)',
+			url: '/win/distance',
+			versions: this.versionCollection,
+			maps: this.mapCollection,
+			location1View: this.filterLocation1View,
+			location2View: this.filterLocation2View
+		});
+	},
+	
 	resolution: function() {
 		this.filterView.hide();
 		
 			url: '/resolution'
 		});
 	},
+	
 	performance: function() {
 		this.filterView.hide();
 		
 			url: '/performance/graph'
 		});
 	},
+	
 	winpielength: function() {
-		this.filterView.show(['maps','versions']);
+		this.filterView.show(['maps','versions','location1','location2']);
 		
 		this.currentView.remove();		
 		this.currentView = new MultiPieView({
 			name: 'Win - Game Length (Pie)',
 			url: '/win/bar',
 			versions: this.versionCollection,
-			maps: this.mapCollection
+			maps: this.mapCollection,
+			location1View: this.filterLocation1View,
+			location2View: this.filterLocation2View
 		});
 	},
+	
+	winpiedistance: function() {
+		this.filterView.show(['maps','versions','location1','location2']);
+		
+		this.currentView.remove();		
+		this.currentView = new MultiPieView({
+			el: $('#content'),
+			name: 'Win - Start distance (Pie)',
+			url: '/win/distance',
+			versions: this.versionCollection,
+			maps: this.mapCollection,
+			location1View: this.filterLocation1View,
+			location2View: this.filterLocation2View
+		});
+	},
+	
 	cpuspeed : function() {
 		this.filterView.hide();
 		
 			url: '/cpucore'
 		});
 	},
+	
 	cpucore : function() {
 		this.filterView.hide();
 		
 			url: '/cpuspeed'
 		});
 	},
+	
 	gpu : function() {
 		this.filterView.hide();
 		
 			url: '/gpu'
 		});
 	},
+	
 	start: function() {
 		var me = this;
 		
-		this.versionCollection = new FilterItemCollection(configVersions);
+		this.versionCollection = new FilterItemCollection({
+			url: 'versions'
+		});
+		this.versionCollection.fetch();
+		
 		this.mapCollection = new FilterItemCollection(configMaps);
-		this.typeCollection = new FilterItemCollection();
 		
-		$.ajax('/versions', {
-			dataType : 'json',
-			success : function(response) {
-				me.versionCollection.reset(response);
-			}
+		this.typeCollection = new FilterItemCollection({
+			url: 'types'
 		});
+		this.typeCollection.fetch();
 		
-		$.ajax('/type', {
-			dataType : 'json',
-			success : function(response) {
-				me.typeCollection.reset(response);
-			}
+		this.location1Collection = new FilterItemCollection({
+			url: '/location1'
 		});
+		this.location1Collection.fetch();
 		
+		this.location2Collection = new FilterItemCollection({
+			url: '/location2'
+		});
+		this.location2Collection.fetch();
+
 		// View
 		this.filterVersionsView = new FilterCheckView({
 			el: $( '#filter-versions' ),
 			collection:	this.typeCollection
 		});
 		
+		this.filterLocation1View = new FilterSelectLocationView({
+			el: $( '#filter-location-1' ),
+			collection:	this.location1Collection,
+			'maps': this.mapCollection
+		});
+		
+		this.filterLocation2View = new FilterSelectLocationView({
+			el: $( '#filter-location-2' ),
+			collection:	this.location2Collection,
+			'maps': this.mapCollection
+		});
+		
 		this.filterView = new FilterView({
 			el: $('#filters-widget'),
 			'maps': this.filterMapsView,
 			'versions': this.filterVersionsView,
 			'type1': this.filterType1View,
-			'type2': this.filterType2View
+			'type2': this.filterType2View,
+			'location1': this.filterLocation1View,
+			'location2': this.filterLocation2View
 		});		
 
 		this.menuView = new MenuView({

stats/templates/webapp.html

 				<div id="filters">
 					<div class="filter" id="filter-versions"></div>
 					<div class="filter" id="filter-maps"></div>
-					<div id="filter-type">
-						<div class="filter ui-corner-all" id="filter-type-1"></div>
-						<div class="filter ui-corner-all" id="filter-type-2"></div>
+					<div class="filter-parent" id="filter-type">
+						<div class="filter filter-1 ui-corner-all" id="filter-type-1"></div>
+						<div class="filter filter-2 ui-corner-all" id="filter-type-2"></div>
+					</div>
+					<div class="filter-parent" id="location-type">
+						<div class="filter filter-1 ui-corner-all" id="filter-location-1"></div>
+						<div class="filter filter-2 ui-corner-all" id="filter-location-2"></div>
 					</div>
 				</div>
 			</div>
     endGameQuery = EndGame.objects.all()
     
     endGameQuery = filterByMapAndVersion(endGameQuery, GET)
+    endGameQuery = filterByLocation(endGameQuery, GET)
         
     t1 = endGameQuery.filter(winner=1).count()
     t2 = endGameQuery.filter(winner=2).count()
     unit = 'min'
     
     endGameQuery = filterByMapAndVersion(endGameQuery, GET)
+    endGameQuery = filterByLocation(endGameQuery, GET)
     
     q1 = endGameQuery.filter(winner=1)
     q2 = endGameQuery.filter(winner=2)
 
 @login_required(login_url='/login/')
 @cache_page(60 * 60 * 2)
+def winDistanceBar(request):
+    GET = request.GET
+    endGameQuery = EndGame.objects.all()
+    
+    # set data config
+    if 'delta' in GET:
+        delta = int(GET['delta'])
+    else:
+        delta = 50
+    
+    if 'offset' in GET:
+        offset = int(GET['offset'])
+    else:    
+        offset = 0
+        
+    iMax = math.floor((350 - offset) / delta)
+    unit = 'm'
+    
+    endGameQuery = filterByMapAndVersion(endGameQuery, GET)
+    endGameQuery = filterByLocation(endGameQuery, GET)
+    
+    q1 = endGameQuery.filter(winner=1)
+    q2 = endGameQuery.filter(winner=2)
+    
+    data1 = {}
+    for g in q1:
+        l = g.start_path_distance
+        i,cat = findCategory(l , delta, iMax, unit,offset)
+        if i >= 0:
+            if not data1.has_key(i):
+                data1[i] = {'cat': cat, 'count':0}
+            data1[i]['count'] += 1
+    data1 = [ [key, val] for key, val in data1.items()]
+    
+    data2 = {}
+    for g in q2:
+        l = g.start_path_distance
+        i,cat = findCategory(l , delta, iMax, unit,offset)
+        if i >= 0:
+            if not data2.has_key(i):
+                data2[i] = {'cat': cat, 'count':0}
+            data2[i]['count'] += 1
+    data2 = [ [key, val] for key, val in data2.items()]
+
+    data = []
+    for ii in range( min(len(data1),len(data2)) ):
+        data.append([
+            data1[ii][1]['cat'],
+            data1[ii][1]['count'],
+            data2[ii][1]['count']
+        ])
+    
+    data = {
+        'data': data,
+        'order': ['Marines','Aliens'],
+        'colors' : [
+                '#3366cc',
+                '#dc3912'
+        ]        
+    }
+    dataJSON = json.dumps(data)
+    return HttpResponse(dataJSON)
+
+@login_required(login_url='/login/')
+@cache_page(60 * 60 * 2)
 def killPie(request):
     GET = request.GET
     killQuery = Kill.objects.all()
 
 @login_required(login_url='/login/')
 @cache_page(60 * 60 * 2)
+def startLocation1List(request):
+    GET = request.GET
+    
+    query = EndGame.objects.all().exclude(start_location1=None)
+    query = filterByMapAndVersion(query, GET)
+    
+    locations = list( query.values('start_location1').distinct() )
+    
+    locations = [{'name': t['start_location1']} for t in locations]
+    locationsJSON = json.dumps(locations)
+    return HttpResponse(locationsJSON)
+
+@login_required(login_url='/login/')
+@cache_page(60 * 60 * 2)
+def startLocation2List(request):
+    GET = request.GET
+    
+    query = EndGame.objects.all().exclude(start_location2=None)
+    query = filterByMapAndVersion(query, GET)
+    
+    locations = list( query.values('start_location2').distinct() )
+    
+    locations = [{'name': t['start_location2']} for t in locations]
+    locationsJSON = json.dumps(locations)
+    return HttpResponse(locationsJSON)
+
+@login_required(login_url='/login/')
+@cache_page(60 * 60 * 2)
 def weaponList(request):
     GET = request.GET
     killQuery = Kill.objects.all()
         query = query.filter(version__in=versions)
         
     if 'map' in GET:
-        maps = json.loads( GET['map'])
+        maps = json.loads( GET['map'] )
         
         if not isinstance(maps, list):
             maps = [ maps ]
         maps = map(str, maps)
         query = query.filter(mapName__in=maps)
         
+    return query
+
+def filterByLocation(query, GET):
+    
+    if 'location1' in GET and 'location2' in GET:
+        location1 = GET['location1']
+        location2 = GET['location2']
+              
+        query = query.filter(start_location1=location1)
+        query = query.filter(start_location2=location2)
+        
     return query
     
     # data
     url(r'^type$', 'NS2Stats2.stats.views.typeList'),
+    url(r'^location1$', 'NS2Stats2.stats.views.startLocation1List'),
+    url(r'^location2$', 'NS2Stats2.stats.views.startLocation2List'),
+    url(r'^types$', 'NS2Stats2.stats.views.typeList'),
     url(r'^weapon$', 'NS2Stats2.stats.views.weaponList'),
     url(r'^maps$', 'NS2Stats2.stats.views.mapList'),
     url(r'^versions$', 'NS2Stats2.stats.views.versionList'),
     url(r'^resolution$', 'NS2Stats2.stats.views.resolution'),
     url(r'^win/pie$', 'NS2Stats2.stats.views.winPie'),
     url(r'^win/bar$', 'NS2Stats2.stats.views.winBar'),
+    url(r'^win/distance$', 'NS2Stats2.stats.views.winDistanceBar'),
     url(r'^kill/pie$', 'NS2Stats2.stats.views.killPie'),
     url(r'^kill/weapon/pie$', 'NS2Stats2.stats.views.killWeaponPie'),
     url(r'^performance/graph$', 'NS2Stats2.stats.views.playerPerformance'),
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.