書籍情報、蔵書情報アクセス用モデル作成
authorOHASHI, Norikazu <katz@neko-mori.sakura.ne.jp>
Mon, 29 Apr 2019 12:52:06 +0000 (21:52 +0900)
committerOHASHI, Norikazu <katz@neko-mori.sakura.ne.jp>
Mon, 29 Apr 2019 12:52:06 +0000 (21:52 +0900)
create_table.sql
readme.md
sinatra/app/controllers/web_gui.rb
sinatra/app/models/books_db.rb [new file with mode: 0644]
sinatra/app/models/users_db.rb
sinatra/app/views/scss/style.scss

index d0c60f5..8a5df9a 100644 (file)
@@ -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);
index 6c32561..224b1ea 100644 (file)
--- 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 | 更新日時 |
 
index 3f52c76..e46c8f1 100644 (file)
@@ -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 (file)
index 0000000..18af0f2
--- /dev/null
@@ -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<User>] 対象となるユーザ一覧
+  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<User>] 対象となるユーザ一覧
+  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<User>] 対象となるユーザ一覧
+  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<Book>]  検索結果
+  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<Book>] books 絞り込み対象の書籍情報一覧
+  # @return [Array<Hash>] 絞り込んだ書籍情報
+  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<Book>] books 絞り込み対象の書籍情報一覧
+  # @return [Array<Hash>] 絞り込んだ書籍情報
+  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
index 0b7a80c..763a518 100644 (file)
@@ -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)
index 54c9e4f..8b1baad 100644 (file)
@@ -69,6 +69,7 @@ input.push_button{
     text-align: center;
     height: 32em;
     padding: 5px 0px;
+    overflow: auto;
 }
 
 #foot {