api update
This commit is contained in:
1 parent
502a314c68
commit
99b412ea8d
19 files changed
+684
No files matched your search
@@ -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()
|
||||
@@ -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]}`
|
||||
}
|
||||
@@ -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
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 });
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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)
|
||||
}))
|
||||
})
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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;
|
||||
}, {});
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export function query(list) {
|
||||
return list
|
||||
.reduce(
|
||||
(query, param) => query.concat([`${param[0]}=${param[1]}`]),
|
||||
[]
|
||||
)
|
||||
.join('&');
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user