diff --git a/myapp/mutations/tag.py b/myapp/mutations/tag.py new file mode 100644 index 0000000..015278c --- /dev/null +++ b/myapp/mutations/tag.py @@ -0,0 +1,45 @@ +__author__ = 'RemiZOffAlex' +__email__ = 'remizoffalex@mail.ru' + +import logging + +from .. import lib, models + + +log = logging.getLogger(__name__) + + +def tag_as_dict( + tag: models.Tag, + fields: list = ['id', 'name'] +): + """Тег в словарь + """ + def field_pages(tag): + # Теги + result = [] + for tagLink in tag.tags: + newTag = tagLink.tag.as_dict() + result.append(newTag) + return result + + def field_user(tag): + # Пользователь + return tag.user.as_dict() + + funcs = { + 'pages': field_pages, + 'user': field_user, + } + result = {} + + for column in tag.__table__.columns: + if column.name in fields: + result[column.name] = getattr(tag, column.name) + + for field in fields: + if field in funcs: + func = funcs[field] + result[field] = func(tag) + + return result diff --git a/myapp/ns_api/tag/__init__.py b/myapp/ns_api/tag/__init__.py index 15b67cb..085e295 100644 --- a/myapp/ns_api/tag/__init__.py +++ b/myapp/ns_api/tag/__init__.py @@ -4,7 +4,7 @@ __email__ = 'remizoffalex@mail.ru' from . import ( # noqa F401 + common, note, page, - tag ) diff --git a/myapp/ns_api/tag/tag.py b/myapp/ns_api/tag/common.py similarity index 85% rename from myapp/ns_api/tag/tag.py rename to myapp/ns_api/tag/common.py index 42353d1..be20a33 100644 --- a/myapp/ns_api/tag/tag.py +++ b/myapp/ns_api/tag/common.py @@ -4,6 +4,25 @@ __email__ = 'remizoffalex@mail.ru' from .. import jsonrpc, login_required from ... import models +from ...mutations.tag import tag_as_dict + +@jsonrpc.method('tag') +def tag_id( + id: int, + fields: list = ['id', 'name'] +) -> dict: + """Тег + """ + tag = models.db_session.query( + models.Tag + ).filter( + models.Tag.id == id + ).first() + if tag is None: + raise ValueError + + result = tag_as_dict(tag, fields) + return result @jsonrpc.method('tag.add') diff --git a/myapp/ns_api/tag/page.py b/myapp/ns_api/tag/page.py index 4aefa42..a0165ca 100644 --- a/myapp/ns_api/tag/page.py +++ b/myapp/ns_api/tag/page.py @@ -62,7 +62,12 @@ def tag_page_delete(tag: int, id: int) -> bool: @jsonrpc.method('tag.pages') -def tag_pages_list(id: int, page: int) -> list: +def tag_pages_list( + id: int, + page: int = 1, + order_by: dict = {'field': 'title', 'order': 'asc'}, + fields: list = ['id', 'title', 'tags', 'user'] +) -> list: """Список статей """ tag = models.db_session.query( @@ -81,9 +86,19 @@ def tag_pages_list(id: int, page: int) -> list: models.Page ).filter( models.Page.id.in_(indexes) - ).order_by( - models.Page.title.asc() ) + + # Сортировка + if order_by['field'] not in ['created', 'id', 'title', 'updated']: + raise ValueError + if order_by['order'] not in ['asc', 'desc']: + raise ValueError + field = getattr(models.Page, order_by['field']) + order = getattr(field, order_by['order']) + pages = pages.order_by( + order() + ) + pages = lib.getpage( pages, page, diff --git a/myapp/ns_tag/templates/user/tag.html b/myapp/ns_tag/templates/user/tag.html deleted file mode 100644 index c9921aa..0000000 --- a/myapp/ns_tag/templates/user/tag.html +++ /dev/null @@ -1,103 +0,0 @@ -{% extends "private/skeleton.html" %} -{% block content %} - -{% raw %} -

- -Тег {{ tag.name }}

-
-{% endraw %} -{% include '/user/tag_menu.html' %} - - - -{% include '/inc/pages.html' %} - - - -{% endblock %} - -{% block breadcrumb %} -{% raw %} - -{% endraw %} -{% endblock %} - -{% block script %} - -{% endblock %} diff --git a/myapp/ns_tag/templates/user/tag_menu.html b/myapp/ns_tag/templates/user/tag_menu.html deleted file mode 100644 index fe1cade..0000000 --- a/myapp/ns_tag/templates/user/tag_menu.html +++ /dev/null @@ -1,14 +0,0 @@ -
-
- -
-
- -
- -
Заметки
-Заметки - -
-
-
diff --git a/myapp/ns_tag/templates/user/tag_notes.html b/myapp/ns_tag/templates/user/tag_notes.html deleted file mode 100644 index 05ef41e..0000000 --- a/myapp/ns_tag/templates/user/tag_notes.html +++ /dev/null @@ -1,105 +0,0 @@ -{% extends "private/skeleton.html" %} -{% block content %} - -{% raw %} -

- -Тег {{ tag.name }}

-
-{% endraw %} - -{% include 'user/tag_menu.html' %} - - - -{% include '/inc/notes.html' %} - - - -{% endblock %} - -{% block breadcrumb %} -{% raw %} - -{% endraw %} -{% endblock %} - -{% block script %} - - - -{% endblock %} diff --git a/myapp/ns_tag/templates/user/tags.html b/myapp/ns_tag/templates/user/tags.html deleted file mode 100644 index bd2b9b8..0000000 --- a/myapp/ns_tag/templates/user/tags.html +++ /dev/null @@ -1,115 +0,0 @@ -{% extends "private/skeleton.html" %} -{% block content %} - -

-
-Список тегов

-
- -{% raw %} -
-
- -
-
-
-
-
- -
-
- -
-
-{% endraw %} - -{% endblock %} - -{% block breadcrumb %} - -{% endblock %} - -{% block script %} - -{% endblock %} diff --git a/myapp/ns_tag/views_user.py b/myapp/ns_tag/views_user.py index 2987b92..b787e32 100644 --- a/myapp/ns_tag/views_user.py +++ b/myapp/ns_tag/views_user.py @@ -12,7 +12,7 @@ def tags(): """ pagedata = {} pagedata['title'] = 'Список меток - ' + app.config['TITLE'] - body = render_template('user/tags.html', pagedata=pagedata) + body = render_template('/private/skeleton.html', pagedata=pagedata) return body @@ -32,15 +32,8 @@ def tag_id(id): tag.name, app.config['TITLE'] ) - pagedata['tag'] = tag.as_dict() - pagedata['pagination'] = { - "page": 1, - "per_page": app.config['ITEMS_ON_PAGE'], - "size": 0 - } - - body = render_template('user/tag.html', pagedata=pagedata) + body = render_template('/private/skeleton.html', pagedata=pagedata) return body @@ -60,13 +53,6 @@ def tag_notes(id): tag.name, app.config['TITLE'] ) - pagedata['tag'] = tag.as_dict() - pagedata['pagination'] = { - "page": 1, - "per_page": app.config['ITEMS_ON_PAGE'], - "size": 0 - } - - body = render_template('user/tag_notes.html', pagedata=pagedata) + body = render_template('/private/skeleton.html', pagedata=pagedata) return body diff --git a/myapp/templates/error.html b/myapp/templates/error.html index 35737a7..4720bee 100644 --- a/myapp/templates/error.html +++ b/myapp/templates/error.html @@ -1,4 +1,8 @@ -{% extends "skeleton.html" %} +{% if user %} +{% extends "/public/skeleton.html" %} +{% else %} +{% extends "/private/skeleton.html" %} +{% endif %} {% block content %}

{{ error_code }}: {{ error_message }}

diff --git a/myapp/templates/public/domains/tag/inc.j2 b/myapp/templates/public/domains/tag/inc.j2 index d39bc14..8b47567 100644 --- a/myapp/templates/public/domains/tag/inc.j2 +++ b/myapp/templates/public/domains/tag/inc.j2 @@ -1,3 +1,5 @@ +{% include '/public/domains/tag/menu.js' %} +{% include '/public/domains/tag/pages.js' %} {% include '/public/domains/tag/tag.js' %} {% include '/public/domains/tag/tags.js' %} @@ -5,6 +7,8 @@ Object.assign( routes, { "/tag/:id": layout_decorator(Tag), + "/tag/:id/pages": layout_decorator(TagPages), + "/tag/:id/pages/:page": layout_decorator(TagPages), "/tags": layout_decorator(Tags), } ); diff --git a/myapp/templates/public/domains/tag/menu.js b/myapp/templates/public/domains/tag/menu.js new file mode 100644 index 0000000..dd59060 --- /dev/null +++ b/myapp/templates/public/domains/tag/menu.js @@ -0,0 +1,45 @@ +function MenuTag() { + let data = { + menuitem: null, + tag: null, + }; + function button_common() { + if (data.menuitem===null) { + return {tag: '<', children: '
'}; + } else { + return m(m.route.Link, {class: "btn btn-outline-secondary me-2 text-decoration-none", href: `/tag/${data.tag.id}`, title: data.tag.name}, m('i', {class: 'fa fa-bars'})) + } + }; + function button_pages() { + if (data.menuitem==='pages') { + return {tag: '<', children: '
Статьи
'}; + } else { + return m(m.route.Link, {class: "btn btn-outline-secondary me-2 text-decoration-none", href: `/tag/${data.tag.id}/pages`}, 'Статьи') + } + }; + return { + oninit: function(vnode) { + console.log('MenuTag.oninit'); + for (let key in vnode.attrs){ + data[key] = vnode.attrs[key]; + }; + }, + onupdate: function(vnode) { + console.log('MenuTag.onupdate'); + for (let key in vnode.attrs){ + data[key] = vnode.attrs[key]; + }; + }, + view: function(vnode) { + console.log('MenuTag.view'); + if (data.tag!=null) { + return m('div', {class: 'row'}, + m('div', {class: 'col py-2'}, [ + button_common(), + button_pages(), + ]), + ); + }; + } + }; +}; diff --git a/myapp/templates/public/domains/tag/pages.js b/myapp/templates/public/domains/tag/pages.js new file mode 100644 index 0000000..1ca5adc --- /dev/null +++ b/myapp/templates/public/domains/tag/pages.js @@ -0,0 +1,162 @@ +function TagPages() { + let data = { + filter: PanelFilter(), + order_by: PanelOrderBy({ + field: 'title', + fields: [ + {value: 'id', text: 'ID'}, + {value: 'title', text: 'заголовку'}, + {value: 'created', text: 'дате создания'}, + {value: 'updated', text: 'дате обновления'} + ], + clickHandler: pages_get, + order: 'asc', + }), + tag: null, + raw_pages: [], + get pages() { + let result = data.raw_pages.filter(page_filter); + return result; + }, + pagination: { + page: 1, + size: 0, + prefix_url: '/pages' + }, + }; + function breadcrumbs_render() { + let result = m('ul', {class: 'breadcrumb mt-2'}, [ + m('li', {class: 'breadcrumb-item'}, m(m.route.Link, {href: '/'}, m('i', {class: 'fa fa-home'}))), + m('li', {class: 'breadcrumb-item'}, m(m.route.Link, {href: '/tags'}, 'Список тегов')), + m('li', {class: 'breadcrumb-item'}, m(m.route.Link, {href: `/tag/${data.tag.id}`}, data.tag.name)), + m('li', {class: 'breadcrumb-item active'}, 'Список статей'), + ]); + return result; + }; + function page_filter(page) { + let filter = data.filter.data; + let value = filter.value; + if ( value.length<1 ) { + return true; + } + if ( page.title.toLowerCase().includes(value.toLowerCase()) ) { + return true; + } + return false; + }; + function tag_get(id) { + m.request({ + url: '/api', + method: "POST", + body: { + "jsonrpc": "2.0", + "method": 'tag', + "params": { + "id": id + }, + "id": get_id() + } + }).then( + function(response) { + if ('result' in response) { + data.tag = response['result']; + data.pagination.prefix_url = `/tag/${data.tag.id}/pages`; + document.title = `Статьи с тегом [${data.tag.name}`; + pages_get(); + } + } + ); + }; + function pages_get() { + m.request({ + url: '/api', + method: "POST", + body: [ + { + "jsonrpc": "2.0", + "method": 'tag.pages', + "params": { + "id": data.tag.id, + "page": data.pagination.page, + "order_by": data.order_by.value, + "fields": ["id", "title", "tags"] + }, + "id": get_id() + }, + { + "jsonrpc": "2.0", + "method": 'tag.pages.count', + "params": { + "id": data.tag.id, + }, + "id": get_id() + } + ] + + }).then( + function(response) { + if ('result' in response[0]) { + data.raw_pages = response[0]['result']; + } + if ('result' in response[1]) { + data.pagination.size = response[1]['result']; + } + } + ); + } + return { + oninit: function(vnode) { + console.log('TagPages.oninit'); + if (vnode.attrs.page!==undefined) { + data.pagination.page = Number(vnode.attrs.page); + }; + tag_get(vnode.attrs.id); + }, + onbeforeupdate: function(vnode) { + console.log('TagPages.onbeforeupdate'); + if (vnode.attrs.page!==undefined) { + if (data.pagination.page.toString() != vnode.attrs.page) { + data.pagination.page = Number(vnode.attrs.page); + pages_get(); + } + }; + }, + view: function(vnode) { + console.log('TagPages.view'); + result = []; + if (data.tag!=null) { + result.push( + breadcrumbs_render(), + m('div', {class: 'row'}, + m('div', {class: 'col h1 py-2'}, [ + m('div', {class: "btn-group btn-group-lg me-2"}, [ + m(m.route.Link, {class: "btn btn-outline-secondary", href: `/tag/${data.tag.id}`, title: "Вернуться"}, m('i', {class: "fa fa-chevron-left"})), + m('button', {type: "button", class: "btn btn-outline-secondary", onclick: function() { panel_show(data.filter.data) }}, + m('i', {class: "fa fa-filter"}) + ), + m('button', {type: "button", class: "btn btn-outline-secondary", onclick: function() { panel_show(data.order_by.data) }}, + m('i', {class: "fa fa-sort-alpha-asc"}) + ) + ]), + `Статьи с тегом [${data.tag.name}]` + ]), + m('hr'), + ) + ); + + // result.push(m(MenuTag, {menuitem: 'pages', tag: data.tag})); + result.push({tag: MenuTag, attrs: {menuitem: 'pages', tag: data.tag}}); + + result.push(m(data.filter)); + result.push(m(data.order_by)); + result.push(m(Pagination, data.pagination)); + if (data.pages.length>0) { + result.push(m(ComponentPages, {pages: data.pages})); + result.push(m(Pagination, data.pagination)); + }; + result.push(breadcrumbs_render()); + }; + return result; + } + } +}; diff --git a/myapp/templates/public/domains/tag/tag.js b/myapp/templates/public/domains/tag/tag.js index b68817e..964d105 100644 --- a/myapp/templates/public/domains/tag/tag.js +++ b/myapp/templates/public/domains/tag/tag.js @@ -58,17 +58,6 @@ function Tag() { // result.push(m(MenuTag, {"tag1": data.tag})); result.push({ tag: MenuTag, attrs: { tag: data.tag } }); - result.push( - m('div', { class: 'row' }, [ - m('div', { class: "col-md-4 py-2" }, - m(m.route.Link, { class: "btn btn-outline-secondary btn-lg w-100", href: `/tag/${data.tag.id}/notes` }, 'Заметки с тегом'), - ), - m('div', { class: "col-md-4 py-2" }, - m(m.route.Link, { class: "btn btn-outline-secondary btn-lg w-100", href: `/tag/${data.tag.id}/pages` }, 'Статьи с тегом'), - ), - ]) - ); - result.push( m('div', { class: 'row' }, m('div', { class: "col py-2" }, m.trust(data.tag.description)),