From 77938aac04f438378de5fe5e968a9dc1ccb72b84 Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Wed, 31 Aug 2022 18:51:31 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20=D0=BD?= =?UTF-8?q?=D0=B0=D0=B4=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/components/Api/Api.vue | 10 + client/components/Search/Search.vue | 41 ++++- .../SelectGenreDialog/SelectGenreDialog.vue | 172 ++++++++++++++++++ client/quasar.js | 4 +- server/controllers/WebSocketController.js | 8 + server/core/DbCreator.js | 5 +- server/core/WebWorker.js | 39 ++++ .../genres.json => genres/genresText.js} | 4 +- server/core/genres/index.js | 53 ++++++ 9 files changed, 328 insertions(+), 8 deletions(-) create mode 100644 client/components/Search/SelectGenreDialog/SelectGenreDialog.vue rename server/core/{Genres/genres.json => genres/genresText.js} (98%) create mode 100644 server/core/genres/index.js diff --git a/client/components/Api/Api.vue b/client/components/Api/Api.vue index ade4a3d..924c2cf 100644 --- a/client/components/Api/Api.vue +++ b/client/components/Api/Api.vue @@ -156,6 +156,16 @@ class Api { return response; } + async getGenreTree() { + const response = await this.request({action: 'get-genre-tree'}); + + if (response.error) { + throw new Error(response.error); + } + + return response; + } + async getConfig() { const response = await this.request({action: 'get-config'}); diff --git a/client/components/Search/Search.vue b/client/components/Search/Search.vue index 14588e8..56c60c2 100644 --- a/client/components/Search/Search.vue +++ b/client/components/Search/Search.vue @@ -160,6 +160,8 @@ + + @@ -169,6 +171,7 @@ import vueComponent from '../vueComponent.js'; import { reactive } from 'vue'; import PageScroller from './PageScroller/PageScroller.vue'; +import SelectGenreDialog from './SelectGenreDialog/SelectGenreDialog.vue'; import authorBooksStorage from './authorBooksStorage'; import DivBtn from '../share/DivBtn.vue'; import Dialog from '../share/Dialog.vue'; @@ -180,6 +183,7 @@ import _ from 'lodash'; const componentOptions = { components: { PageScroller, + SelectGenreDialog, Dialog, DivBtn }, @@ -236,6 +240,8 @@ class Search { loadingMessage = ''; loadingMessage2 = ''; settingsDialogVisible = false; + selectGenreDialogVisible = false; + page = 1; pageCount = 1; @@ -262,6 +268,8 @@ class Search { totalFound = 0; bookRowsOnPage = 100; inpxHash = ''; + genreTree = []; + genreTreeInpxHash = ''; limitOptions = [ {label: '10', value: 10}, @@ -338,7 +346,7 @@ class Search { } selectGenre() { - this.$root.stdDialog.alert('Выбор жанра'); + this.selectGenreDialogVisible = true; } selectLang() { @@ -455,9 +463,8 @@ class Search { try { let result; - const key = `${authorId}-${this.inpxHash}`; - if (this.abCacheEnabled) { + const key = `${authorId}-${this.inpxHash}`; const data = await authorBooksStorage.getData(key); if (data) { result = JSON.parse(data); @@ -516,6 +523,33 @@ class Search { } } + async updateGenreTreeIfNeeded() { + try { + if (this.genreTreeInpxHash !== this.inpxHash) { + let result; + + if (this.abCacheEnabled) { + const key = `genre-tree-${this.inpxHash}`; + const data = await authorBooksStorage.getData(key); + if (data) { + result = JSON.parse(data); + } else { + result = await this.api.getGenreTree(); + + await authorBooksStorage.setData(key, JSON.stringify(result)); + } + } else { + result = await this.api.getGenreTree(); + } + + this.genreTree = result.genreTree; + this.genreTreeInpxHash = result.inpxHash; + } + } catch (e) { + this.$root.stdDialog.alert(e.message, 'Ошибка'); + } + } + async updateTableData() { let result = []; @@ -591,6 +625,7 @@ class Search { this.inpxHash = result.inpxHash; this.searchResult = result; + await this.updateGenreTreeIfNeeded(); await this.updateTableData(); this.scrollToTop(); } catch (e) { diff --git a/client/components/Search/SelectGenreDialog/SelectGenreDialog.vue b/client/components/Search/SelectGenreDialog/SelectGenreDialog.vue new file mode 100644 index 0000000..2f2e7ca --- /dev/null +++ b/client/components/Search/SelectGenreDialog/SelectGenreDialog.vue @@ -0,0 +1,172 @@ + + + + + \ No newline at end of file diff --git a/client/quasar.js b/client/quasar.js index a2a3243..202a8d2 100644 --- a/client/quasar.js +++ b/client/quasar.js @@ -31,7 +31,7 @@ import {QSelect} from 'quasar/src/components/select'; //import {QPopupProxy} from 'quasar/src/components/popup-proxy'; import {QDialog} from 'quasar/src/components/dialog'; //import {QChip} from 'quasar/src/components/chip'; -//import {QTree} from 'quasar/src/components/tree'; +import {QTree} from 'quasar/src/components/tree'; //import {QVirtualScroll} from 'quasar/src/components/virtual-scroll'; //import {QExpansionItem} from 'quasar/src/components/expansion-item'; @@ -63,7 +63,7 @@ const components = { //QPopupProxy, QDialog, //QChip, - //QTree, + QTree, //QExpansionItem, //QVirtualScroll, }; diff --git a/server/controllers/WebSocketController.js b/server/controllers/WebSocketController.js index af5d496..43e7d5b 100644 --- a/server/controllers/WebSocketController.js +++ b/server/controllers/WebSocketController.js @@ -70,6 +70,8 @@ class WebSocketController { await this.search(req, ws); break; case 'get-book-list': await this.getBookList(req, ws); break; + case 'get-genre-tree': + await this.getGenreTree(req, ws); break; default: throw new Error(`Action not found: ${req.action}`); @@ -133,6 +135,12 @@ class WebSocketController { this.send(result, req, ws); } + + async getGenreTree(req, ws) { + const result = await this.webWorker.getGenreTree(); + + this.send(result, req, ws); + } } module.exports = WebSocketController; diff --git a/server/core/DbCreator.js b/server/core/DbCreator.js index ec736c6..eb154ca 100644 --- a/server/core/DbCreator.js +++ b/server/core/DbCreator.js @@ -275,7 +275,10 @@ class DbCreator { let genre = rec.genre || emptyFieldValue; genre = rec.genre.split(','); - for (const g of genre) { + for (let g of genre) { + if (!g) + g = emptyFieldValue; + let genreRec; if (genreMap.has(g)) { const genreId = genreMap.get(g); diff --git a/server/core/WebWorker.js b/server/core/WebWorker.js index 46e45c7..d52b89e 100644 --- a/server/core/WebWorker.js +++ b/server/core/WebWorker.js @@ -1,5 +1,6 @@ const os = require('os'); const fs = require('fs-extra'); +const _ = require('lodash'); const WorkerState = require('./WorkerState'); const { JembaDbThread } = require('jembadb'); @@ -9,6 +10,7 @@ const DbSearcher = require('./DbSearcher'); const ayncExit = new (require('./AsyncExit'))(); const log = new (require('./AppLogger'))().log;//singleton const utils = require('./utils'); +const genreTree = require('./genres'); //server states const ssNormal = 'normal'; @@ -203,6 +205,43 @@ class WebWorker { return await this.dbSearcher.getBookList(authorId); } + async getGenreTree() { + this.checkMyState(); + + const config = await this.dbConfig(); + + let result; + const db = this.db; + if (!db.wwCache.genres) { + const genres = _.cloneDeep(genreTree); + const last = genres[genres.length - 1]; + + const genreValues = new Set(); + for (const section of genres) { + for (const g of section.value) + genreValues.add(g.value); + } + + //добавим к жанрам те, что нашлись при парсинге + const rows = await db.select({table: 'genre', map: `(r) => ({value: r.value})`}); + for (const row of rows) { + if (!genreValues.has(row.value)) + last.value.push({name: row.value, value: row.value}); + } + + result = { + genreTree: genres, + inpxHash: (config.inpxHash ? config.inpxHash : ''), + }; + + db.wwCache.genres = result; + } else { + result = db.wwCache.genres; + } + + return result; + } + async logServerStats() { while (1) {// eslint-disable-line try { diff --git a/server/core/Genres/genres.json b/server/core/genres/genresText.js similarity index 98% rename from server/core/Genres/genres.json rename to server/core/genres/genresText.js index f0f5d1a..2ef4488 100644 --- a/server/core/Genres/genres.json +++ b/server/core/genres/genresText.js @@ -1,4 +1,4 @@ -" +module.exports = ` #---------- Список жанров fb2 ---------- 0.1 Фантастика 0.2 Детективы и Триллеры @@ -276,4 +276,4 @@ 0.22.251 tbg_secondary;Учебники и пособия для среднего и специального образования 0.22.252 tbg_higher;Учебники и пособия ВУЗов #---------- 2021-07-22 11:35:50.469539---------- -" \ No newline at end of file +`; \ No newline at end of file diff --git a/server/core/genres/index.js b/server/core/genres/index.js new file mode 100644 index 0000000..22f440a --- /dev/null +++ b/server/core/genres/index.js @@ -0,0 +1,53 @@ +const genresText = require('./genresText.js'); +const genres = []; + +const sec2index = {}; +const lines = genresText.split('\n').map(l => l.trim()); + +let index = 0; +let other;//прочее в конец + +for (const line of lines) { + if (!line || line[0] == '#') + continue; + + const p = line.indexOf(' '); + const num = line.substring(0, p).trim().split('.'); + if (num.length < 2) + continue; + + const section = `${num[0]}.${num[1]}`; + if (section == '0.0') + continue; + + let name = line.substring(p + 1).trim(); + + if (num.length < 3) {//раздел + if (section == '0.20') {//прочее + other = {name, value: []}; + } else { + if (sec2index[section] === undefined) { + if (!genres[index]) + genres[index] = {name, value: []}; + sec2index[section] = index; + index++; + } + } + } else {//подраздел + const n = name.split(';').map(l => l.trim()); + + if (section == '0.20') {//прочее + other.value.push({name: n[1], value: n[0]}); + } else { + const i = sec2index[section]; + if (i !== undefined) { + genres[i].value.push({name: n[1], value: n[0]}); + } + } + } +} + +if (other) + genres.push(other); + +module.exports = genres; \ No newline at end of file