Skip to content
Merged

Deploy #1759

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/backend/Helpers/UserUploadCharacterProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ private void InitializeCharacterThings()

private void HandleAddonData()
{
_character.AddonData.BankTabs = _characterData.BankTabs;
_character.AddonData.Level = _characterData.Level;
_character.AddonData.LevelXp = _characterData.LevelXp;

Expand Down
66 changes: 66 additions & 0 deletions apps/backend/Jobs/Magic/MagicAggregateMiscReportsJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using Wowthing.Lib.Enums;
using Wowthing.Lib.Jobs;
using Wowthing.Lib.Models;
using Wowthing.Lib.Utilities;

namespace Wowthing.Backend.Jobs.Magic;

public class MagicAggregateMiscReportsJob : JobBase, IScheduledJob
{
public static readonly ScheduledJob Schedule = new ScheduledJob
{
Type = JobType.MagicAggregateMiscReports,
Priority = JobPriority.High,
Interval = TimeSpan.FromMinutes(1),
Version = 1,
};

public override async Task Run(string[] data)
{
var timer = new JankTimer();

var aggregateMap = await Context.MiscAggregate
.ToDictionaryAsync(ma => (ma.ReportType, ma.Region));

// Delves
foreach (var reportType in Enum.GetValues<MiscReportType>())
{
foreach (var region in Enum.GetValues<WowRegion>())
{
var aggregateKey = (reportType, (short)region);

var dataCounts = new Dictionary<string, int>();
var reportQuery = Context.MiscReport
.AsNoTracking()
.Where(mr => mr.Region == (short)region &&
mr.ExpiresAt > DateTime.UtcNow)
.AsAsyncEnumerable();
await foreach (var report in reportQuery)
{
dataCounts.TryAdd(report.Data, 0);
dataCounts[report.Data]++;
}

if (!aggregateMap.TryGetValue(aggregateKey, out var dbAggregate))
{
dbAggregate = new MiscAggregate
{
ReportType = reportType,
Region = (short)region,
};
Context.MiscAggregate.Add(dbAggregate);
}

dbAggregate.JsonData = dataCounts.Count > 0
? dataCounts.OrderByDescending(x => x.Value).First().Key
: null;
}
}

await Context.SaveChangesAsync();

timer.AddPoint("Save", true);

Logger.Information("{timer}", timer.ToString());
}
}
41 changes: 41 additions & 0 deletions apps/backend/Jobs/Maintenance/MaintenanceDeleteMiscReportsJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Npgsql;
using Wowthing.Lib.Jobs;

namespace Wowthing.Backend.Jobs.Maintenance;

public class MaintenanceDeleteMiscReportsJob : JobBase, IScheduledJob
{
public static readonly ScheduledJob Schedule = new ScheduledJob
{
Type = JobType.MaintenanceDeleteMiscReports,
Priority = JobPriority.High,
Interval = TimeSpan.FromMinutes(1),
Version = 1,
};

private const string DeleteQuery = @"
DELETE FROM misc_report
WHERE id IN (
SELECT id
FROM misc_report
WHERE expires_at < $1
LIMIT 10000
)
";

public override async Task Run(string[] data)
{
await using var connection = Context.GetConnection();
await connection.OpenAsync();

await using var command = connection.CreateCommand();
command.CommandText = DeleteQuery;
command.Parameters.Add(new NpgsqlParameter { Value = DateTime.UtcNow.AddHours(-1) });

int deleted = await command.ExecuteNonQueryAsync();
if (deleted > 0)
{
Logger.Information("Deleted {count} misc report(s)", deleted);
}
}
}
81 changes: 81 additions & 0 deletions apps/backend/Jobs/User/UserUploadJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,17 @@ await HandleWarbank(accountRegion.Value, userAddonData, parsed.ScanTimes.EmptyIf
}
}

try
{
await HandleMiscReports(accountRegion.Value, parsed);
}
catch (Exception ex)
{
{
Logger.Error(ex, "HandleBattlePets failed!");
}
}

_timer.AddPoint("Account");
}

Expand Down Expand Up @@ -1040,6 +1051,76 @@ await localContext.PlayerWarbankItem
}
}

private async Task HandleMiscReports(WowRegion accountRegion, Upload parsedData)
{
if (parsedData.Delves == null)
{
return;
}

await using var localContext = await ContextFactory.CreateDbContextAsync();

var reportMap = await localContext.MiscReport
.Where(mr => mr.UserId == _userId && mr.Region == (short)accountRegion)
.ToDictionaryAsync(mr => mr.ReportType);

long currentTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
DateTime? expiresAt = null;
List<(int, string)> delves = [];
foreach (string delveString in parsedData.Delves)
{
string[] parts = delveString.Split(":");
if (parts.Length != 3)
{
continue;
}

// unixTimestamp:poiId:story
int.TryParse(parts[0], out int validUntil);
int.TryParse(parts[1], out int poiId);
if (validUntil < currentTimestamp || poiId == 0 || string.IsNullOrWhiteSpace(parts[2]))
{
// log
continue;
}

expiresAt ??= validUntil.AsUtcDateTime();

delves.Add((poiId, parts[2]));
}

if (expiresAt != null && delves.Count > 0)
{
List<object[]> dataParts = [];
foreach ((int poiId, string story) in delves.OrderBy(tup => tup.Item1))
{
dataParts.Add([poiId, story]);
}

string data = JsonSerializer.Serialize(dataParts);

if (reportMap.TryGetValue(MiscReportType.Delve, out var dbReport))
{
dbReport.Data = data;
}
else
{
dbReport = new MiscReport
{
UserId = _userId,
ExpiresAt = expiresAt.Value,
ReportedAt = DateTime.UtcNow,
ReportType = MiscReportType.Delve,
Region = (short)accountRegion,
Data = data,
};
localContext.MiscReport.Add(dbReport);
}
}

await localContext.SaveChangesAsync();
}

private async Task HandleWorldQuestReports(WowRegion accountRegion,
Dictionary<WorldQuestReportKey, WorldQuestReport> newReports)
{
Expand Down
1 change: 1 addition & 0 deletions apps/backend/Models/Uploads/Upload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class Upload
public List<string> Heirlooms { get; set; }

public Dictionary<int, string> Decor { get; set; }
public List<string> Delves { get; set; }
public List<int> Illusions { get; set; }
public List<int> Quests { get; set; }
public Dictionary<int, int> QuestsV2 { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@

let characterImage = $derived.by(() => {
const imagePath = $userStore.images[`${character.id}-2`];
if (!imagePath) {
return undefined;
}

const imageUrl = document.getElementById('app').getAttribute('data-image-url');
return imageUrl ? `${imageUrl}${imagePath}` : imagePath;
});
Expand Down
12 changes: 7 additions & 5 deletions apps/frontend/components/home/view-switcher/Holiday.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@
stats.total += dropStats.overall.total;
}

const vendorStats = lazyState.vendors.stats[everything.vendorsKey.join('--')];
if (vendorStats) {
stats.have += vendorStats.have;
stats.total += vendorStats.total;
if (everything?.vendorsKey?.length > 0) {
const vendorStats = lazyState.vendors.stats[everything?.vendorsKey.join('--')];
if (vendorStats) {
stats.have += vendorStats.have;
stats.total += vendorStats.total;
}
}

if (everything.achievementsKey?.length > 0) {
if (everything?.achievementsKey?.length > 0) {
let cat = wowthingData.achievements.categories?.find(
(cat) => cat?.slug === everything.achievementsKey[0]
);
Expand Down
49 changes: 37 additions & 12 deletions apps/frontend/components/items/ItemsItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
} from './convertible/data';
import { Constants } from '@/data/constants';
import { iconStrings } from '@/data/icons';
import { iconLibrary } from '@/shared/icons';
import { InventoryType } from '@/enums/inventory-type';
import { iconLibrary, uiIcons } from '@/shared/icons';
import { wowthingData } from '@/shared/stores/data';
import { getItemUrl } from '@/utils/get-item-url';
import { getBonusIdCraftingStat } from '@/utils/items/get-bonus-id-crafting-stat';
Expand Down Expand Up @@ -102,14 +103,20 @@
}
};

const statEnchants: Record<number, Icon> = {
7977: uiIcons.circleR, // Resourcefulness
8005: uiIcons.circleM, // Multicrafting
8035: uiIcons.circleI, // Ingenuity
};

const statModifiers: Record<number, Icon> = {
76: iconLibrary.mdiLetterR, // Resourcefulness
77: iconLibrary.mdiLetterF, // Finesse
78: iconLibrary.mdiLetterD, // Deftness
79: iconLibrary.mdiLetterP, // Perception
80: iconLibrary.mdiLetterS, // Speed
81: iconLibrary.mdiLetterM, // Multicrafting
82: iconLibrary.mdiLetterI, // Ingenuity
76: uiIcons.circleR, // Resourcefulness
77: uiIcons.circleF, // Finesse
78: uiIcons.circleD, // Deftness
79: uiIcons.circleP, // Perception
80: uiIcons.circleS, // Speed
81: uiIcons.circleM, // Multicrafting
82: uiIcons.circleI, // Ingenuity
};
</script>

Expand Down Expand Up @@ -191,18 +198,23 @@
--image-border-color: #bbb;
}
}
.crafted-modifier {
--image-margin-top: -6px;
.crafted-modifier,
.crafted-enchant {
--scale: 1.2;

border-radius: 50%;
pointer-events: none;
position: absolute;
left: 0px;
top: 1px;
height: 23px;
left: 0px;
width: 23px;
}
.crafted-modifier {
top: -1px;
}
.crafted-enchant {
bottom: 2px;
}
.crafted-quality {
pointer-events: none;
position: absolute;
Expand Down Expand Up @@ -290,6 +302,19 @@
)}
/>
</div>

{#if item?.inventoryType === InventoryType.ProfessionTool}
{@const statEnchantIcon = statEnchants[gear.equipped.enchantmentIds[0]]}
{#if statEnchantIcon}
<div class="crafted-enchant drop-shadow2">
<IconifyWrapper icon={statEnchantIcon} />
</div>
{:else}
<div class="crafted-enchant status-warn drop-shadow">
<IconifyWrapper icon={iconLibrary.mdiLetterX} />
</div>
{/if}
{/if}
{:else}
{@const upgradeData = getUpgradeData()}
{#if upgradeData?.[0] > 0}
Expand Down
5 changes: 5 additions & 0 deletions apps/frontend/components/items/ItemsSearch.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,17 @@
form {
align-items: center;
display: flex;
flex-wrap: wrap;
gap: 0.8rem;
}
.option {
align-items: center;
display: flex;
gap: 0.2rem;

> span {
white-space: nowrap;
}
}
.results-container {
column-count: max(2, var(--column-count, 1));
Expand Down
Loading
Loading