/ranking & script.recalcRanking

This commit is contained in:
masnn committed 2020-06-07 19:11:19 +08:00
1 parent 4aa1c9fe1f
commit 7a4d382dc9
9 files changed
+154 -53

No files matched your search

+26
View File
@@ -1,10 +1,35 @@
const user = require('../model/user');
const domain = require('../model/domain');
const system = require('../model/system');
const paginate = require('../lib/paginate');
const { Route, Handler } = require('../service/server');
const { PERM_MANAGE } = require('../permission');
const { RoleAlreadyExistError, ValidationError, PermissionError } = require('../error');
class DomainRankHandler extends Handler {
async get({ domainId, page = 1 }) {
const [dudocs, upcount, ucount] = await paginate(
user.getMultiInDomain(domainId).sort({ rating: -1 }),
page,
100,
);
let udocs = [];
// eslint-disable-next-line no-await-in-loop
for (const uid of dudocs.map((dudoc) => dudoc.uid)) {
udocs.push(user.getById(domainId, uid));
}
udocs = await Promise.all(udocs);
const path = [
['Hydro', '/'],
['domain_rank', null],
];
this.response.template = 'domain_rank.html';
this.response.body = {
udocs, upcount, ucount, page, path,
};
}
}
class DomainCreateHandler extends Handler {
async prepare() {
if (this.user.priv !== 1) {
@@ -158,6 +183,7 @@ class DomainRoleHandler extends ManageHandler {
}
async function apply() {
Route('/ranking', DomainRankHandler);
Route('/domain/create', DomainCreateHandler);
Route('/domain/dashboard', DomainDashboardHandler);
Route('/domain/edit', DomainEditHandler);
+2 -2
View File
@@ -1,9 +1,9 @@
class User {
constructor(rank, old, handle = 0) {
constructor(rank, old, uid = 0) {
this.rank = rank;
this.old = old;
this.seed = 1;
this.handle = handle.toString();
this.uid = uid;
}
}
+1 -1
View File
@@ -272,7 +272,7 @@ async function load() {
}
}
const builtinScript = [
'install', 'uninstall', 'rating',
'install', 'uninstall', 'rating', 'recalcRating',
];
for (const i of builtinScript) require(`./script/${i}`);
await script();
+2 -2
View File
@@ -5,7 +5,7 @@ const db = require('../service/db');
const coll = db.collection('document');
const collStatus = db.collection('document.status');
const TYPE_DOMAIN_ROLE = 0;
const TYPE_DOMAIN_USER = 0;
const TYPE_PROBLEM = 10;
const TYPE_PROBLEM_SOLUTION = 11;
const TYPE_PROBLEM_LIST = 12;
@@ -353,7 +353,7 @@ global.Hydro.model.document = module.exports = {
setStatus,
setSub,
TYPE_DOMAIN_ROLE,
TYPE_DOMAIN_USER,
TYPE_CONTEST,
TYPE_DISCUSSION,
TYPE_DISCUSSION_NODE,
+1 -1
View File
@@ -25,7 +25,7 @@ function add(domainId, owner, name) {
tasks.push(
document.add(
domainId, builtin.BUILTIN_ROLES[id].perm, owner,
document.TYPE_DOMAIN_ROLE, id,
document.TYPE_DOMAIN_USER, id,
),
);
}
+49 -45
View File
@@ -9,24 +9,27 @@ const db = require('../service/db');
const coll = db.collection('user');
class USER {
constructor(user) {
this._id = user._id;
this.mail = user.mail;
this.uname = user.uname;
this.salt = () => user.salt;
this.hash = () => user.hash;
this.perm = user.perm;
this.priv = user.priv;
this.role = user.role;
this.viewLang = user.language || 'zh_CN';
this.codeLang = user.codeLang || 'c';
this.codeTemplate = user.codeTemplate || '';
this.regat = user.regat;
this.loginat = user.loginat;
this.bio = user.bio || '';
this.gravatar = user.gravatar || '';
this.nAccept = user.nAccept || 0;
this.nSubmit = user.nSubmit || 0;
constructor(udoc, dudoc) {
this.udoc = () => udoc;
this.dudoc = () => dudoc;
this._id = udoc._id;
this.mail = udoc.mail;
this.uname = udoc.uname;
this.salt = () => udoc.salt;
this.hash = () => udoc.hash;
this.priv = udoc.priv;
this.viewLang = udoc.language || 'zh_CN';
this.codeLang = udoc.codeLang || 'c';
this.codeTemplate = udoc.codeTemplate || '';
this.regat = udoc.regat;
this.loginat = udoc.loginat;
this.bio = udoc.bio || '';
this.gravatar = udoc.gravatar || '';
this.nAccept = dudoc.nAccept || 0;
this.nSubmit = dudoc.nSubmit || 0;
this.rating = dudoc.rating || 1500;
this.perm = dudoc.perm;
this.role = dudoc.role;
}
hasPerm(p) {
@@ -40,25 +43,20 @@ class USER {
}
}
async function getPerm(domainId, udoc) {
if (udoc._id === 1) { // Is Guest
const p = await document.get(domainId, document.TYPE_DOMAIN_ROLE, 'guest');
return p ? p.content : builtin.BUILTIN_ROLES.guest.perm;
}
if (udoc.priv === 1) { // Is superadmin
const p = await document.get(domainId, document.TYPE_DOMAIN_ROLE, 'admin');
return p ? p.content : builtin.BUILTIN_ROLES.admin.perm;
}
const role = await document.getStatus(domainId, document.TYPE_DOMAIN_ROLE, 0, udoc._id);
const p = await document.get(domainId, document.TYPE_DOMAIN_ROLE, (role || {}).role || 'default');
return p ? p.content : builtin.BUILTIN_ROLES.default.perm;
async function getInDomain(domainId, udoc) {
const dudoc = await document.getStatus(domainId, document.TYPE_DOMAIN_USER, 0, udoc._id);
if (udoc._id === 1) dudoc.role = 'guest';
if (udoc.priv === 1) dudoc.role = 'admin';
const p = await document.get(domainId, document.TYPE_DOMAIN_USER, dudoc.role || 'default');
dudoc.perm = p ? p.content : builtin.BUILTIN_ROLES[dudoc.role || 'default'].perm;
return dudoc;
}
async function getById(domainId, _id) {
const udoc = await coll.findOne({ _id });
if (!udoc) throw new UserNotFoundError(_id);
udoc.perm = await getPerm(domainId, udoc);
return new USER(udoc);
const dudoc = await getInDomain(domainId, udoc);
return new USER(udoc, dudoc);
}
async function getList(domainId, uids) {
@@ -73,8 +71,8 @@ async function getByUname(domainId, uname) {
const unameLower = uname.trim().toLowerCase();
const udoc = await coll.findOne({ unameLower });
if (!udoc) throw new UserNotFoundError(uname);
udoc.perm = await getPerm(domainId, udoc);
return new USER(udoc);
const dudoc = await getInDomain(domainId, udoc);
return new USER(udoc, dudoc);
}
async function getByEmail(domainId, mail, ignoreMissing = false) {
@@ -84,8 +82,8 @@ async function getByEmail(domainId, mail, ignoreMissing = false) {
if (ignoreMissing) return null;
throw new UserNotFoundError(mail);
}
udoc.perm = await getPerm(domainId, udoc);
return new USER(udoc);
const dudoc = await getInDomain(domainId, udoc);
return new USER(udoc, dudoc);
}
function setPassword(uid, password) {
@@ -152,6 +150,10 @@ function getMulti(params) {
return coll.find(params);
}
function setInDomain(domainId, uid, params) {
return document.setStatus(domainId, document.TYPE_DOMAIN_USER, 0, uid, params);
}
async function getPrefixList(prefix, limit = 50) {
prefix = prefix.toLowerCase();
const $regex = new RegExp(`\\A\\Q${prefix.replace(/\\E/gmi, /\\E\\E\\Q/gmi)}\\E`, 'gmi');
@@ -160,21 +162,21 @@ async function getPrefixList(prefix, limit = 50) {
}
function setRole(domainId, uid, role) {
return document.setStatus(domainId, document.TYPE_DOMAIN_ROLE, 0, uid, { role });
return document.setStatus(domainId, document.TYPE_DOMAIN_USER, 0, uid, { role });
}
function setRoles(domainId, roles) {
const tasks = [];
for (const role in roles) {
tasks.push(document.set(
domainId, document.TYPE_DOMAIN_ROLE, role, { content: roles[role] },
domainId, document.TYPE_DOMAIN_USER, role, { content: roles[role] },
));
}
return Promise.all(tasks);
}
async function getRoles(domainId) {
const docs = await document.getMulti(domainId, document.TYPE_DOMAIN_ROLE).sort('_id', 1).toArray();
const docs = await document.getMulti(domainId, document.TYPE_DOMAIN_USER).sort('_id', 1).toArray();
const roles = [];
for (const doc of docs) {
roles.push({ _id: doc.docId, perm: doc.content });
@@ -183,21 +185,21 @@ async function getRoles(domainId) {
}
function getRole(domainId, name) {
return document.get(domainId, document.TYPE_DOMAIN_ROLE, name);
return document.get(domainId, document.TYPE_DOMAIN_USER, name);
}
function getInDomain(domainId) {
return document.getMultiStatus(domainId, document.TYPE_DOMAIN_ROLE).toArray();
function getMultiInDomain(domainId) {
return document.getMultiStatus(domainId, document.TYPE_DOMAIN_USER);
}
function addRole(domainId, name, permission) {
return document.add(domainId, permission, 1, document.TYPE_DOMAIN_ROLE, name);
return document.add(domainId, permission, 1, document.TYPE_DOMAIN_USER, name);
}
function deleteRoles(domainId, roles) {
return Promise.all([
document.deleteMulti(domainId, document.TYPE_DOMAIN_ROLE, { docId: { $in: roles } }),
document.deleteMultiStatus(domainId, document.TYPE_DOMAIN_ROLE, { role: { $in: roles } }),
document.deleteMulti(domainId, document.TYPE_DOMAIN_USER, { docId: { $in: roles } }),
document.deleteMultiStatus(domainId, document.TYPE_DOMAIN_USER, { role: { $in: roles } }),
]);
}
@@ -219,6 +221,8 @@ global.Hydro.model.user = module.exports = {
setById,
setEmail,
setPassword,
setInDomain,
getMultiInDomain,
getPrefixList,
setRole,
setRoles,
+1 -2
View File
@@ -14,9 +14,8 @@ async function run({ domainId, contestId }) {
for (const result of rankedTsdocs) {
users.push({ ...result[1], rank: result[0], old: udict[result[1].uid].rating || 1500 });
}
console.log(users);
const rated = rating(users);
console.log(rated);
return rated;
}
global.Hydro.script.rating = module.exports = { run };
+29
View File
@@ -0,0 +1,29 @@
/* eslint-disable no-await-in-loop */
const contest = require('../model/contest');
const user = require('../model/user');
const rating = require('../lib/rating');
async function run({ domainId }, report) {
const contests = await contest.getMulti(domainId).sort('endAt', -1).toArray();
await report({ total: contests.length });
for (const i in contests) {
const tdoc = contests[i];
const tsdocs = await contest.getMultiStatus(domainId, { docId: tdoc.docId })
.sort(contest.RULES[tdoc.rule].statusSort).toArray();
const udict = await user.getList(domainId, tsdocs.map((tsdoc) => tsdoc.uid));
const rankedTsdocs = contest.RULES[tdoc.rule].rank(tsdocs);
const users = [];
for (const result of rankedTsdocs) {
users.push({ ...result[1], rank: result[0], old: udict[result[1].uid].rating || 1500 });
}
const rated = rating(users);
const tasks = [];
for (const udoc of rated) {
tasks.push(user.setInDomain(domainId, udoc.uid, { rating: udoc.new }));
}
await Promise.all(tasks);
await report({ current: i + 1 });
}
}
global.Hydro.script.recalcRating = module.exports = { run };
+43
View File
@@ -0,0 +1,43 @@
{% extends "layout/basic.html" %}
{% import "components/paginator.html" as paginator with context %}
{% import "components/nothing.html" as nothing with context %}
{% block content %}
<div class="row">
<div class="medium-12 columns">
<div class="section">
<div class="section__body no-padding">
<div data-fragment-id="ranking">
{% if not udocs %}
{{ nothing.render('Sorry, there is no user in this domain.') }}
{% else %}
<table class="data-table">
<colgroup>
<col class="col--rank">
<col class="col--user">
<col class="col--rating">
</colgroup>
<thead>
<tr>
<th class="col--rank">{{ _('Rank') }}</th>
<th class="col--user">{{ _('Username') }}</th>
<th class="col--rating">{{ _('Rating') }}</th>
</tr>
</thead>
<tbody>
{% for udoc in udocs %}
<tr>
<td class="col--rank">{{ (page - 1) * 100 + loop.index }}</td>
<td class="col--user">{{ user.render_inline(udoc, badge=false) }}</td>
<td class="col--rating">{{ udoc.rating|default(1500)|round(0) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ paginator.render(page, upcount) }}
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}