You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Hydro/hydro/model/problem.js

186 lines
4.6 KiB
JavaScript

5 years ago
const { ObjectID } = require('bson');
const gridfs = require('../service/gridfs');
5 years ago
const { ProblemNotFoundError } = require('../error');
const validator = require('../lib/validator');
const db = require('../service/db.js');
const coll = db.collection('problem');
const collStatus = db.collection('problem.status');
/**
* @typedef {import('../interface').Pdoc} Pdoc
* @typedef {import('bson').ObjectID} ObjectID
* @typedef {import('mongodb').Cursor} Cursor
*/
/**
5 years ago
* @param {string} title
* @param {string} content
* @param {number} owner
* @param {number} pid
* @param {import('bson').ObjectID} data
* @param {string[]} category
* @param {string[]} tag
* @param {boolean} hidden
*/
async function add({
title,
content,
owner,
pid = null,
data = null,
category = [],
tag = [],
5 years ago
hidden = false,
}) {
validator.checkTitle(title);
validator.checkContent(content);
const res = await coll.insertOne({
content,
owner,
pid,
title,
data,
category,
tag,
hidden,
nSubmit: 0,
5 years ago
nAccept: 0,
});
return res.insertedId;
}
/**
* @param {string|ObjectID} pid
* @param {number} uid
* @returns {Pdoc}
*/
async function get(pid, uid = null) {
let query = {};
5 years ago
if (pid.generationTime || pid.length === 24) query = { _id: new ObjectID(pid) };
else query = { pid };
5 years ago
const pdoc = await coll.findOne(query);
if (!pdoc) throw new ProblemNotFoundError(pid);
if (uid) {
query.uid = uid;
5 years ago
pdoc.psdoc = await collStatus.findOne(query);
}
return pdoc;
}
/**
* @param {ObjectID} pid
* @returns {Pdoc}
*/
async function getById(_id) {
_id = new ObjectID(_id);
5 years ago
const pdoc = await coll.findOne({ _id });
if (!pdoc) throw new ProblemNotFoundError(_id);
return pdoc;
}
/**
* @param {string|ObjectID} pid
* @param {number} uid
* @returns {Pdoc[]}
*/
function getMany(query, sort, page, limit) {
5 years ago
return coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit)
.toArray();
}
/**
* @param {object} query
* @returns {Cursor}
*/
function getMulti(query) {
return coll.find(query);
}
/**
* @param {object} query
* @returns {Cursor}
*/
function getMultiStatus(query) {
return collStatus.find(query);
}
/**
* @param {ObjectID} _id
* @param {object} query
* @returns {Pdoc}
*/
async function edit(_id, $set) {
if ($set.title) validator.checkTitle($set.title);
if ($set.content) validator.checkContent($set.content);
await coll.findOneAndUpdate({ _id }, { $set });
5 years ago
const pdoc = await getById(_id);
if (!pdoc) throw new ProblemNotFoundError(_id);
return pdoc;
}
5 years ago
async function inc(_id, field, n) {
await coll.findOneAndUpdate({ _id }, { $inc: { [field]: n } });
const pdoc = await getById(_id);
if (!pdoc) throw new ProblemNotFoundError(_id);
return pdoc;
}
5 years ago
function count(query) {
return coll.find(query).count();
}
async function random(query) {
5 years ago
const pdocs = coll.find(query);
const pcount = await pdocs.count();
if (pcount) {
5 years ago
const pdoc = await pdocs.skip(Math.floor(Math.random() * pcount)).limit(1).toArray()[0];
return pdoc.pid;
5 years ago
} return null;
}
async function getList(pids) {
5 years ago
const r = {};
for (const pid of pids) r[pid] = await get(pid); // eslint-disable-line no-await-in-loop
return r;
}
async function getListStatus(uid, pids) {
const psdocs = await getMultiStatus({ uid, pid: { $in: Array.from(new Set(pids)) } }).toArray();
const r = {};
for (const psdoc of psdocs) r[psdoc.pid] = psdoc;
return r;
}
5 years ago
async function updateStatus(pid, uid, rid, status) {
const res = await collStatus.findOneAndUpdate(
{ pid, uid },
{ $set: { rid, status } },
{ upsert: true },
);
return res.value;
}
async function setTestdata(_id, readStream) {
const pdoc = await getById(_id);
const f = gridfs.openUploadStream('data.zip');
await new Promise((resolve, reject) => {
readStream.pipe(f);
f.once('finish', resolve);
f.once('error', reject);
});
if (pdoc.data && typeof pdoc.data === 'object') gridfs.delete(this.pdoc.data);
5 years ago
return await edit(_id, { data: f.id }); // eslint-disable-line no-return-await
}
4 years ago
async function getData(pid) {
const pdoc = await get(pid);
if (!pdoc.data) return null;
return gridfs.openDownloadStream(pdoc.data);
}
global.Hydro.model.problem = module.exports = {
add,
5 years ago
inc,
get,
getMany,
edit,
count,
random,
getById,
getMulti,
5 years ago
getList,
getListStatus,
getMultiStatus,
setTestdata,
5 years ago
updateStatus,
4 years ago
getData,
5 years ago
};