diff --git a/dDashboardServer/routes/UgcRoutes.cpp b/dDashboardServer/routes/UgcRoutes.cpp index 6754a7269..673599755 100644 --- a/dDashboardServer/routes/UgcRoutes.cpp +++ b/dDashboardServer/routes/UgcRoutes.cpp @@ -241,7 +241,7 @@ namespace UgcRoutes { "A page of player models (kind=model) or of car and rocket assemblies (kind=modular: one per combination of modules, however many builds use " "it). Query: q= (\"state:\", \"kind:\"/\"type:\" (a build type, e.g. build6), \"owner:\", \"account:\", \"property:\", \"name:\", " "\"lot:\"/\"module:\" (a LOT or a module's name), \"id:\", or plain text across names, owners and ids), state=, type=, sort=newest|oldest|owner|name|" - "bricks|triangles|slowest (models) or newest|oldest|references|name (assemblies), reverse=1 (the sort's other direction), page= (from 0), size= (1-200). {items, total, page, size, counts, " + "bricks|triangles|slowest|made (models) or newest|oldest|references|name (assemblies), reverse=1 (the sort's other direction), page= (from 0), size= (1-200). {items, total, page, size, counts, " "kinds, ugcPublicUrl, canManage}", [](HTTPReply& reply, const HTTPContext& context) { const bool modular = QueryValue(context.queryString, "kind") == "modular"; @@ -260,7 +260,7 @@ namespace UgcRoutes { query.search = parsed.search; query.state = parsed.state; static const std::map SORTS = { { "newest", IUgcLookup::eSort::NEWEST }, { "oldest", IUgcLookup::eSort::OLDEST }, - { "owner", IUgcLookup::eSort::OWNER }, { "name", IUgcLookup::eSort::NAME }, { "bricks", IUgcLookup::eSort::BRICKS }, { "triangles", IUgcLookup::eSort::TRIANGLES }, { "slowest", IUgcLookup::eSort::SLOWEST } }; + { "owner", IUgcLookup::eSort::OWNER }, { "name", IUgcLookup::eSort::NAME }, { "bricks", IUgcLookup::eSort::BRICKS }, { "triangles", IUgcLookup::eSort::TRIANGLES }, { "slowest", IUgcLookup::eSort::SLOWEST }, { "made", IUgcLookup::eSort::MADE } }; if (const auto it = SORTS.find(sortText); it != SORTS.end()) query.sort = it->second; query.reverse = reverse; query.offset = page * size; diff --git a/dDashboardServer/static/js/ugc.js b/dDashboardServer/static/js/ugc.js index ecb758770..a7229f83b 100644 --- a/dDashboardServer/static/js/ugc.js +++ b/dDashboardServer/static/js/ugc.js @@ -11,7 +11,7 @@ var STATES = { pending: ['Waiting', 'secondary'], done: ['Made', 'success'], failed: ['Failed', 'danger'], empty: ['Empty', 'light'] }; var SORTS = { - model: [['newest', 'Newest'], ['oldest', 'Oldest'], ['bricks', 'Most bricks'], ['triangles', 'Most triangles'], ['slowest', 'Slowest to make'], ['owner', 'Owner'], ['name', 'File name']], + model: [['newest', 'Newest'], ['oldest', 'Oldest'], ['bricks', 'Most bricks'], ['triangles', 'Most triangles'], ['made', 'Recently made'], ['slowest', 'Slowest to make'], ['owner', 'Owner'], ['name', 'File name']], modular: [['newest', 'Newest'], ['oldest', 'Oldest'], ['references', 'Most builds'], ['name', 'Name']] }; // The list's state, as in the address: ?kind=&q=&state=&type=&sort=&page= (from 1)&view=, and the open item (&item=, &build=) @@ -141,7 +141,8 @@ column('State', function (i) { return badge(i.state) + (i.attempts ? ' ' + esc(i.attempts) + ' attempt' + (i.attempts === 1 ? '' : 's') + '' : '') + waitBadge(i); }), - column('Made', function (i) { return '' + esc(madeText(i)) + ''; }, 'slowest', true), + column('Made', function (i) { return '' + (i.processedAt ? esc(fmt.unix(i.processedAt)) : '') + ''; }, 'made', true), + column('Took', function (i) { return '' + esc(costText(i).replace(/^took /, '')) + ''; }, 'slowest', true), column('Size', function (i) { return '' + (i.bricks ? esc(i.bricks) + ' bricks
' + esc(i.triangles.toLocaleString()) + ' triangles' : '') + '
'; }, 'bricks', true), column('File', function (i) { return '' + esc(i.detail || '') + '' + errorText(i); }, 'name'), column('', function (i) { return '
' + actions(i, i.id) + '
'; }) diff --git a/dDashboardServer/templates/ugc.jinja2 b/dDashboardServer/templates/ugc.jinja2 index 76448f37b..d5af1b87e 100644 --- a/dDashboardServer/templates/ugc.jinja2 +++ b/dDashboardServer/templates/ugc.jinja2 @@ -74,7 +74,7 @@
-
IconIDOwnerStateMadeSizeFile
+ IconIDOwnerStateMadeTookSizeFile
IconNewest buildTypeModulesStateBuildsOwners
diff --git a/dDatabase/GameDatabase/ITables/IUgcLookup.h b/dDatabase/GameDatabase/ITables/IUgcLookup.h index f0228a01e..21cd62b48 100644 --- a/dDatabase/GameDatabase/ITables/IUgcLookup.h +++ b/dDatabase/GameDatabase/ITables/IUgcLookup.h @@ -64,6 +64,7 @@ public: BRICKS, // models: the most bricks first TRIANGLES, // models: the most triangles first SLOWEST, // the longest last make first (process_ms) + MADE, // the most recently made (or attempted) first (processed_at) }; // A page of one kind: all of them or those matching the search (the same matching as SearchUgc), in a state or any diff --git a/dDatabase/GameDatabase/ITables/UgcLookupSql.h b/dDatabase/GameDatabase/ITables/UgcLookupSql.h index d259db041..efd1bbfeb 100644 --- a/dDatabase/GameDatabase/ITables/UgcLookupSql.h +++ b/dDatabase/GameDatabase/ITables/UgcLookupSql.h @@ -58,6 +58,7 @@ namespace UgcLookupSql { case eSort::NAME: return std::string("ORDER BY ") + (modular ? "b.ldf_config" : "u.filename") + dir(false) + ", " + id + " DESC "; case eSort::BRICKS: return modular ? "ORDER BY " + id + dir(true) + " " : "ORDER BY u.brick_count" + std::string(dir(true)) + ", u.id DESC "; case eSort::TRIANGLES: return modular ? "ORDER BY " + id + dir(true) + " " : "ORDER BY u.triangle_count" + std::string(dir(true)) + ", u.id DESC "; + case eSort::MADE: return "ORDER BY " + std::string(modular ? "b" : "u") + ".processed_at" + dir(true) + ", " + id + " DESC "; case eSort::SLOWEST: return "ORDER BY " + std::string(modular ? "b" : "u") + ".process_ms" + dir(true) + ", " + id + " DESC "; default: return "ORDER BY " + id + dir(true) + " "; } diff --git a/tests/dWebTests/UgcLookupTests.cpp b/tests/dWebTests/UgcLookupTests.cpp index 0f0bbe07d..ed43bcd1f 100644 --- a/tests/dWebTests/UgcLookupTests.cpp +++ b/tests/dWebTests/UgcLookupTests.cpp @@ -182,6 +182,8 @@ TEST_F(UgcLookupSqlTests, ListsPagesWithCounts) { query.reverse = true; // the oldest first EXPECT_EQ(List(query, false).first, (std::vector{ 1000, 1001 })); query.reverse = false; + query.sort = IUgcLookup::eSort::MADE; // neither has been made: ties go newest first + EXPECT_EQ(List(query, false).first, (std::vector{ 1001, 1000 })); query.sort = IUgcLookup::eSort::SLOWEST; // the tower took 5.2 s; the boat isn't made EXPECT_EQ(List(query, false).first, (std::vector{ 1000, 1001 })); query.reverse = true;