protected override void PreLoad(Callback <uint, Neighborhood> appender) { using (var db = DAFactory.Get()) { var nhoods = db.Neighborhoods.All(ShardId); var midnight = LotVisitUtils.Midnight(); //gets this morning's midnight (basically current date, with day reset) var activityBeginning = midnight - new TimeSpan(7, 0, 0, 0); var visits = db.LotVisits.StreamBetweenPlusNhood(ShardId, activityBeginning, midnight).ToList(); var enumerator = visits.GetEnumerator(); var nhoodHours = new Dictionary <uint, double>(); while (enumerator.MoveNext()) { var visit = enumerator.Current; var span = LotVisitUtils.CalculateDateOverlap(activityBeginning, midnight, visit.time_created, visit.time_closed.Value); if (nhoodHours.ContainsKey(visit.neighborhood_id)) { nhoodHours[visit.neighborhood_id] += span.TotalMinutes; } else { nhoodHours.Add(visit.neighborhood_id, span.TotalMinutes); } } var order = nhoodHours.OrderByDescending(x => x.Value).ToList(); WorstRating = nhoods.Count; foreach (var item in nhoods) { var lots = db.Lots.GetLocationsInNhood((uint)item.neighborhood_id); var avatars = db.Avatars.GetLivingInNhood((uint)item.neighborhood_id); var townHall = db.Lots.Get(item.town_hall_id ?? 0)?.location ?? 0; var cycle = (item.election_cycle_id == null) ? null : db.Elections.GetCycle(item.election_cycle_id.Value); var converted = HydrateOne(item, avatars, lots, townHall, cycle, visits, order); var intId = (uint)item.neighborhood_id; appender(intId, converted); } // var neighObj = nhoods.Select(x => { var loc = MapCoordinates.Unpack(x.location); var result = new CityNeighbourhood() { Name = x.name, Color = new Color(x.color), Description = x.description, GUID = x.guid, Location = new Point(loc.X, loc.Y), ID = x.neighborhood_id }; return(result); }).ToList(); Lots.CityRepresentation.City_NeighJSON = JsonConvert.SerializeObject(neighObj); } }
public async Task TickNeighborhoods(DateTime now) { var config = Context.Config.Neighborhoods; //process the neighbourhoods for this city var endDate = new DateTime(now.Year, now.Month, 1).AddMonths(1); if (config.Election_Week_Align) { endDate = FindLastWeek(endDate); } var timeToNextMonth = (endDate - now); var ds = Kernel.Get <IDataService>(); var epochNow = Epoch.FromDate(now); using (var da = DAFactory.Get()) { var midnight = LotVisitUtils.Midnight(); //gets this morning's midnight (basically current date, with day reset) var activityBeginning = midnight - new TimeSpan(7, 0, 0, 0); var range = da.LotVisits.StreamBetweenPlusNhood(Context.ShardId, activityBeginning, midnight); var enumerator = range.GetEnumerator(); var nhoodHours = new Dictionary <uint, double>(); while (enumerator.MoveNext()) { var visit = enumerator.Current; var span = LotVisitUtils.CalculateDateOverlap(activityBeginning, midnight, visit.time_created, visit.time_closed.Value); if (nhoodHours.ContainsKey(visit.neighborhood_id)) { nhoodHours[visit.neighborhood_id] += span.TotalMinutes; } else { nhoodHours.Add(visit.neighborhood_id, span.TotalMinutes); } } // var nhoodOrder = nhoodHours.OrderByDescending(x => x.Value); var nhoods = da.Neighborhoods.All(Context.ShardId); foreach (var nhood in nhoods) { var nhoodDS = await ds.Get <Neighborhood>((uint)nhood.neighborhood_id); if (nhoodDS == null) { continue; } //placement within the top neighbourhoods for activity var placement = nhoodOrder.ToList().FindIndex(x => x.Key == nhood.neighborhood_id); if (placement == -1) { placement = nhoods.Count; } nhoodDS.Neighborhood_ActivityRating = (uint)placement + 1; //is there an active cycle for this neighbourhood? var stillActive = false; if (nhood.election_cycle_id != null) { var cycle = da.Elections.GetCycle(nhood.election_cycle_id.Value); if (cycle != null) { if (cycle.current_state == DbElectionCycleState.shutdown || cycle.current_state == DbElectionCycleState.failsafe) { long timeToEnd = (long)cycle.end_date - epochNow; if (timeToEnd <= 0 && nhood.mayor_id != null) { await SetMayor(da, 0, (uint)nhood.neighborhood_id); } stillActive = epochNow < cycle.end_date; //can't switch eligibility til we end } else if (cycle.current_state < DbElectionCycleState.ended) { var active = (epochNow >= cycle.start_date && epochNow < cycle.end_date); long timeToEnd = (long)cycle.end_date - epochNow; DbElectionCycleState targetState; if (timeToEnd <= 0) { targetState = DbElectionCycleState.ended; } else if (timeToEnd <= 60 * 60 * 24 * 3) //last 3 days are the full election { targetState = DbElectionCycleState.election; } else //all other time is the nomination { targetState = DbElectionCycleState.nomination; } if (targetState != cycle.current_state) { await ChangeElectionState(da, nhood, cycle, targetState); nhoodDS.Neighborhood_ElectionCycle.ElectionCycle_CurrentState = (byte)targetState; } //important: if we are in failsafe mode we can't switch eligibility or start a new cycle. if (cycle.current_state != DbElectionCycleState.ended) { stillActive = true; } } } } //do we need to start a new cycle? if (!stillActive && timeToNextMonth.TotalDays < 7 && (nhood.flag & 1) == 0) { //update eligibility if ((nhood.flag & 2) > 0) { //not eligibile for elections (temp) //is our placement within bounds? if (placement != -1 && placement < config.Mayor_Elegibility_Limit) { //make us eligible. nhood.flag &= ~(uint)2; nhoodDS.Neighborhood_Flag = nhood.flag; da.Neighborhoods.UpdateFlag((uint)nhood.neighborhood_id, nhood.flag); SendBulletinPost(da, nhood.neighborhood_id, "f123", (int)NeighBulletinStrings.ElectionBeginSubject, (int)NeighBulletinStrings.ElectionBegin, 0, nhood.name, config.Mayor_Elegibility_Limit.ToString()); } } else { //is our placement outwith bounds? if (placement == -1 || placement >= config.Mayor_Elegilility_Falloff) { //make us ineligible. nhood.flag |= 2; nhoodDS.Neighborhood_Flag = nhood.flag; da.Neighborhoods.UpdateFlag((uint)nhood.neighborhood_id, nhood.flag); //start a shutdown cycle var dbCycle = new DbElectionCycle { current_state = DbElectionCycleState.shutdown, election_type = DbElectionCycleType.shutdown, start_date = Epoch.FromDate(midnight), end_date = Epoch.FromDate(endDate) }; var cycleID = da.Elections.CreateCycle(dbCycle); nhoodDS.Neighborhood_ElectionCycle = new ElectionCycle() { ElectionCycle_CurrentState = (byte)dbCycle.current_state, ElectionCycle_ElectionType = (byte)dbCycle.election_type, ElectionCycle_StartDate = dbCycle.start_date, ElectionCycle_EndDate = dbCycle.end_date }; da.Neighborhoods.UpdateCycle((uint)nhood.neighborhood_id, cycleID); SendBulletinPost(da, nhood.neighborhood_id, "f123", (int)NeighBulletinStrings.ElectionCancelledSubject, (int)NeighBulletinStrings.ElectionCancelled, 0, nhood.name); } } var eligible = (nhood.flag & 2) == 0; if (eligible || nhood.election_cycle_id == null) { //yes var dbCycle = new DbElectionCycle { current_state = (eligible) ? DbElectionCycleState.nomination : DbElectionCycleState.shutdown, election_type = (eligible) ? DbElectionCycleType.election : DbElectionCycleType.shutdown, start_date = Epoch.FromDate(midnight), end_date = Epoch.FromDate(endDate) }; var cycleID = da.Elections.CreateCycle(dbCycle); nhoodDS.Neighborhood_ElectionCycle = new ElectionCycle() { ElectionCycle_CurrentState = (byte)dbCycle.current_state, ElectionCycle_ElectionType = (byte)dbCycle.election_type, ElectionCycle_StartDate = dbCycle.start_date, ElectionCycle_EndDate = dbCycle.end_date }; da.Neighborhoods.UpdateCycle((uint)nhood.neighborhood_id, cycleID); if (eligible) { //notify current mayor if (nhood.mayor_id != null) { var mail = Kernel.Get <MailHandler>(); mail.SendSystemEmail("f116", (int)NeighMailStrings.TermLengthSubject, (int)NeighMailStrings.TermLength, 1, MessageSpecialType.Nominate, dbCycle.end_date, nhood.mayor_id.Value, nhood.name, dbCycle.end_date.ToString()); } //post to bulletin var nomEndDate = dbCycle.end_date - 60 * 60 * 24 * 3; //nomination ends 3 days before end of cycle SendBulletinPost(da, nhood.neighborhood_id, "f123", (int)NeighBulletinStrings.NominateSubject, (int)NeighBulletinStrings.Nominate, nomEndDate, nhood.name, nomEndDate.ToString()); } } } } } }
public void SetTop10s(Neighborhood nhood, List <uint> avatars, List <DbLotVisitNhood> visits, List <KeyValuePair <uint, double> > order) { var midnight = LotVisitUtils.Midnight(); //gets this morning's midnight (basically current date, with day reset) var activityBeginning = midnight - new TimeSpan(7, 0, 0, 0); var index = order.FindIndex(x => x.Key == nhood.Id); nhood.Neighborhood_ActivityRating = (uint)((index == -1) ? WorstRating : (index + 1)); var subset = visits.Where(x => x.neighborhood_id == nhood.Id && x.type == DbLotVisitorType.visitor); //get the total hours for all lots in the nhood var enumerator = subset.GetEnumerator(); var lotHours = new Dictionary <uint, double>(); var cats = new Dictionary <uint, LotCategory>(); while (enumerator.MoveNext()) { var visit = enumerator.Current; var span = LotVisitUtils.CalculateDateOverlap(activityBeginning, midnight, visit.time_created, visit.time_closed.Value); if (lotHours.ContainsKey((uint)visit.location)) { lotHours[(uint)visit.location] += span.TotalMinutes; } else { lotHours.Add((uint)visit.location, span.TotalMinutes); cats.Add((uint)visit.location, visit.category); } } var ordered = lotHours.OrderByDescending(x => x.Value); //TOP 10 OVERALL nhood.Neighborhood_TopLotOverall = ImmutableList.Create(ordered.Take(10).Select(x => x.Key).ToArray()); //TOP 10 CATEGORIES var categories = Enum.GetValues(typeof(LotCategory)).Cast <LotCategory>().Skip(1).Take(10); nhood.Neighborhood_TopLotCategory = ImmutableList.Create(categories.Select(x => ordered.FirstOrDefault(y => cats[y.Key] == x).Key).ToArray()); //avatars activity outwith their neighbourhood still counts - so we need to take another subset. var avas = new HashSet <uint>(avatars); //faster if this is a hashset var subset2 = visits.Where(x => avas.Contains(x.avatar_id)); //TOP 10 ACTIVITY enumerator = subset2.GetEnumerator(); var avaHours = new Dictionary <uint, double>(); while (enumerator.MoveNext()) { var visit = enumerator.Current; var span = LotVisitUtils.CalculateDateOverlap(activityBeginning, midnight, visit.time_created, visit.time_closed.Value); if (avaHours.ContainsKey((uint)visit.avatar_id)) { avaHours[(uint)visit.avatar_id] += span.TotalMinutes; } else { avaHours.Add((uint)visit.avatar_id, span.TotalMinutes); } } ordered = avaHours.OrderByDescending(x => x.Value); nhood.Neighborhood_TopAvatarActivity = ImmutableList.Create(ordered.Take(10).Select(x => x.Key).ToArray()); }
public void Run(TaskContext context) { Running = true; var tuning = Tuning.Bonus; if (tuning == null) { tuning = new BonusTaskTuning(); } if (context.ShardId == null || !context.ShardId.HasValue) { throw new Exception("Top 100 must be given a shard_id to process"); } using (var db = DAFactory.Get()) { var endTime = LotVisitUtils.Midnight(); var endDay = endTime.Subtract(TimeSpan.FromMilliseconds(1)); endDay = new DateTime(endDay.Year, endDay.Month, endDay.Day); var startTime = endTime.Subtract(TimeSpan.FromDays(4)); var shardId = context.ShardId; var vistorHourScale = db.Tuning.AllCategory("category_mul", 2).ToDictionary(x => x.tuning_index); //0: relationship, 1: skill/money, 2: visitor hour scale while (startTime < endTime) { var dayStart = startTime; var dayEnd = dayStart.Add(TimeSpan.FromDays(1)); var stream = db.LotVisits.StreamBetween(shardId.Value, dayStart, dayEnd); var enumerator = stream.GetEnumerator(); var hours = new Dictionary <int, double>(); while (Running && enumerator.MoveNext()) { var visit = enumerator.Current; var span = LotVisitUtils.CalculateDateOverlap(dayStart, dayEnd, visit.time_created, visit.time_closed.Value); if (hours.ContainsKey(visit.lot_id)) { hours[visit.lot_id] += span.TotalMinutes; } else { hours.Add(visit.lot_id, span.TotalMinutes); } } db.LotVisitTotals.Insert(hours.Where(x => x.Value > 0).Select(x => { return(new DbLotVisitTotal() { lot_id = x.Key, date = dayStart, minutes = (int)x.Value }); })); startTime = startTime.Add(TimeSpan.FromDays(1)); } var top100Calculated = db.LotTop100.Calculate(endTime, context.ShardId.Value); if (!top100Calculated) { throw new Exception("Unknown error while calculating top 100 lots"); } var bonusMetrics = db.Bonus.GetMetrics(endDay, context.ShardId.Value).ToList(); //force this as a list. if we lazy evaluate it, an exception will be thrown. db.Bonus.Insert(bonusMetrics.Select(x => { int?bonus_property = null; int?bonus_visitor = null; int?bonus_sim = null; float multiplier = 1; DbTuning cattuning; if (vistorHourScale.TryGetValue((int)x.category, out cattuning)) { multiplier = cattuning.value; } if (x.visitor_minutes != null && x.visitor_minutes >= 60) { bonus_visitor = (int)(Math.Floor((double)x.visitor_minutes / (double)60) * tuning.visitor_bonus.per_unit * multiplier); } if (x.property_rank != null) { bonus_property = (100 - x.property_rank.Value + 1) * tuning.property_bonus.per_unit; if (tuning.property_bonus.overrides != null && tuning.property_bonus.overrides.ContainsKey(x.property_rank.Value)) { bonus_property = tuning.property_bonus.overrides[x.property_rank.Value]; } } return(new DbBonus() { avatar_id = x.avatar_id, period = endDay, bonus_property = bonus_property, bonus_visitor = bonus_visitor, bonus_sim = bonus_sim }); })); } }