From: OHASHI, Norikazu Date: Mon, 29 Apr 2019 12:52:06 +0000 (+0900) Subject: 書籍情報、蔵書情報アクセス用モデル作成 X-Git-Url: http://www.wald-der-katze.sakura.ne.jp/git/gitweb.cgi?a=commitdiff_plain;h=15a43e4916d3df32faac5c70023c5260a5f4ba7f;p=book_server.git 書籍情報、蔵書情報アクセス用モデル作成 --- diff --git a/create_table.sql b/create_table.sql index d0c60f5..8a5df9a 100644 --- a/create_table.sql +++ b/create_table.sql @@ -16,6 +16,7 @@ CREATE TABLE books ( series VARCHAR(255), author VARCHAR(127), orignal_author VARCHAR(127), + illustrator VARCHAR(127), translator VARCHAR(127), supervisor VARCHAR(127), publisher VARCHAR(127), @@ -37,6 +38,6 @@ CREATE TABLE book_covers ( key_hash VARCHAR(60) NOT NULL PRIMARY KEY, isbn VARCHAR(14) NOT NULL UNIQUE, mime_type VARCHAR(255) NOT NULL, - shadow LONGBLOB NOT NULL, + cover LONGBLOB NOT NULL, create_at DATETIME NOT NULL, update_at DATETIME NOT NULL); diff --git a/readme.md b/readme.md index 6c32561..224b1ea 100644 --- a/readme.md +++ b/readme.md @@ -62,6 +62,7 @@ | 4 | series | VARCHAR(255) | | シリーズ名 | | 5 | author | VARCHAR(127) | | 著者名 | | 6 | orignal_author | VARCHAR(127) | | 原著者 | +| 7 | illustrator | VARCHAR(127) | | 作画者 | | 7 | translator | VARCHAR(127) | | 翻訳者 | | 8 | supervisor | VARCHAR(127) | | 監修者 | | 9 | publisher | VARCHAR(127) | | 出版社 | @@ -88,7 +89,7 @@ | 1 | key_hash | VARCHAR(60) | NOT NULL, PRIMARY KEY | SHA125 HASH (アクセスキー) | | 2 | isbn | VARCHAR(14) | NOT NULL, UNIQUE | 対象ISBN | | 3 | mime_type | VARCHAR(255) | NOT NULL | 画像のMIME TYPE | -| 4 | shadow | LONGBLOB | NOT NULL | 画像のデータ | +| 4 | cover | LONGBLOB | NOT NULL | 画像のデータ | | 5 | creat_at | DATETIME | NOT NULL | 登録日時 | | 6 | update_at | DATETIME | NOT NULL | 更新日時 | diff --git a/sinatra/app/controllers/web_gui.rb b/sinatra/app/controllers/web_gui.rb index 3f52c76..e46c8f1 100644 --- a/sinatra/app/controllers/web_gui.rb +++ b/sinatra/app/controllers/web_gui.rb @@ -165,7 +165,10 @@ class WebGui < Sinatra::Base raise StandardError, "アカウント名とIDが不正" end end - UserAccount.changeUser(id, passwd: new_pass, full_name: full_name, email: email) + params = {passwd: new_pass, + full_name: full_name, + email: email} + UserAccount.updateUser(id, params) redirect "/user_home" rescue UserAccount::NotFoundInstanceError, UserAccount::AuthenticationError diff --git a/sinatra/app/models/books_db.rb b/sinatra/app/models/books_db.rb new file mode 100644 index 0000000..18af0f2 --- /dev/null +++ b/sinatra/app/models/books_db.rb @@ -0,0 +1,521 @@ +# coding: utf-8 +# 書籍系DBアクセス処理 +# @author OHASHI, Norikazu + +require 'active_record' +require 'mysql2' +require 'httpclient' +require 'json' +require 'digest/sha2' + +# DB設定ファイルの読み込み +db_config = File.join(File.dirname(__FILE__), 'database.yml') +ActiveRecord::Base.configurations = YAML.load_file(db_config) +ActiveRecord::Base.establish_connection(:development) + +#書籍情報 +class Book < ActiveRecord::Base + # 正規表現によるレコード探索 + # @param [Symbol] key カラム名 + # @param [String] pattern 正規表現パターン + # @return [Array] 対象となるユーザ一覧 + def self.with_regexp(key, pattern) + column = columns_hash[key.to_s].name + where("`#{table_name}`.`#{column}` REGEXP ?", pattern) + end +end + +#蔵書情報 +class BookCollection < ActiveRecord::Base + # 正規表現によるレコード探索 + # @param [Symbol] key カラム名 + # @param [String] pattern 正規表現パターン + # @return [Array] 対象となるユーザ一覧 + def self.with_regexp(key, pattern) + column = columns_hash[key.to_s].name + where("`#{table_name}`.`#{column}` REGEXP ?", pattern) + end +end + +#書影情報 +class BookCover < ActiveRecord::Base + # 正規表現によるレコード探索 + # @param [Symbol] key カラム名 + # @param [String] pattern 正規表現パターン + # @return [Array] 対象となるユーザ一覧 + def self.with_regexp(key, pattern) + column = columns_hash[key.to_s].name + where("`#{table_name}`.`#{column}` REGEXP ?", pattern) + end +end + +# ユーザ管理 +class BookManager + + # 対象の書籍情報がすでに存在している + class AlreadyInstanceError < StandardError + end + + # 対象の書籍情報が存在していない + class NotFoundInstanceError < StandardError + end + + # DBアクセス失敗 + class DbAccessError < StandardError + end + + # データ取得に失敗 + class FailedGetInstance < StandardError + end + # 定数定義 + + # 探索対象 + # 書籍情報から取得 + SEARCH_ON_BOOKS = 1 + + # openBDから取得 + SEARCH_ON_OPENBD = 2 + + # ISBNの文字列から "-" を削除 + # @param [String] isbn 処理対象のISBN + # @return [String] 変換後のISBN + def self.toIsbn(isbn) + if isbn.kind_of?(String) + return isbn.gsub(/-/, "") + end + return isbn + end + + # 蔵書テーブルから ISBN の蔵書情報を削除 + # @param [String] isbn 処理対象のISBN + def self.deleteBookOfCollection(isbn) + begin + colections = BookCollection.where(isbn: isbn) + BookCollection.transcation do + colections.each do |item| + item.destroy! + end + end + rescue + raise DbAccessError + end + end + + + # ISBNから書籍情報検索してハッシュで返却 + # @param [String] isbn + # @return [Hash] 書籍情報 + def self.getHashOfBooks(book) + book = Book.find_by(isbn: isbn) + if (book == nil) + return nil + end + book_hash = { + search_type: SEARCH_ON_BOOKS, + isbn: book.isbn, + title: book.title, + volume: book.volume, + series: book.series, + author: book.author, + orignal_author: book.orignal_author, + illustrator: book.illustrator, + translator: book.translator, + supervisor: book.supervisor, + publisher: book.publisher, + pubdate: book.pubdate, + cover_uri: book.cover + } + return book_hash + end + + # openBDの著者情報から各関係者ハッシュを作成 + # @param [String] parsons 著者情報 + # @return [Hash] 関係者ハッシュ + def self.getParsons(parsons) + parsons_hash = {} + parsons.split(" ").each do |item| + name, jobs = item.split("/") + case jobs + when "著", "著者", "作者", "作" then + key = :author + when "イラスト", "画", "作画" then + key = :illustrator + when "翻訳", "訳" then + key = :translator + when "原著", "原" then + key = :orignal_author + when "監修", "監" then + key = :supervisor + else + key = :author + end + if ( parsons_hash[key] == nil || parsons_hash[key] = "") + parsons_hash[key] = name + else + parsons_hash[key] += ", " + name + end + end + return parsons_hash + end + + # ISBNからopenBDを検索してハッシュで返却 + # @param [String] isbn + # @return [Hash] 書籍情報 + def self.getHashOfOpenBD(book) + client = HTTPClient.new + res = client.get("http://api.openbd.jp/v1/get", isbn: isbn) + books = JSON.perse(res.body) + + if (books.size > 1) + raise FailedGetInstance + end + if (books.size < 1) + return nil + end + book = books[0]["summary"] + parsons = getParsons(book["author"]) + book_hash = { + search_type: SEARCH_ON_OPENBD, + isbn: book["isbn"], + title: book["title"], + volume: book["volume"], + series: book["series"], + author: parsons[:author], + orignal_author: parsons[:orignal_author], + translator: parsons[:translator], + supervisor: parsons[:supervisor], + illustrator: book[:illustrator], + publisher: book["publisher"], + pubdate: book["pubdate"], + cover_uri: book["cover"] + } + return book_hash + end + + # 各ユーザ用の書籍情報を作成 + # @param [Book] book 書籍情報 + # @param [BookCollection] book_collect 蔵書情報 + # @return [Hash] 書籍情報 + def createBookHash(book, book_collect) + { + isbn: book[:isbn], + title: book[:title], + volume: book[:volume], + series: book[:series], + author: book[:author], + orignal_author: book[:orignal_author], + translator: book[:translator], + supervisor: book[:supervisor], + illustrator: book[:illustrator], + publisher: book[:publisher], + pubdate: book[:pubdate], + cover_uri: book[:cover], + summary: book_collect[:summary], + rank: book_collect[:rank] + } + end + + private_class_method :toIsbn, :deleteBookOfCollection, + :getHashOfBooks, :getParsons, :getHashOfOpenBD, + :createBookHash + + # 書影情報登録 + # @param [String] isbn 画像を登録するISBN + # @param [Hash] cover_image 書影の画像データとフォーマット + + def self.addBookCover(isbn, cover_image) + cover = cover_image[:data] + mime_type = cover_image[:mime_type] + key_hash = Digest::SHA256.hexdigest(cover) + + if (BookCover.find_by(isbn: isbn) != nil) + raise AlreadyInstanceError + end + book_cover = BookCover.new + book_cover.key_hash = key_hash + book_cover.isbn = isbn + book_cover.mime_type = mime_type + book_cover.cover = cover + book_cover.create_at = DateTime.now + book_cover.update_at = DateTime.now + if (not book_cover.save) + raise DbAccessError + end + return key_hash + + end + + # 書影イメージを取得 + # @param [String] key_hash 書影イメージのハッシュ + # @return [BookCover] 書影情報 + def self.getBookCover(key_hash) + book_cover = BookCover.find_by(key_hash: key_hash) + if (book_cover) + raise NotFoundInstanceError + end + return book_cover + end + + # 書影イメージの削除 + # @param [String] isbn_str 削除対象のISBN + def self.deleteBookCover(isbn_str) + isbn = toIsbn(isbn_str) + book_cover = BookCover.find_by(isbn: isbn) + if (book_cover) + raise NotFoundInstanceError + end + if (not book_cover.destroy) + raise DbAccessError + end + end + + # 書籍探索(ISBN) + # @param [String] isbn_str 探索対象のISBN + # @return [Hash] 書籍情報 + def self.searchISBN(isbn_str, user_id) + isbn = toIsbn(isbn_str) + collection = BookCollection.find_by(isbn: isbn, user_id: user_id) + if (collection != nil) + #ユーザが保持しているのですでにあることを例外で通知 + raise AlreadyInstanceError + end + book_info = getHashOfBooks(isbn) + if (book_info != nil) + book_info = getHashOfOpenBD(isbn) + end + return book_info + end + + # 書籍情報の登録 + # @param [Hash] 書籍情報のハッシュ + def self.createBook(book_info) + isbn = book_info[:isbn] + if (Book.find_by(isbn: isbn) != nil) + raise AlreadyInstanceError + end + book = Book.new + book_info.each do |key, value| + if (key == :cover_image) + # 書影のイメージはここでは設定しない + next + end + if (key == :cover_uri) + # 書影にはURIのキーを登録 + book.cover = value + else + # その他のステータスはそのまま登録 + method = key.to_s + "=" + book.send(method, value) + end + end + + # 登録日時の設定 + book.create_at = DateTime.now + book.update_at = DateTime.now + if (not book.save) + raise DbAccessError + end + end + + # 書籍情報取得 + # @param [String] isbn_str 取得対象のISBN + # @return [Book] + def self.getBook(isbn_str) + isbn = toIsbn(isbn_str) + + book = Book.find_by(isbn) + if (book == nil) + # 対象の書籍情報がなかった。 + raise NotFoundInstanceError + end + return book + end + + # 書籍情報の変更 + # @param [String] isbn_str + # @param [Hash] columns 変更するカラムと値のハッシュ + def self.updateBook(id, columns) + isbn = toIsbn(isbn_str) + book = Book.find_by(isbn: isbn) + change_f = false + if (book == nil) + raise NotFoundInstanceError + end + + columns.each do |key, value| + if ((value == nil) || (value == "")) + # 値がない、値が空文字の場合は対象としない。 + next + end + if (key == :cover_uri) + method = "cover=" + else + method = key.to_s + "=" + end + if (value != book.send(method.chop)) + book.send(method, value) + change_f = true + end + end + + # 更新内容の適用 + if (change_f) + book.update_at = DateTime.now + if (book.save) + raise DbAccessError + end + end + end + + # + # 書籍情報検索 + # @param [Hash] find_keys 検索キー情報 + # @return [Array] 検索結果 + def self.findBook(find_keys) + + books = Book.all + find_keys.each do |key, value| + if (books == nil) + break + end + if (value =~ /^\/(.+)\/$/) + books = books.with_regexp(key, value) + else + books = books.where(key => value) + end + end + return books + end + + # 書籍情報の削除 + # @param [String] isbn_str 削除対象のISBN + def self.deleteBook(isbn_str) + isbn = toIsbn(isbn_str) + book = Book.find_by(isbn: isbn) + if (book == nil) + # 対象の書籍情報がなかった。 + raise NotFoundInstanceError + end + + # 蔵書情報から該当する書籍を削除 + deleteBookOfCollection(isbn) + + # 書影情報を削除 + cover = BookCover.find_by(isbn: isbn) + if (cover != nil) + if (not cover.destroy) + raise DbAccessError + end + end + + # 書籍情報を削除 + if (not book.destroy) + raise DbAccessError + end + end + + # ユーザでの絞り込み + # @param [Integer] user_id ユーザID + # @param [Arrray] books 絞り込み対象の書籍情報一覧 + # @return [Array] 絞り込んだ書籍情報 + def narrowBookOfId(user_id, books) + narrow_books=Array.new + books.each do |book| + book_collect = BookCollection.find_by(user_id: user_id, isbn: book.isbn) + if (book_collect != nil) + narrow_book = createBookHash(book, book_collect) + narrow_books.push(narrow_book) + end + end + return narrow_books + end + + #蔵書情報の取得 + # @param [String] isbn_str 取得対象のISBN + # @param [Integer] user_id 取得対象のユーザID + # @param [Arrray] books 絞り込み対象の書籍情報一覧 + # @return [Array] 絞り込んだ書籍情報 + def getBookCollect(isbn_str, user_id) + isbn = toIsbn(isbn_str) + book = Book.find_by(isbn: isbn) + if (book == nil) + raise NotFoundInstanceError + end + book_collect = BookCollection.find_by(isbn: isbn, user_id: user_id) + if (book_collect == nil) + raise NotFoundInstanceError + end + return createBookHash(book, book_collect) + end + + #蔵書情報を登録 + #@param [String] isbn 登録するISBN + #@param [Integer] user_id 登録対象のユーザID + #@param [String] summary ユーザ毎の紹介分 + #@param [Integer] rank ユーザの評価 + #@return [BookCollection] 蔵書情報 + def createBookCollect(isbn, user_id, summary, rank) + if (BookCollection.find_by(isbn: isbn, user_id: user_id) != nil) + raise NotFoundInstanceError + end + book_collect = BookCollection.new + book_collect.isbn = isbn + book_collect.user_id = user_id + book_collect.summary = summary + book_collect.rank = rank + book_collect.create_at = DateTime.now + book_collect.update_at = DateTime.now + if (not book_collect.save) + raise DbAccessError + end + end + + # 蔵書情報の変更 + # @param [String] isbn_str 変更対象のISBN + # @param [Integer] user_id 変更対象のユーザID + # @param [Hash] columns 変更するカラムと値のハッシュ + def self.updateBookCollect(isbn_str, user_id, columns) + isbn = toIsbn(isbn_str) + book_collect = BookCollection.find_by(isbn: isbn, user_id: user_id ) + change_f = false + if (book_collect == nil) + raise NotFoundInstanceError + end + + columns.each do |key, value| + if ((value == nil) || (value == "")) + # 値がない、値が空文字の場合は対象としない。 + next + end + if (value != book_collect.send(key)) + # 値が異なれば更新 + method = key.to_s + "=" + book_collect.send(method, value) + change_f = true + end + end + + # 更新内容の適用 + if (change_f) + user.update_at = DateTime.now + if (user.save) + raise DbAccessError + end + end + end + + # 蔵書情報の削除 + # @param [String] isbn_str 削除対象のISBN + # @param [Integer] user_id 削除対象のユーザID + def self.deleteUser(isbn_str, user_id) + isbn = toIsbn(isbn_str) + book_collect = BookCollection.find_by(isbn: isbn, user_id: user_id ) + if (book_collect == nil) + raise NotFoundInstanceError + end + if (not book_collect.destroy) + raise DbAccessError + end + end + +end diff --git a/sinatra/app/models/users_db.rb b/sinatra/app/models/users_db.rb index 0b7a80c..763a518 100644 --- a/sinatra/app/models/users_db.rb +++ b/sinatra/app/models/users_db.rb @@ -109,8 +109,9 @@ class UserAccount user.user_role = User::ROLE_NORMAL user.create_at = DateTime.now user.update_at = DateTime.now - user.save - + if (not user.save) + raise DbAccessError + end return user.id end @@ -166,8 +167,7 @@ class UserAccount # ユーザ情報の変更 # @param [Integer] id 変更対象のユーザID # @param [Hash] columns 変更するカラムと値のハッシュ - - def self.changeUser(id, columns) + def self.updateUser(id, columns) user = User.find_by(user_id: id) change_f = false if (user == nil) @@ -196,11 +196,14 @@ class UserAccount # 更新内容の適用 if (change_f) user.update_at = DateTime.now - user.save + if (user.save) + raise DbAccessError + end end end # ユーザ情報の削除 + # @param [Integer] id 削除対象のユーザID def self.deleteUser(id) user = User.find_by(id: id) if (user == nil) diff --git a/sinatra/app/views/scss/style.scss b/sinatra/app/views/scss/style.scss index 54c9e4f..8b1baad 100644 --- a/sinatra/app/views/scss/style.scss +++ b/sinatra/app/views/scss/style.scss @@ -69,6 +69,7 @@ input.push_button{ text-align: center; height: 32em; padding: 5px 0px; + overflow: auto; } #foot {