api update

This commit is contained in:
tmshv committed 2020-02-15 01:58:42 +03:00
1 parent 502a314c68
commit 99b412ea8d
19 files changed
+684

No files matched your search

+18
View File
@@ -0,0 +1,18 @@
import moment from 'moment'
moment.locale('ru')
/**
*
* Date -> DD.MM.YYYY
*
* @param date
*/
export const dateFormat = (date: string | Date) => moment(date).format('L')
/**
*
* Date -> Unix time
*
* @param date
*/
export const timestamp = (date: Date) => date.getTime()
+23
View File
@@ -0,0 +1,23 @@
const getExt = (name: string) => /\.(\w+)$/.exec(name)[1]
const defaultExt = (ext: string) => ext.toUpperCase()
const exts = ['png', 'pdf', 'jpg', 'jpeg', 'gif', 'doc']
const extd = ['ПНГ', 'ПДФ', 'ЖПГ', 'ЖПГ', 'ГИФ', 'ДОК']
const units = ['байт', 'КБ', 'МБ', 'ГБ', 'ТБ', 'ПБ']
export function ext(filname: string): string {
const ext = getExt(filname)
const i = exts.indexOf(ext)
return i > -1
? extd[i]
: defaultExt(ext)
}
export function size(bytes: any, precision = 1): string {
if (isNaN(parseFloat(bytes)) || !isFinite(bytes)) return ''
const number = Math.floor(Math.log(bytes) / Math.log(1024))
const size = (bytes / Math.pow(1024, Math.floor(number))).toFixed(precision)
return `${size} ${units[number]}`
}
+37
View File
@@ -0,0 +1,37 @@
function prioritySort(priority, key, list) {
priority = priority.reverse()
let weight = i => {
const p = key(i)
const index = priority.indexOf(p)
return index > -1
? index + 1
: 0
}
return sort(list, weight)
}
function sort(list, fn, reverse = false) {
return list
.map(i => [fn(i), i])
.sort((a, b) => reverse
? a[0] - b[0]
: b[0] - a[0])
.map(i => i[1])
}
function sortBy(fn, reverse = false) {
return (i1, i2) => {
const t1 = fn(i1)
const t2 = fn(i2)
return reverse
? t1 - t2
: t2 - t1
}
}
exports.prioritySort = prioritySort
exports.sortBy = sortBy
exports.sort = sort
+19
View File
@@ -0,0 +1,19 @@
export async function find(collection, query, {sort, skip, limit}) {
const cursor = collection.find(query)
if (sort) cursor.sort(sort)
if (skip) cursor.skip(skip)
if (limit) cursor.limit(limit)
return cursor.toArray()
}
export async function findOne(collection, query) {
return collection.findOne(query)
}
export async function total(collection, query) {
return collection
.find(query)
.count()
}
+12
View File
@@ -0,0 +1,12 @@
function titleCase(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
function trim(string) {
return string
.replace(/^\s+/, '')
.replace(/\s+$/, '')
}
exports.titleCase = titleCase
exports.trim = trim
+40
View File
@@ -0,0 +1,40 @@
function getPathWithNoTrailingSlash(path='') {
path = path.charAt(path.length - 1) === '/'
? path.substr(0, path.length - 1)
: path
return path.length === 0
? '/'
: path
}
function isEqualPath(a, b, ignoreTrailingSlash = false) {
return ignoreTrailingSlash
? getPathWithNoTrailingSlash(a) === getPathWithNoTrailingSlash(b)
: a === b
}
function isMatchPathPattern(pattern, path, ignoreTrailingSlash = false) {
const test = ignoreTrailingSlash
? getPathWithNoTrailingSlash(path)
: path
return pattern.test(test)
}
/**
*
* /article/* -> ^\/article\/[/\w\d-_]+\/?$
*
* @param url
* @return {RegExp}
*/
function urlToPattern(url) {
const u = url.replace('/*', '[/\\w\\d-_]+')
const pattern = `^${u}/?$`
return new RegExp(pattern)
}
exports.getPathWithNoTrailingSlash = getPathWithNoTrailingSlash
exports.isMatchPathPattern = isMatchPathPattern
exports.urlToPattern = urlToPattern
exports.isEqualPath = isEqualPath
+22
View File
@@ -0,0 +1,22 @@
const courses = {
painting: ['Живопись', ''],
drawing: ['Рисунок', '/courses/drawing'],
sculpting: ['Скульптура', ''],
sculpture: ['Скульптура', ''],
ceramic: ['Керамика', '/courses/ceramics'],
ceramics: ['Керамика', '/courses/ceramics'],
composition: ['Композиция', ''],
'easel-composition': ['Станковая композиция', ''],
dpi: ['ДПИ', ''],
'art-talks': ['Беседа по искусству', ''],
history: ['История искусства', '/courses/art-history'],
cg: ['Комп. графика', '/courses/cg'],
}
export function getCourse(id) {
const course = id in courses ? courses[id] : null
if (!course) return null
const [title, url] = course
return {id, title, url}
}
@@ -0,0 +1,44 @@
export const autumnMonth = 8;
export const springMonth = 0;
export function scheduleDate(schedule){
let {period, semester} = schedule;
let i = semester === 'spring' ? 1 : 0;
let year = getPeriod(period)[i];
return new Date(
year,
i ? springMonth : autumnMonth,
1
);
}
/**
*
* f("2012-2015") -> [2012, 2015]
* f(["2012", "2015"]) -> [2012, 2015]
* f([2012, 2015]) -> [2012, 2015]
*
* @param i
* @returns {*}
*/
export function getPeriod(i){
if (typeof i === 'string') return getPeriod(i.split('-'));
return i.map(Number);
}
/**
*
* f("2012-2015") -> "2012-2015"
* f(["2012", "2015"]) -> "2012-2015"
* f([2012, 2015]) -> "2012-2015"
*
* @param i
* @returns {*}
*/
export function getPeriodString(i) {
return getPeriod(i)
.join('-')
}
@@ -0,0 +1,71 @@
import Article from '../core/Article'
import ImageArtifactType from '../core/ImageArtifactType'
import { sortBy } from '../utils/sort'
import { timestamp } from '../lib/date'
import { encodeImage } from '../factory/image'
export const sortArticleByDate = sortBy(
x => timestamp(new Date(x.date))
)
export async function findArticlesNin(nin, skip, limit, sort) {
const query = {
_id: { $nin: nin }
}
return Article.find(query, {
sort,
skip,
limit,
})
}
export async function findArticles(page: number, pageSize: number) {
const limit = pageSize
const skip = (page - 1) * pageSize
const pinnedArticles = page === 1
? await Article.findPinned()
: []
const total = await Article.total()
const totalPages = Math.ceil(total / pageSize)
const id = i => i._id
const pinnedIds = pinnedArticles.map(id)
const articles = await findArticlesNin(pinnedIds, skip, limit, { date: -1 })
const prevPage = page > 1
? page - 1
: null
const nextPage = page < totalPages
? page + 1
: null
const items = [
...pinnedArticles.sort(sortArticleByDate),
...articles
]
.map(x => x.plain())
.map(x => ({
...x,
preview: !x.preview ? null : (
x.preview.getPicture(ImageArtifactType.MEDIUM)
)
}))
return {
items,
prevPage,
nextPage,
totalPages,
}
}
export function encodeArticle(article) {
return {
...article,
preview: encodeImage(article.preview),
}
}
@@ -0,0 +1,18 @@
const { collection } = require('../db')
// import { collection } from '../core/db'
export async function getCollective(query = {}) {
return await collection('collective')
.find(query)
.toArray();
}
/**
* Find a person by it's id
*
* @param id
* @returns {Promise|*}
*/
export async function getPerson(id) {
return await collection('collective').findOne({ id: id });
}
+14
View File
@@ -0,0 +1,14 @@
/**
*
* {a: {}, b: {}, ...} -> Map (a: Class, b: Class)
*
* @param Class
* @param object
*/
export function mapOf(Class, object) {
return Object
.entries(object)
.reduce((map, i) => (
map.set(i[0], new Class(i[1]))
), new Map())
}
+135
View File
@@ -0,0 +1,135 @@
import { createMap } from '../utils/common'
import { getCollective } from './collective'
import {
scheduleDate,
getPeriod as schedulePeriod,
getPeriodString
} from '../models/schedule'
import { getCourse } from '../models/course'
// import { last } from 'lodash'
// const sortBy = require('../lib/sort').sortBy
const SCHEDULES = 'schedules'
export async function findSchedule(collection, period, semester) {
const schedule = await collection(SCHEDULES).findOne({
$or: [
{ period: period },
{ period: schedulePeriod(period) }
],
semester: semester
})
return schedule ? migrate(schedule) : null
}
// async function findAllScheduleParams() {
// const schedules = await collection(SCHEDULES)
// .find({}, {
// period: 1,
// semester: 1,
// _id: 0,
// })
// .toArray()
// return schedules
// .map(i => ({
// ...i,
// period: schedulePeriod(i.period),
// }))
// .sort(sortBy(
// i => scheduleDate(i).getTime(),
// true,
// ))
// }
// function text({ semester, period }) {
// const word = semester => [['spring', 'Весна'], ['autumn', 'Осень']]
// .find(i => i[0] === semester)
// [1]
// const periodIndex = semester === 'spring' ? 1 : 0
// const year = period[periodIndex]
// return `${word(semester)} ${year} года`
// }
async function migrate(schedule) {
const versions = {
'1.0': migrate10to20,
'2.0': migrate20to30,
}
const version = schedule.version
const update = versions[version]
return version in versions ? migrate(update(schedule)) : schedule
}
function migrate10to20(schedule) {
const dayNames = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
const time = time => time
.map(i => i.split(' '))
const week = days => days.reduce((week, lesson, index) => {
if (lesson) {
const day = dayNames[index]
week[day] = [Object.assign(lesson, {
time: time(lesson.time)
})]
}
return week
}, {})
return Object.assign(schedule, {
version: '2.0',
period: schedulePeriod(schedule.period),
groups: schedule.schedule
.map(group => ({
name: group.group,
time: group.time,
week: week(group.week)
}))
})
}
async function migrate20to30(schedule) {
const days = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat']//, 'sun']
const persons = await getCollective()
const person = createMap(i => i.id, persons)
const week = week => days
.map(day => week[day] || [])
.map(lessons => lessons.map(lesson))
const lesson = i => ({
time: i.time,
course: getCourse(i.lesson),
teacher: person.has(i.teacher) ? teacher(i.teacher) : teacherDummy(i.teacher)
})
const teacher = id => ({
id: id,
url: `/teacher/${id}`,
name: personName(id)
})
const teacherDummy = name => ({
id: name,
url: null,
name: name
})
const personName = id => person.has(id) ? person.get(id).name : null
return Object.assign(schedule, {
version: '3.0',
groups: schedule.groups
.map(group => ({
name: group.name,
time: group.time,
week: week(group.week)
}))
})
}
+106
View File
@@ -0,0 +1,106 @@
/**
* Generates list of numbers
* @param {Number} start starting value
* @param {Number} num amount of numbers in list
* @param {Number} step difference value
* @return {Array} list of numbers
*/
export function range(start, num, step) {
var list = [];
var val = start;
for (var i = 0; i < num; i++) {
list.push(val);
val += step;
}
return list;
}
/**
* Return a closure that compares a Number argument with specified Number
* @param withNumber Number to compare with
* @returns {Function} (i) => {}
*/
export function numberEquals(withNumber) {
return i => i === withNumber;
}
/**
* Return a closure that compares a second Number argument with specified Number
* @param withNumber Number to compare with
* @returns {Function} (i) => {}
*/
export function indexEquals(withNumber) {
return (_, i) => i === withNumber;
}
/**
* Return a reduce function
* [['spring', 'Весена'], ['autumn', 'Осень']] -> 'Весна' or 'Осень' depend on byCondition result
* @param byCondition
* @param map
*/
export function select(byCondition, map = i => i) {
return (result, item) => byCondition(item) ? map(item) : map(result)
}
/**
* Select an item in Array by specified selector function
* @param scope
* @param selector
* @returns {*}
*/
export function choise(scope, selector) {
return scope.reduce(selector, null);
}
export function unique<T, U>(fn: (value: T) => U): (list: T[]) => U[] {
return list => Array.from(
new Set(list.map(fn))
)
}
export function branch(fn) {
let keys = unique(fn);
return list => keys(list)
.reduce((dict: any, key: any) => {
dict[key] = list.filter(
i => fn(i) === key
);
return dict;
}, {})
}
export function assign(dict, key, value) {
dict[key] = value;
return dict;
}
export function map(v, fn) {
return value => fn(value) ? v : value;
}
/**
*
* f(g, [x, y, z, ...]) => map of {g(x): x, g(y): y, g(z): z}
*
* @param list
* @param selector
* @returns {*}
*/
export function createMap(selector, list) {
return list.reduce((map, i) => map.set(selector(i), i), new Map());
}
/**
*
* f(g) => x ? g(x) : null
*
* @param {function} fn
* @returns {function(*=): null}
*/
export function nullSafe(fn) {
return i => i ? fn(i) : null;
}
+21
View File
@@ -0,0 +1,21 @@
import {range} from './common';
/**
*
* @param start
* @returns {*}
*/
export function getDates(start) {
const oneDay = (1000 * 60 * 60 * 24);
const startTime = start.getTime();
const startDay = start.getDay();
return range(0, 6, 1)
.reduce((dates, weekDay) => {
let delta = 1 + weekDay - startDay;
let time = startTime + (oneDay * delta);
return dates.concat([new Date(time)]);
}, [])
.map(i => i.getDate());
}
+13
View File
@@ -0,0 +1,13 @@
/**
*
* f([x, y, z, ...]) -> f(v) -> {v.x, v.y, v.z}
*
* @param fields
* @returns {function(*): *}
*/
export function select(fields=[]){
return i => fields.reduce((out, key) => {
out[key] = i[key];
return out;
}, {});
}
+8
View File
@@ -0,0 +1,8 @@
export function query(list) {
return list
.reduce(
(query, param) => query.concat([`${param[0]}=${param[1]}`]),
[]
)
.join('&');
}
+35
View File
@@ -0,0 +1,35 @@
export function pop(data){
var Populator = function(data){
this.target = data;
}
Populator.prototype = {
populate: function(p){
var self = this;
if(typeof p == "function"){
self.target = p(self.target);
}else if(p instanceof Array){
p.forEach(function(i){
self.target = self.populate(i).result();
});
}
return self;
},
result: function(){
return this.target;
}
};
var fun = new Populator(data);
return fun;
}
export function populate(fn, key){
return (dict) => {
let id = dict[key];
let value = fn(id);
dict[key] = value ? value : id;
return dict;
}
}
+26
View File
@@ -0,0 +1,26 @@
export function sortByPattern(list, pattern, map) {
let weight = i => {
const index = pattern.indexOf(map(i))
return index > -1 ? pattern.length - index : 0
}
return sortByWeight(list, weight)
}
export function sortByWeight(list, fn) {
return list
.map(i => [fn(i), i])
.sort((a, b) => b[0] - a[0])
.map(i => i[1])
}
export function sortBy(fn) {
return (i1, i2) => {
let t1 = fn(i1)
let t2 = fn(i2)
if (t1 < t2) return 1
if (t1 > t2) return -1
return 0
}
}
+22
View File
@@ -0,0 +1,22 @@
import {map} from './common';
/**
* Return a function that accept one String parameter and returns result of replacing.
*
* @param searchValue
* @param replaceValue
*/
export const stringReplace = (searchValue, replaceValue) =>
str => str.replace(searchValue, replaceValue);
let toTrue = map(true, i => i === 'true');
let toFalse = map(false, i => i === 'false');
const boolProcessors = [toTrue, toFalse];
export function stringToBoolean(string){
return boolProcessors
.reduce(
(value, fn) => fn(value),
string
);
}