This commit is contained in:
728
2022-12-18 14:40:21 +08:00
parent 412f7bdc3e
commit f73007c34d
20 changed files with 236 additions and 218 deletions

View File

@ -8,7 +8,7 @@ mkdir ui
cd ismism.ts
deno bundle src/serve.ts ../cli/serve.js
deno bundle cli/dbinit.ts ../cli/dbinit.js
deno bundle cli/dbreset.ts ../cli/dbreset.js
deno bundle cli/smstst.ts ../cli/smstst.js
cp ui/index.html ../ui/index.html

View File

@ -4,9 +4,3 @@ for c in $coll; do
echo importing $1/$c.json to ismism.$c
mongoimport --jsonArray -d=ismism -c=$c --mode=upsert --file=$1/$c.json
done
mongosh ismism --eval 'db.getCollectionNames().forEach(coll => {
const idx = db.getCollection(coll).getIndexes()
console.log(`${coll}:`)
printjson(idx)
})'

View File

@ -3,9 +3,9 @@ ssh i zsh < cli/dbexport.zsh
rm -rf dbexport
scp -r i:dbexport dbexport
deno run --allow-all cli/dbinit.js
zsh cli/dbimport.zsh dbexport
cd dbexport
zip -r ~/work/ismism/dbexport/dbexport-$(date +%Y%m%d-%H%M).zip .
cd ..
deno run --allow-all cli/dbreset.js
zsh cli/dbimport.zsh dbexport

View File

@ -1,3 +1,4 @@
zsh cli/dbpull.zsh
zsh cli/build.zsh
rm -rf release

View File

@ -1,4 +0,0 @@
import { init } from "../src/db.ts"
const coll = await init()
console.log(`collections created:\n${coll.join("\n")}`)

4
ismism.ts/cli/dbreset.ts Normal file
View File

@ -0,0 +1,4 @@
import { reset } from "../src/db.ts"
const coll = await reset()
console.log(`done resetting db:\n${coll.join("\n")}`)

View File

@ -14,6 +14,7 @@
"include": [
"src/",
"ui/",
"cli/",
"tst/"
]
},
@ -32,6 +33,7 @@
"include": [
"src/",
"ui/",
"cli/",
"tst/"
]
},

View File

@ -1,11 +1,12 @@
import { Collection, MongoClient } from "https://deno.land/x/mongo@v0.31.1/mod.ts"
import { Agenda, Fund, Dat, Soc, User, Work, Worker, Rec, Id, Role, Imgsrc, Txt } from "./typ.ts"
import { Agenda, Fund, Soc, User, Work, Worker, Imgsrc, Txt } from "./typ.ts"
const uri = "mongodb://127.0.0.1:27017"
const mongo = new MongoClient()
await mongo.connect(uri)
export const db = mongo.database("ismism")
const db = mongo.database("ismism")
export const coll = {
user: db.collection<User>("user"),
soc: db.collection<Soc>("soc"),
@ -16,21 +17,10 @@ export const coll = {
imgsrc: db.collection<Imgsrc>("imgsrc"),
txt: db.collection<Txt>("txt"),
}
export type Coll<T> = Collection<T>
export type CollId = (typeof coll)["user" | "soc" | "agenda"]
export type CollRec = "work" | "worker" | "fund"
export function collrec(
s: CollRec | string
): Collection<Rec> | null {
switch (s) {
case "work": return coll.work
case "worker": return coll.worker
case "fund": return coll.fund
}
return null
}
export async function init(
export async function reset(
) {
try { await db.dropDatabase() } catch (e) { console.error(e) }
await coll.user.createIndexes({
@ -92,163 +82,3 @@ export async function init(
})
return await db.listCollectionNames()
}
export function is_id(
id: number
) {
return id > 0
}
export function not_id(
id: number
) {
return !(id > 0)
}
export async function idname(
c: Collection<Id>,
id: number[],
): Promise<[number, string][]> {
id = [...new Set(id.filter(is_id))]
const d = await c.find(
{ _id: { $in: id } },
{ projection: { _id: 1, name: 1 } }
).toArray()
return d.map(d => [d._id, d.name])
}
async function uid_of_sid(
sid: number
): Promise<Pick<Soc, "uid"> | null> {
if (not_id(sid)) return null
const projection = { _id: 0, uid: 1 }
return await coll.soc.findOne({ _id: sid }, { projection }) ?? null
}
export type URole = [number, [number, Role][]][]
async function urole(
uid: number[]
): Promise<URole> {
const r = await coll.worker.aggregate([{
$match: { "_id.uid": { $in: uid.filter(is_id) } }
}, {
$group: { _id: "$_id.uid", r: { $push: { aid: "$_id.aid", role: "$role" } } }
}]).toArray() as unknown as { _id: number, r: { aid: number, role: Role }[] }[]
return r.map(({ _id, r }) => [_id, r.map(r => [r.aid, r.role])])
}
export type NRec = {
worker: number,
work: number,
fund: number
}
export async function nrec(
): Promise<NRec> {
const [worker, work, fund] = await Promise.all([
coll.worker.estimatedDocumentCount(),
coll.work.estimatedDocumentCount(),
coll.fund.estimatedDocumentCount(),
])
return { worker, work, fund }
}
export async function nrec_of_uid(
uid: number[]
): Promise<NRec> {
const filter = { "_id.uid": { $in: uid.filter(is_id) } }
const [worker, work, fund] = await Promise.all([
coll.worker.countDocuments(filter),
coll.work.countDocuments(filter),
coll.fund.countDocuments(filter),
])
return { worker, work, fund }
}
export async function nrec_of_aid(
aid: number
): Promise<NRec> {
if (not_id(aid)) return { worker: 0, work: 0, fund: 0 }
const filter = { "_id.aid": aid }
const [worker, work, fund] = await Promise.all([
coll.worker.countDocuments(filter),
coll.work.countDocuments(filter),
coll.fund.countDocuments(filter),
])
return { worker, work, fund }
}
export type RecOf<T extends Rec> = {
rec: T[],
uname: [number, string][],
aname: [number, string][],
urole: URole,
}
export async function rec_of_recent<T extends Rec>(
c: Collection<T>,
utc_lt: number,
limit: number
): Promise<RecOf<T>> {
const rec = await c.find(
// deno-lint-ignore no-explicit-any
{ "_id.utc": { $lt: utc_lt } } as any, {
sort: { "_id.utc": -1 },
limit
}).toArray()
const uid = rec.flatMap(r => [r._id.uid, ...r.referer])
const [uname, aname, ur] = await Promise.all([
idname(coll.user, uid),
idname(coll.agenda, rec.map(r => r._id.aid)),
urole(uid),
])
return { rec, uname, aname, urole: ur }
}
export async function rec_of_uid<T extends Rec>(
c: Collection<T>,
uid: number[],
): Promise<RecOf<T>> {
uid = uid.filter(is_id)
const rec = await c.find(
// deno-lint-ignore no-explicit-any
{ "_id.uid": { $in: uid } } as any,
{ sort: { "_id.utc": -1 } }
).toArray()
const [uname, aname, ur] = await Promise.all([
idname(coll.user, [...uid, ...rec.flatMap(r => r.referer)]),
idname(coll.agenda, rec.map(r => r._id.aid)),
urole(uid),
])
return { rec, uname, aname, urole: ur }
}
export async function rec_of_sid<T extends Rec>(
c: Collection<T>,
sid: number,
): Promise<RecOf<T>> {
const uid = (await uid_of_sid(sid))?.uid ?? []
return rec_of_uid(c, uid)
}
export async function rec_of_aid<T extends Rec>(
c: Collection<T>,
aid: number,
): Promise<RecOf<T>> {
if (not_id(aid)) return { rec: [], uname: [], aname: [], urole: [] }
const rec = await c.find(
// deno-lint-ignore no-explicit-any
{ "_id.aid": aid } as any,
{ sort: { "_id.utc": -1 } }
).toArray()
const uid = rec.flatMap(r => [r._id.uid, ...r.referer])
const [uname, aname, ur] = await Promise.all([
idname(coll.user, uid),
idname(coll.agenda, [aid]),
urole(uid),
])
return { rec, uname, aname, urole: ur }
}
export async function dat<T extends Dat>(
c: Collection<T>,
_id?: Dat["_id"]
): Promise<T | undefined> {
if (!_id) return undefined
return await c.findOne({ _id }, { projection: { _id: 0 } })
}

View File

@ -1,5 +1,5 @@
import { collrec, rec_of_aid, rec_of_recent, rec_of_sid, rec_of_uid } from "./db.ts"
import { agenda } from "./query/agenda.ts"
import { collrec, rec_of_aid, rec_of_recent, rec_of_sid, rec_of_uid } from "./query/rec.ts"
import { soc } from "./query/soc.ts"
import { user } from "./query/user.ts"

View File

@ -1,4 +1,6 @@
import { coll, nrec_of_aid, nrec, dat } from "../db.ts"
import { coll } from "../db.ts"
import { dat } from "./dat.ts"
import { nrec, nrec_of_aid } from "./rec.ts"
export async function agenda(
) {

View File

@ -0,0 +1,10 @@
import { Coll } from "../db.ts"
import { Dat } from "../typ.ts"
export async function dat<T extends Dat>(
c: Coll<T>,
_id?: Dat["_id"]
): Promise<T | undefined> {
if (!_id) return undefined
return await c.findOne({ _id }, { projection: { _id: 0 } })
}

46
ismism.ts/src/query/id.ts Normal file
View File

@ -0,0 +1,46 @@
import { coll, CollId } from "../db.ts"
import { Agenda, Id, Role, Soc, User } from "../typ.ts"
export function is_id(
id: Id["_id"]
) {
return id > 0
}
export function not_id(
id: Id["_id"]
) {
return !(id > 0)
}
export async function idname(
c: CollId,
id: Id["_id"][],
): Promise<[Id["_id"], Id["name"]][]> {
id = [...new Set(id.filter(is_id))]
const d = await c.find(
{ _id: { $in: id } },
{ projection: { _id: 1, name: 1 } }
).toArray()
return d.map(d => [d._id, d.name])
}
export async function uid_of_sid(
sid: Soc["_id"]
): Promise<Pick<Soc, "uid"> | null> {
if (not_id(sid)) return null
const projection = { _id: 0, uid: 1 }
return await coll.soc.findOne({ _id: sid }, { projection }) ?? null
}
export type URole = [User["_id"], [Agenda["_id"], Role][]][]
export async function urole(
uid: User["_id"][]
): Promise<URole> {
const r = await coll.worker.aggregate([{
$match: { "_id.uid": { $in: uid.filter(is_id) } }
}, {
$group: { _id: "$_id.uid", r: { $push: { aid: "$_id.aid", role: "$role" } } }
}]).toArray() as unknown as { _id: number, r: { aid: number, role: Role }[] }[]
return r.map(({ _id, r }) => [_id, r.map(r => [r.aid, r.role])])
}

124
ismism.ts/src/query/rec.ts Normal file
View File

@ -0,0 +1,124 @@
import { Coll, coll } from "../db.ts"
import { Agenda, Id, Rec, User } from "../typ.ts"
import { idname, is_id, not_id, uid_of_sid, urole, URole } from "./id.ts"
export type NRec = {
worker: number,
work: number,
fund: number
}
export async function nrec(
): Promise<NRec> {
const [worker, work, fund] = await Promise.all([
coll.worker.estimatedDocumentCount(),
coll.work.estimatedDocumentCount(),
coll.fund.estimatedDocumentCount(),
])
return { worker, work, fund }
}
export async function nrec_of_uid(
uid: User["_id"][]
): Promise<NRec> {
const filter = { "_id.uid": { $in: uid.filter(is_id) } }
const [worker, work, fund] = await Promise.all([
coll.worker.countDocuments(filter),
coll.work.countDocuments(filter),
coll.fund.countDocuments(filter),
])
return { worker, work, fund }
}
export async function nrec_of_aid(
aid: Agenda["_id"]
): Promise<NRec> {
if (not_id(aid)) return { worker: 0, work: 0, fund: 0 }
const filter = { "_id.aid": aid }
const [worker, work, fund] = await Promise.all([
coll.worker.countDocuments(filter),
coll.work.countDocuments(filter),
coll.fund.countDocuments(filter),
])
return { worker, work, fund }
}
export type RecOf<T extends Rec> = {
rec: T[],
uname: [User["_id"], User["name"]][],
aname: [Agenda["_id"], Agenda["name"]][],
urole: URole,
}
export type CollRec = "work" | "worker" | "fund"
export function collrec(
c: CollRec | string
): Coll<Rec> | null {
switch (c) {
case "work": return coll.work
case "worker": return coll.worker
case "fund": return coll.fund
}
return null
}
export async function rec_of_recent<T extends Rec>(
c: Coll<T>,
utc_lt: Id["utc"],
limit: number
): Promise<RecOf<T>> {
const rec = await c.find(
// deno-lint-ignore no-explicit-any
{ "_id.utc": { $lt: utc_lt } } as any, {
sort: { "_id.utc": -1 },
limit
}).toArray()
const uid = rec.flatMap(r => [r._id.uid, ...r.referer])
const [uname, aname, ur] = await Promise.all([
idname(coll.user, uid),
idname(coll.agenda, rec.map(r => r._id.aid)),
urole(uid),
])
return { rec, uname, aname, urole: ur }
}
export async function rec_of_uid<T extends Rec>(
c: Coll<T>,
uid: Id["utc"][],
): Promise<RecOf<T>> {
uid = uid.filter(is_id)
const rec = await c.find(
// deno-lint-ignore no-explicit-any
{ "_id.uid": { $in: uid } } as any,
{ sort: { "_id.utc": -1 } }
).toArray()
const [uname, aname, ur] = await Promise.all([
idname(coll.user, [...uid, ...rec.flatMap(r => r.referer)]),
idname(coll.agenda, rec.map(r => r._id.aid)),
urole(uid),
])
return { rec, uname, aname, urole: ur }
}
export async function rec_of_sid<T extends Rec>(
c: Coll<T>,
sid: number,
): Promise<RecOf<T>> {
const uid = (await uid_of_sid(sid))?.uid ?? []
return rec_of_uid(c, uid)
}
export async function rec_of_aid<T extends Rec>(
c: Coll<T>,
aid: number,
): Promise<RecOf<T>> {
if (not_id(aid)) return { rec: [], uname: [], aname: [], urole: [] }
const rec = await c.find(
// deno-lint-ignore no-explicit-any
{ "_id.aid": aid } as any,
{ sort: { "_id.utc": -1 } }
).toArray()
const uid = rec.flatMap(r => [r._id.uid, ...r.referer])
const [uname, aname, ur] = await Promise.all([
idname(coll.user, uid),
idname(coll.agenda, [aid]),
urole(uid),
])
return { rec, uname, aname, urole: ur }
}

View File

@ -1,4 +1,4 @@
import { nrec } from "../db.ts";
import { nrec } from "./rec.ts"
export async function recent(
) {

View File

@ -1,5 +1,7 @@
import { coll, idname, not_id, nrec_of_uid } from "../db.ts"
import { nrec_of_uid } from "./rec.ts"
import { Soc } from "../typ.ts"
import { idname, not_id } from "./id.ts"
import { coll } from "../db.ts"
async function soc_of_sid(
sid: number

View File

@ -1,5 +1,7 @@
import { coll, idname, not_id, nrec_of_uid } from "../db.ts"
import { coll } from "../db.ts"
import { User } from "../typ.ts"
import { idname, not_id } from "./id.ts"
import { nrec_of_uid } from "./rec.ts"
import { soc_of_uid } from "./soc.ts"
async function user_of_uid(

View File

@ -1,12 +1,16 @@
import { assert, assertEquals } from "https://deno.land/std@0.154.0/testing/asserts.ts";
import type { RecOf } from "../src/db.ts"
import { assert, assertEquals } from "https://deno.land/std@0.163.0/testing/asserts.ts"
import type { CollRec, RecOf } from "../src/query/rec.ts"
import { Agenda, query, Soc, User } from "../src/query.ts"
import type { Work, Worker, Fund } from "../src/typ.ts"
import type * as T from "../src/typ.ts"
function p(
// deno-lint-ignore no-explicit-any
obj: any
obj: {
coll?: CollRec,
uid?: T.User["_id"],
sid?: T.Soc["_id"],
aid?: T.Agenda["_id"],
utc?: T.Id["utc"],
}
) {
return new URLSearchParams(Object.entries(obj).map(([k, v]) => [k, `${v}`]))
}
@ -18,9 +22,9 @@ Deno.test("user", async () => {
const uname = new Map(u.uname)
assert(uname.get(u.referer[0]) === "未明子")
const [worker, work, fund] = await Promise.all([
await query("rec_of_uid", p({ coll: "worker", uid: 728 })) as RecOf<Worker>,
await query("rec_of_uid", p({ coll: "work", uid: 728 })) as RecOf<Work>,
await query("rec_of_uid", p({ coll: "fund", uid: 728 })) as RecOf<Fund>,
await query("rec_of_uid", p({ coll: "worker", uid: 728 })) as RecOf<T.Worker>,
await query("rec_of_uid", p({ coll: "work", uid: 728 })) as RecOf<T.Work>,
await query("rec_of_uid", p({ coll: "fund", uid: 728 })) as RecOf<T.Fund>,
])
assert(worker.rec.length === 1 && work.rec.length === 2 && fund.rec.length === 3)
assertEquals(work.urole, [[728, [[1, "志愿者"]]]])
@ -35,9 +39,9 @@ Deno.test("soc", async () => {
assert(uname.get(s.uid[1]) === "万大可" && uname.get(s.referer[0]) === "未明子")
assert(s.nrec.worker === 2 && s.nrec.work === 4 && s.nrec.fund === 3)
const [worker, work, fund] = await Promise.all([
await query("rec_of_sid", p({ coll: "worker", sid: 2 })) as RecOf<Worker>,
await query("rec_of_sid", p({ coll: "work", sid: 2 })) as RecOf<Work>,
await query("rec_of_sid", p({ coll: "fund", sid: 2 })) as RecOf<Fund>,
await query("rec_of_sid", p({ coll: "worker", sid: 2 })) as RecOf<T.Worker>,
await query("rec_of_sid", p({ coll: "work", sid: 2 })) as RecOf<T.Work>,
await query("rec_of_sid", p({ coll: "fund", sid: 2 })) as RecOf<T.Fund>,
])
assert(worker.rec.length === 2 && work.rec.length === 4 && fund.rec.length === 3)
assertEquals(work.urole.sort(), [[137, [[1, "志愿者"]]], [728, [[1, "志愿者"]]]].sort())
@ -52,9 +56,9 @@ Deno.test("agenda", async () => {
assertEquals(a1.referer, [1, 2])
assertEquals(a4.referer, [1, 2])
const [worker, work, fund] = await Promise.all([
await query("rec_of_aid", p({ coll: "worker", aid: a1._id })) as RecOf<Worker>,
await query("rec_of_aid", p({ coll: "work", aid: a1._id })) as RecOf<Work>,
await query("rec_of_aid", p({ coll: "fund", aid: a1._id })) as RecOf<Fund>,
await query("rec_of_aid", p({ coll: "worker", aid: a1._id })) as RecOf<T.Worker>,
await query("rec_of_aid", p({ coll: "work", aid: a1._id })) as RecOf<T.Work>,
await query("rec_of_aid", p({ coll: "fund", aid: a1._id })) as RecOf<T.Fund>,
])
assert(worker.rec.length === 6 && work.rec.length === 8 && fund.rec.length === 6)
assert(a4.imgsrc && a4.imgsrc.img.length === 4 && a1.imgsrc === undefined)
@ -64,9 +68,9 @@ Deno.test("agenda", async () => {
Deno.test("recent", async () => {
const { nrec: r } = await query("agenda", p({})) as Agenda
const [worker, work, fund] = await Promise.all([
await query("rec_of_recent", p({ coll: "worker", utc: Date.now() })) as RecOf<Worker>,
await query("rec_of_recent", p({ coll: "work", utc: Date.now() })) as RecOf<Work>,
await query("rec_of_recent", p({ coll: "fund", utc: Date.now() })) as RecOf<Fund>,
await query("rec_of_recent", p({ coll: "worker", utc: Date.now() })) as RecOf<T.Worker>,
await query("rec_of_recent", p({ coll: "work", utc: Date.now() })) as RecOf<T.Work>,
await query("rec_of_recent", p({ coll: "fund", utc: Date.now() })) as RecOf<T.Fund>,
])
assert(worker.rec.length === r.worker && work.rec.length == r.work && fund.rec.length == r.fund)
})

View File

@ -1,10 +1,10 @@
// deno-lint-ignore-file no-window-prefix
import { utc_medium, utc_short } from "../src/ontic/utc.ts"
import type { Agenda, Soc, User } from "../src/query.ts"
import { NRec, RecOf } from "../src/query/rec.ts"
import type { Goal, Tag, Rec, Work, Worker, Fund, Role } from "../src/typ.ts"
import type { NRec, RecOf } from "../src/db.ts"
const ver = "ismism-0.0.3-20221217"
const ver = "ismism-0.0.3-20221218"
let hash = ""
let agenda: Agenda
const role = new Map<number, Map<number, Role>>()
@ -186,8 +186,6 @@ function erecof(
})
}
function esum(
el: HTMLElement,
nrec: NRec

View File

@ -5,6 +5,7 @@
<title>主义主义 活动公示</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="refresh" content="3600">
<link rel="icon" type="x-icon" href="https://img.00000.host/2022/10/27/635a7048a7586.png">
<link rel="apple-touch-icon" href="https://img.00000.host/2022/10/27/635a7048a7586.png">
</head>

View File

@ -6,3 +6,5 @@ storage:
dbPath: db
net:
bindIp: 127.0.0.1, ::1
setParameter:
notablescan: 1