mirror of
https://github.com/DrMboga/WotBlitzStatisticsBlazor.git
synced 2026-10-24 11:50:05 +02:00
Added tanks stat info operation and calculation tier and Wn7 operation
This commit is contained in:
1 parent
3eeda15491
commit
b343e36e0d
14 files changed
+393
-3
No files matched your search
@@ -91,5 +91,15 @@ namespace WotBlitzStatisticsPro.DataAccess
|
|||||||
.FindAsync(Builders<ILanguageDictionary>.Filter.Empty);
|
.FindAsync(Builders<ILanguageDictionary>.Filter.Empty);
|
||||||
return collection.ToList();
|
return collection.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Dictionary<long, int>> GetTankTires(long[] tankIds)
|
||||||
|
{
|
||||||
|
var collection = await _database.GetCollection<VehiclesDictionary>(VehiclesCollectionName)
|
||||||
|
.Find(Builders<VehiclesDictionary>.Filter.In(v => v.TankId, tankIds))
|
||||||
|
.Project(v => new { TankId = v.TankId, Tier = v.Tier })
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return collection.ToDictionary(k => k.TankId, v => v.Tier);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,5 +21,7 @@ namespace WotBlitzStatisticsPro.DataAccess
|
|||||||
Task UpdateVehicles(List<IVehiclesDictionary> vehiclesDictionary);
|
Task UpdateVehicles(List<IVehiclesDictionary> vehiclesDictionary);
|
||||||
|
|
||||||
Task<List<ILanguageDictionary>> ReadLanguages();
|
Task<List<ILanguageDictionary>> ReadLanguages();
|
||||||
|
|
||||||
|
Task<Dictionary<long, int>> GetTankTires(long[] tankIds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,40 @@
|
|||||||
namespace WotBlitzStatisticsPro.DataAccess.Model.Accounts
|
using WotBlitzStatisticsPro.Common.Model;
|
||||||
|
|
||||||
|
namespace WotBlitzStatisticsPro.DataAccess.Model.Accounts
|
||||||
{
|
{
|
||||||
public class TankInfo
|
public class TankInfo
|
||||||
{
|
{
|
||||||
|
///<summary>
|
||||||
|
/// Player account identifier
|
||||||
|
///</summary>
|
||||||
|
public long AccountId { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Tank identifier
|
||||||
|
///</summary>
|
||||||
|
public long TankId { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public int BattleLifeTimeInSeconds { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Last battle
|
||||||
|
///</summary>
|
||||||
|
public int LastBattleTime { get; set; }
|
||||||
|
|
||||||
|
public bool? InGarage { get; set; }
|
||||||
|
|
||||||
|
private int? InGarageUpdated { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Mastery:
|
||||||
|
///
|
||||||
|
/// 0 — None
|
||||||
|
/// 1 — Rank 3
|
||||||
|
/// 2 — Rank 2
|
||||||
|
/// 3 — Rank 1
|
||||||
|
/// 4 — Mastery
|
||||||
|
///</summary>
|
||||||
|
public MarkOfMastery MarkOfMastery { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
namespace WotBlitzStatisticsPro.DataAccess.Model.Accounts
|
||||||
|
{
|
||||||
|
public class TankInfoHistory
|
||||||
|
{
|
||||||
|
///<summary>
|
||||||
|
/// Player account identifier
|
||||||
|
///</summary>
|
||||||
|
public long AccountId { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Tank identifier
|
||||||
|
///</summary>
|
||||||
|
public long TankId { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Last battle
|
||||||
|
///</summary>
|
||||||
|
public int LastBattleTime { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Battles count
|
||||||
|
///</summary>
|
||||||
|
public long? Battles { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Base capture points
|
||||||
|
///</summary>
|
||||||
|
public long? CapturePoints { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Damage dealt
|
||||||
|
///</summary>
|
||||||
|
public long? DamageDealt { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Damage received
|
||||||
|
///</summary>
|
||||||
|
public long? DamageReceived { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Base dropped capture points
|
||||||
|
///</summary>
|
||||||
|
public long? DroppedCapturePoints { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Frags count
|
||||||
|
///</summary>
|
||||||
|
public long? Frags { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Frags count after 8 tier
|
||||||
|
///</summary>
|
||||||
|
public long? Frags8P { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Hits count
|
||||||
|
///</summary>
|
||||||
|
public long? Hits { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Losses count
|
||||||
|
///</summary>
|
||||||
|
public long? Losses { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Max frags per battle
|
||||||
|
///</summary>
|
||||||
|
public long? MaxFrags { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Max XP per battle
|
||||||
|
///</summary>
|
||||||
|
public long? MaxXp { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Shots count
|
||||||
|
///</summary>
|
||||||
|
public long? Shots { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Spotted vehicles count
|
||||||
|
///</summary>
|
||||||
|
public long? Spotted { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Amount of survived battles
|
||||||
|
///</summary>
|
||||||
|
public long? SurvivedBattles { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Amount of survived and wined battles
|
||||||
|
///</summary>
|
||||||
|
public long? WinAndSurvived { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Wins count
|
||||||
|
///</summary>
|
||||||
|
public long? Wins { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Total experience
|
||||||
|
///</summary>
|
||||||
|
public long? Xp { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wn7 coefficient
|
||||||
|
/// </summary>
|
||||||
|
public double Wn7 { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-1
@@ -1,4 +1,5 @@
|
|||||||
using WotBlitzStatisticsPro.Common.Model;
|
using System.Collections.Generic;
|
||||||
|
using WotBlitzStatisticsPro.Common.Model;
|
||||||
using WotBlitzStatisticsPro.DataAccess.Model.Accounts;
|
using WotBlitzStatisticsPro.DataAccess.Model.Accounts;
|
||||||
|
|
||||||
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline
|
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline
|
||||||
@@ -24,5 +25,9 @@ namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline
|
|||||||
|
|
||||||
public AccountInfoHistory AccountInfoHistory { get; set; }
|
public AccountInfoHistory AccountInfoHistory { get; set; }
|
||||||
|
|
||||||
|
public List<TankInfo> Tanks { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<long, TankInfoHistory> TanksHistory { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using WotBlitzStatisticsPro.DataAccess;
|
||||||
|
using WotBlitzStatisticsPro.Logic.Calculations;
|
||||||
|
using WotBlitzStatisticsPro.Logic.Pipeline;
|
||||||
|
|
||||||
|
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations
|
||||||
|
{
|
||||||
|
public class CalculateStatisticsOperation : IOperation<AccountInformationPipelineContext>
|
||||||
|
{
|
||||||
|
private readonly IDictionariesDataAccessor _dictionariesDataAccessor;
|
||||||
|
|
||||||
|
public CalculateStatisticsOperation(IDictionariesDataAccessor dictionariesDataAccessor)
|
||||||
|
{
|
||||||
|
_dictionariesDataAccessor = dictionariesDataAccessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(AccountInformationPipelineContext context, Func<AccountInformationPipelineContext, Task> next)
|
||||||
|
{
|
||||||
|
var tankIds = context.Tanks.Select(t => t.TankId).ToArray();
|
||||||
|
var tankTires = await _dictionariesDataAccessor.GetTankTires(tankIds);
|
||||||
|
|
||||||
|
foreach (var tankInfoHistory in context.TanksHistory)
|
||||||
|
{
|
||||||
|
if (!tankTires.ContainsKey(tankInfoHistory.Key))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tankInfoHistory.Value.CalculateWn7(tankTires[tankInfoHistory.Key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.AccountInfoHistory.CalculateMiddleTier(context.TanksHistory.Values.ToList(), tankTires);
|
||||||
|
context.AccountInfoHistory.CalculateWn7();
|
||||||
|
|
||||||
|
await next.Invoke(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
@@ -1,8 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
|
using WotBlitzStatisticsPro.DataAccess.Model.Accounts;
|
||||||
using WotBlitzStatisticsPro.Logic.Pipeline;
|
using WotBlitzStatisticsPro.Logic.Pipeline;
|
||||||
using WotBlitzStatisticsPro.WgApiClient;
|
using WotBlitzStatisticsPro.WgApiClient;
|
||||||
|
using WotBlitzStatisticsPro.WgApiClient.Model;
|
||||||
|
|
||||||
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations
|
namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations
|
||||||
{
|
{
|
||||||
@@ -24,6 +27,19 @@ namespace WotBlitzStatisticsPro.Logic.AccountInformationPipeline.Operations
|
|||||||
var tanksInfo = await
|
var tanksInfo = await
|
||||||
_wargamingApi.GetPlayerAccountTanksInfo(context.AccountId, context.RealmType, context.RequestLanguage);
|
_wargamingApi.GetPlayerAccountTanksInfo(context.AccountId, context.RealmType, context.RequestLanguage);
|
||||||
|
|
||||||
|
context.Tanks = new List<TankInfo>();
|
||||||
|
context.TanksHistory = new Dictionary<long, TankInfoHistory>();
|
||||||
|
|
||||||
|
tanksInfo.ForEach(tank =>
|
||||||
|
{
|
||||||
|
context.Tanks.Add(_mapper.Map<WotAccountTanksStatistics, TankInfo>(tank));
|
||||||
|
var stat = _mapper.Map<WotAccountTanksFullStatistics, TankInfoHistory>(tank.All);
|
||||||
|
stat.AccountId = tank.AccountId;
|
||||||
|
stat.TankId = tank.TankId;
|
||||||
|
stat.LastBattleTime = tank.LastBattleTime;
|
||||||
|
context.TanksHistory[stat.TankId] = stat;
|
||||||
|
});
|
||||||
|
|
||||||
await next.Invoke(context);
|
await next.Invoke(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using WotBlitzStatisticsPro.DataAccess.Model.Accounts;
|
||||||
|
|
||||||
|
namespace WotBlitzStatisticsPro.Logic.Calculations
|
||||||
|
{
|
||||||
|
public static class TierHelper
|
||||||
|
{
|
||||||
|
public static void CalculateMiddleTier(this AccountInfoHistory account, IList<TankInfoHistory> allTanks, Dictionary<long, int> tankTires)
|
||||||
|
{
|
||||||
|
double x = 0d;
|
||||||
|
if (!account.Battles.HasValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach (var tank in allTanks)
|
||||||
|
{
|
||||||
|
if (tankTires.ContainsKey(tank.TankId) && tank.Battles.HasValue && tank.Battles > 0)
|
||||||
|
{
|
||||||
|
x += tankTires[tank.TankId] * tank.Battles.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (account.Battles > 0)
|
||||||
|
x /= account.Battles.Value;
|
||||||
|
account.AvgTier = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
using WotBlitzStatisticsPro.DataAccess.Model.Accounts;
|
||||||
|
|
||||||
|
namespace WotBlitzStatisticsPro.Logic.Calculations
|
||||||
|
{
|
||||||
|
public static class Wn7Helper
|
||||||
|
{
|
||||||
|
public static double CalculateWn7(
|
||||||
|
long battles,
|
||||||
|
double tier,
|
||||||
|
double avdFrags,
|
||||||
|
double avgDamage,
|
||||||
|
double avgSpot,
|
||||||
|
double avgDef,
|
||||||
|
double winRate)
|
||||||
|
{
|
||||||
|
double firstSummation = CalculateFirstSummation(tier, avdFrags);
|
||||||
|
double secondSummation = CalculateSecondSummation(tier, avgDamage);
|
||||||
|
double thirdSummation = CalculateThirdSummation(tier, avgSpot);
|
||||||
|
double fourthSummation = CalculateFourthSummation(avgDef);
|
||||||
|
double fifthSummation = CalculateFifthSummation(winRate);
|
||||||
|
double sixthSummation = CalculateSixthSummation(tier, battles);
|
||||||
|
|
||||||
|
return firstSummation + secondSummation + thirdSummation + fourthSummation + fifthSummation + sixthSummation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CalculateWn7(this TankInfoHistory tank, double tier)
|
||||||
|
{
|
||||||
|
if (!tank.Battles.HasValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double avdFrags = tank.Frags.DoubleValue() / tank.Battles.DoubleValue();
|
||||||
|
double avgDamage = tank.DamageDealt.DoubleValue() / tank.Battles.DoubleValue();
|
||||||
|
double avgSpot = tank.Spotted.DoubleValue() / tank.Battles.DoubleValue();
|
||||||
|
double avgDef = tank.DroppedCapturePoints.DoubleValue() / tank.Battles.DoubleValue();
|
||||||
|
double winRate = (100d * tank.Wins.DoubleValue() / tank.Battles.DoubleValue()); // - 48;
|
||||||
|
|
||||||
|
tank.Wn7 = CalculateWn7(tank.Battles.Value, tier, avdFrags, avgDamage, avgSpot, avgDef, winRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CalculateWn7(this AccountInfoHistory account)
|
||||||
|
{
|
||||||
|
if (!account.Battles.HasValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double avdFrags = account.Frags.DoubleValue() / account.Battles.DoubleValue();
|
||||||
|
double avgDamage = account.DamageDealt.DoubleValue() / account.Battles.DoubleValue();
|
||||||
|
double avgSpot = account.Spotted.DoubleValue() / account.Battles.DoubleValue();
|
||||||
|
double avgDef = account.DroppedCapturePoints.DoubleValue() / account.Battles.DoubleValue();
|
||||||
|
double winRate = (100 * account.Wins.DoubleValue() / account.Battles.DoubleValue()); // - 48;
|
||||||
|
|
||||||
|
account.Wn7 = CalculateWn7(account.Battles.Value, account.AvgTier, avdFrags, avgDamage, avgSpot, avgDef, winRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//(1240-1040/(min(TIER,6)^0.164))*FRAGS
|
||||||
|
private static double CalculateFirstSummation(double tier, double frags)
|
||||||
|
{
|
||||||
|
double x = Math.Min(tier, 6);
|
||||||
|
x = Math.Pow(x, 0.164d);
|
||||||
|
x = 1040d / x;
|
||||||
|
x = 1240d - x;
|
||||||
|
return x * frags;
|
||||||
|
}
|
||||||
|
|
||||||
|
//DAMAGE * 530/(184*exp(0.24*TIER)+130)
|
||||||
|
private static double CalculateSecondSummation(double tier, double damage)
|
||||||
|
{
|
||||||
|
double x = 184d * Math.Exp(0.24d * tier) + 130d;
|
||||||
|
return damage * 530d / x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//SPOT * 125*min(TIER,3)/3
|
||||||
|
private static double CalculateThirdSummation(double tier, double spot)
|
||||||
|
{
|
||||||
|
return spot * 125d * Math.Min(tier, 3d) / 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
//min(DEF,2.2)*100
|
||||||
|
private static double CalculateFourthSummation(double def)
|
||||||
|
{
|
||||||
|
return Math.Min(def, 2.2d) * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
//((185/(0.17+exp(((WINRATE)-35)*-0.134)))-500)*0.45
|
||||||
|
private static double CalculateFifthSummation(double winRate)
|
||||||
|
{
|
||||||
|
double x = (winRate - 35d) * -0.134;
|
||||||
|
x = Math.Exp(x) + 0.17d;
|
||||||
|
return (185d / x - 500d) * 0.45d;
|
||||||
|
}
|
||||||
|
|
||||||
|
//(-1*(((5 - min(TIER,5))*125)/(1 + exp((TIER-(TOTAL/220^(3/TIER)))*1.5))))
|
||||||
|
private static double CalculateSixthSummation(double tier, double total)
|
||||||
|
{
|
||||||
|
double x = -125d * (5d - Math.Min(tier, 5d));
|
||||||
|
double y = total / Math.Pow(220d, 3d / tier);
|
||||||
|
y = 1.5d * (tier - y);
|
||||||
|
y = Math.Exp(y) + 1d;
|
||||||
|
return x / y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double DoubleValue(this long? val)
|
||||||
|
{
|
||||||
|
if (!val.HasValue)
|
||||||
|
return 0d;
|
||||||
|
return (double)val.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using WotBlitzStatisticsPro.DataAccess.Model.Accounts;
|
||||||
|
using WotBlitzStatisticsPro.WgApiClient.Model;
|
||||||
|
|
||||||
|
namespace WotBlitzStatisticsPro.Logic.Mappers
|
||||||
|
{
|
||||||
|
public class TankInfoProfile : Profile
|
||||||
|
{
|
||||||
|
public TankInfoProfile()
|
||||||
|
{
|
||||||
|
CreateMap<WotAccountTanksStatistics, TankInfo>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using WotBlitzStatisticsPro.DataAccess.Model.Accounts;
|
||||||
|
using WotBlitzStatisticsPro.WgApiClient.Model;
|
||||||
|
|
||||||
|
namespace WotBlitzStatisticsPro.Logic.Mappers
|
||||||
|
{
|
||||||
|
public class TankInfoStatisticsProfile: Profile
|
||||||
|
{
|
||||||
|
public TankInfoStatisticsProfile()
|
||||||
|
{
|
||||||
|
CreateMap<WotAccountTanksFullStatistics, TankInfoHistory>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ namespace WotBlitzStatisticsPro.Logic
|
|||||||
|
|
||||||
pipeline.AddOperation<GetAccountInfoOperation>()
|
pipeline.AddOperation<GetAccountInfoOperation>()
|
||||||
.AddOperation<GetTanksInfoOperation>()
|
.AddOperation<GetTanksInfoOperation>()
|
||||||
|
.AddOperation<CalculateStatisticsOperation>()
|
||||||
;
|
;
|
||||||
// ToDo: add other operations
|
// ToDo: add other operations
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ namespace WotBlitzStatisticsPro.Logic
|
|||||||
{
|
{
|
||||||
services.AddTransient<GetAccountInfoOperation>();
|
services.AddTransient<GetAccountInfoOperation>();
|
||||||
services.AddTransient<GetTanksInfoOperation>();
|
services.AddTransient<GetTanksInfoOperation>();
|
||||||
|
services.AddTransient<CalculateStatisticsOperation>();
|
||||||
|
|
||||||
services.AddTransient<IOperationFactory>(serviceProvider =>
|
services.AddTransient<IOperationFactory>(serviceProvider =>
|
||||||
new ServiceProviderOperationFactory(serviceProvider));
|
new ServiceProviderOperationFactory(serviceProvider));
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace WotBlitzStatisticsPro.WgApiClient.Model
|
|||||||
/// Last battle
|
/// Last battle
|
||||||
///</summary>
|
///</summary>
|
||||||
[JsonProperty("last_battle_time")]
|
[JsonProperty("last_battle_time")]
|
||||||
private int LastBattleTime { get; set; }
|
public int LastBattleTime { get; set; }
|
||||||
|
|
||||||
[JsonProperty("in_garage")]
|
[JsonProperty("in_garage")]
|
||||||
public bool? InGarage { get; set; }
|
public bool? InGarage { get; set; }
|
||||||
|
|||||||
Reference in new issue
Block a user