Serverside Teachers

This commit is contained in:
tmshv committed 2017-05-26 01:38:49 +03:00
1 parent f257e08e7c
commit 525f630812
11 files changed
+275 -87

No files matched your search

-23
View File
@@ -93,29 +93,6 @@ app.config(($locationProvider, $routeProvider) => {
.then(i => i.data) .then(i => i.data)
} }
}, },
{
name: '/collective',
template: '<page-collective members="$resolve.list"></page-collective>',
title: 'Преподаватели',
resolve: {
list: api => api.teacher
.list(collectiveSortPattern)
.then(i => i.data)
}
},
{
name: '/teacher/:id',
template: '<page-teacher member="$resolve.profile"></page-teacher>',
title: 'Преподаватели',
resolve: {
profile: ($route, api) => {
let id = $route.current.params.id
return api.teacher
.fetch(id)
.then(i => i.data)
}
}
},
{ {
name: '/awards', name: '/awards',
template: '<page-awards items="$resolve.awards"></page-awards>', template: '<page-awards items="$resolve.awards"></page-awards>',
+12
View File
@@ -0,0 +1,12 @@
const React = require('react')
const Share = () => (
<div className="likely">
<div className="vkontakte">Поделиться</div>
<div className="facebook">Поделиться</div>
<div className="telegram">Отправить</div>
<div className="twitter">Твитнуть</div>
</div>
)
module.exports = Share
+40
View File
@@ -0,0 +1,40 @@
const React = require('react')
const Share = require('./Share')
const getHtml = require('../lib/component').getHtml
const Head = ({url, children}) => (
!url
? <h1>{children}</h1>
: <a href={url}><h2>{children}</h2></a>
)
const TeacherProfile = ({picture, name, url, profile, biography, shareable}) => (
<div className="teacher-profile">
<div className="teacher-profile__picture">
<img src={picture.url}/>
</div>
<div className="teacher-profile__info">
<Head url={url}>
<span>{name[0]} {name[1]}</span> <strong>{name[2]}</strong>
</Head>
<div>
<p className="teacher-profile__position"><i><strong>{profile.position}</strong></i></p>
<p>{profile.status}</p>
<p>Образование: {profile.edu}</p>
<p>Диплом: <i>{profile.diploma}</i></p>
</div>
<div className="teacher-profile__biography">
{getHtml(biography)}
</div>
</div>
{!shareable ? null : (
<Share/>
)}
</div>
)
module.exports = TeacherProfile
+72
View File
@@ -0,0 +1,72 @@
const Data = require('./Data')
const Image = require('./Image')
const {find, findOne} = require('../lib/store')
const store = () => Data.getStore(Teacher)
class Teacher {
static async find(query, options = {}) {
const items = await find(store(), query, options)
return Promise.all(items.map(processProfile))
}
static async findById(id) {
const data = await findOne(store(), {id})
if (!data) return null
return processProfile(data)
}
constructor(data) {
this.id = data.id
this.biography = data.biography
this.file = data.file
this.edu = data.edu
this.picture = data.picture
this.diploma = data.diploma
this.position = data.position
this.name = data.name
this.status = data.status
this.hash = data.hash
this.url = `/teacher/${this.id}`
}
/**
*
* "First Middle Last" -> "F. M. Last"
*
* @returns {string}
*/
shortName() {
const f = i => i.replace(/^(.).+/, '$1')
const [first, middle, last] = this.splitName()
return `${f(first)}. ${f(middle)}. ${last}`
}
/**
*
* "First Middle Last" -> ["First", "Middle", "Last"]
*
* @returns {Array|{index: number, input: string}}
*/
splitName() {
const name = this.name
const m = /(.+)\s(.+)\s(.+)/.exec(name)
return m.slice(1)
}
}
const processProfile = async profile => {
const imageId = profile.picture
const picture = await Image.findById(imageId)
return new Teacher({
...profile,
picture,
})
}
module.exports = Teacher
+2
View File
@@ -1,5 +1,6 @@
const Data = require('./Data') const Data = require('./Data')
const Album = require('./Album') const Album = require('./Album')
const Teacher = require('./Teacher')
const Image = require('./Image') const Image = require('./Image')
const {MongoClient, ObjectId} = require('mongodb') const {MongoClient, ObjectId} = require('mongodb')
@@ -28,6 +29,7 @@ export function id(i) {
function init(db) { function init(db) {
Data.setStore(Album, db.collection('albums')) Data.setStore(Album, db.collection('albums'))
Data.setStore(Image, db.collection('images')) Data.setStore(Image, db.collection('images'))
Data.setStore(Teacher, db.collection('collective'))
return db return db
} }
+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
+7 -4
View File
@@ -40,9 +40,12 @@ export default [
] ]
}, },
{ {
'text': 'Коллектив', text: 'Коллектив',
'url': '/collective', url: '/collective',
'color': 'pink' color: 'pink',
items: [
{url: '/teacher/*', text: ''},
]
}, },
{ {
text: 'Галерея', text: 'Галерея',
@@ -66,7 +69,7 @@ export default [
{url: 'structure', text: 'Структура и органы управления образовательной организацией'}, {url: 'structure', text: 'Структура и органы управления образовательной организацией'},
{url: 'education', text: 'Образование'}, {url: 'education', text: 'Образование'},
{url: 'standards', text: 'Образовательные стандарты'}, {url: 'standards', text: 'Образовательные стандарты'},
{url: '/collective', text: 'Руководство. Педагогический (научно-педагогический) состав'}, //{url: '/collective', text: 'Руководство. Педагогический (научно-педагогический) состав'},
{url: 'support', text: 'Материально-техническое обеспечение образовательного процесса'}, {url: 'support', text: 'Материально-техническое обеспечение образовательного процесса'},
{url: 'grants', text: 'Стипендии и иные виды материальной поддержки'}, {url: 'grants', text: 'Стипендии и иные виды материальной поддержки'},
{url: 'paid-services', text: 'Платные образовательные услуги'}, {url: 'paid-services', text: 'Платные образовательные услуги'},
-2
View File
@@ -8,7 +8,6 @@ import {index as indexFile} from '../config'
import sitemap from './sitemap' import sitemap from './sitemap'
import schedule from './schedule' import schedule from './schedule'
import documents from './documents' import documents from './documents'
import teachers from './teachers'
import pages from './pages' import pages from './pages'
export function routes(store) { export function routes(store) {
@@ -16,7 +15,6 @@ export function routes(store) {
sitemap(), sitemap(),
schedule(), schedule(),
documents(store), documents(store),
teachers(),
pages(), pages(),
]) ])
} }
+90 -55
View File
@@ -1,65 +1,100 @@
import co from 'co'; const React = require('react')
import compose from 'koa-compose'; const Article = require('../components/Article')
import route from 'koa-route'; const TeacherProfile = require('../components/TeacherProfile')
import {index, json} from './'; const Teacher = require('../core/Teacher')
import {sortByPattern} from '../utils/sort'; const ImageArtifactType = require('../core/ImageArtifactType')
import {c} from '../core/db'; const {render} = require('../lib/render')
const {prioritySort} = require('../lib/sort')
const getPathWithNoTrailingSlash = require('../lib/url').getPathWithNoTrailingSlash
let split = d => s => s.split(d); const {get} = require('koa-route')
let toArray = split(',');
export default function () { function getCollectiveMeta() {
return compose([ return {
collective(), title: 'Преподаватели',
list(), description: 'Преподаватели Шлиссельбургской ДХШ',
teacher() }
]);
};
function collective() {
return route.get('/collective', index());
} }
function list() { function getMeta(teacher) {
return route.get('/teacher/list', json( return {
async(ctx) => { title: teacher.name,
ctx.type = 'application/json'; description: 'Преподаватель Шлиссельбургской ДХШ',
let pattern = toArray(ctx.query.sort || ''); }
let data = await c('collective').find().toArray();
if (!data) {
ctx.status = 404;
return;
}
let collective = await Promise.all(
data.map(processProfile)
);
ctx.body = sortByPattern(collective, pattern, i => i.id);
}
));
} }
function teacher() { const pictureTypes = [
return route.get('/teacher/:id', json( ImageArtifactType.BIG,
async (ctx, id) => { ImageArtifactType.MEDIUM,
ctx.type = 'application/json'; ImageArtifactType.ORIGIN,
]
let data = await c('collective').findOne({id: id}); const profilePicture = profile => profile.picture.findArtifact(pictureTypes)
if (!data) {
ctx.status = 404;
return;
}
ctx.body = await processProfile(data); function getCollective(order) {
} const teachersSorted = prioritySort.bind(null, order, t => t.id)
))
return get('/collective', async ctx => {
const path = getPathWithNoTrailingSlash(ctx.path)
let teachers = await Teacher.find({})
if (teachers) {
teachers = teachersSorted(teachers)
const Component = (
<div className="page-collective content content_full">
<img className="page-collective__collective-image"
width="100%"
src="https://static.shlisselburg.org/art/graphics/collective.jpg"
/>
<div className="content content_semi-wide">
{teachers.map((teacher, index) => (
<TeacherProfile key={index}
profile={teacher}
picture={profilePicture(teacher)}
url={teacher.url}
biography={teacher.biography}
name={teacher.splitName()}
/>
))}
</div>
</div>
)
ctx.type = 'text/html'
ctx.body = await render(path, Component, getCollectiveMeta(), {menuPadding: false})
} else {
ctx.status = 404
}
})
} }
let processProfile = profile => co(function *() { function getTeacher() {
let imageId = profile.picture; return get('/teacher/:id', async (ctx, id) => {
profile.picture = yield c('images').findOne({_id: imageId}); const path = getPathWithNoTrailingSlash(ctx.path)
profile.url = `/teacher/${profile.id}`; const teacher = await Teacher.findById(id)
return profile;
}); if (teacher) {
const Component = (
<div className="content content_semi-wide">
<TeacherProfile profile={teacher}
picture={profilePicture(teacher)}
biography={teacher.biography}
name={teacher.splitName()}
shareable={true}
/>
</div>
)
ctx.type = 'text/html'
ctx.body = await render(path, Component, getMeta(teacher), {commentsEnabled: true})
} else {
ctx.status = 404
}
})
}
exports.getCollective = getCollective
exports.getTeacher = getTeacher
+12
View File
@@ -24,12 +24,22 @@ const home = require('./routes/home')
const article = require('./routes/articles') const article = require('./routes/articles')
const albums = require('./routes/albums') const albums = require('./routes/albums')
const gallery = require('./routes/gallery') const gallery = require('./routes/gallery')
const teachers = require('./routes/teachers')
const dirPublic = path.join(__dirname, '../public') const dirPublic = path.join(__dirname, '../public')
const dirTemplates = path.join(__dirname, 'templates') const dirTemplates = path.join(__dirname, 'templates')
handlebars.registerHelper('raw-helper', options => options.fn()) handlebars.registerHelper('raw-helper', options => options.fn())
const collectiveOrder = [
'mg-timasheva',
'va-sarzhin',
'od-gogoleva',
'my-valkova',
'vv-voronova',
'nv-andreeva',
]
export default function (store) { export default function (store) {
const $ = convert const $ = convert
@@ -58,6 +68,8 @@ export default function (store) {
app.use(article.getArticles(5)) app.use(article.getArticles(5))
app.use(article.getArticle()) app.use(article.getArticle())
app.use(albums.getAlbum()) app.use(albums.getAlbum())
app.use(teachers.getCollective(collectiveOrder))
app.use(teachers.getTeacher())
app.use(routes(store)) app.use(routes(store))
return Server(app.callback()) return Server(app.callback())
+3 -3
View File
@@ -4,13 +4,12 @@ $picture-width: 30%;
$info-width: 100% - $picture-width; $info-width: 100% - $picture-width;
.teacher-profile { .teacher-profile {
margin: 0 auto $double-margin;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
flex-wrap: wrap; flex-wrap: wrap;
&__picture { &__picture {
margin: 0 0 $double-margin;
width: $picture-width; width: $picture-width;
max-width: 15em; max-width: 15em;
min-width: 12em; min-width: 12em;
@@ -23,7 +22,8 @@ $info-width: 100% - $picture-width;
&__info { &__info {
width: $info-width; width: $info-width;
padding-left: $single-margin; padding-left: $double-margin;
margin: 0 0 $double-margin;
box-sizing: border-box; box-sizing: border-box;
h2 { h2 {