mirror of
https://github.com/th3r00t/pyShelf.git
synced 2026-04-28 01:59:35 -04:00
Refactoring collections handler
This commit is contained in:
42
docs/Automated Collections.html
Normal file
42
docs/Automated Collections.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="Stylesheet" type="text/css" href="style.css">
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="rss.xml">
|
||||
<title>Automated Collections</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="Automated Collection Management System"><h1 id="Automated Collection Management System" class="header"><a href="#Automated Collection Management System">Automated Collection Management System</a></h1></div>
|
||||
<p>
|
||||
The collection management system needs rewritten from the ground up.
|
||||
The model should look something like this.
|
||||
</p>
|
||||
<ul>
|
||||
<li class="done0">
|
||||
<span class="todo">TODO</span> :: Refactor collections algorithm. #8f46262c
|
||||
<pre python>
|
||||
Collection {
|
||||
id: int
|
||||
name: String
|
||||
books: list(Book)
|
||||
}
|
||||
</pre>
|
||||
|
||||
</ul>
|
||||
<p>
|
||||
it is likely the book object will need rewritten as well.
|
||||
</p>
|
||||
<pre python>
|
||||
Book {
|
||||
id: int
|
||||
title: String
|
||||
# other book metadata
|
||||
collection: list(Collection.id)
|
||||
}
|
||||
</pre>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -29,25 +29,25 @@ sub-folders in your collection.
|
||||
<div id="Book Scraping System-Scrapers"><h2 id="Scrapers" class="header"><a href="#Book Scraping System-Scrapers">Scrapers</a></h2></div>
|
||||
<ul>
|
||||
<li class="done4">
|
||||
epub #75f00edf
|
||||
epub #2899a8e9
|
||||
|
||||
<li class="done4">
|
||||
mobi #2fe4b161
|
||||
mobi #ec035720
|
||||
|
||||
<li class="done4">
|
||||
pdf #ffa7e7f0
|
||||
pdf #05875e64
|
||||
|
||||
<li class="done0">
|
||||
cbz #64b5da95
|
||||
cbz #4a513e39
|
||||
|
||||
</ul>
|
||||
<div id="Book Scraping System-Collections"><h2 id="Collections" class="header"><a href="#Book Scraping System-Collections">Collections</a></h2></div>
|
||||
<ul>
|
||||
<li class="done0">
|
||||
Manual Collections #2e7e6fcf
|
||||
Manual Collections #b07156f4
|
||||
|
||||
<li class="done4">
|
||||
Automated Collections #81db675a
|
||||
<li class="done0">
|
||||
<a href="Automated Collections.html">Automated Collections</a> #f258c1f8
|
||||
|
||||
</ul>
|
||||
<div id="Book Scraping System-State"><h2 id="State" class="header"><a href="#Book Scraping System-State">State</a></h2></div>
|
||||
|
||||
113
docs/Client.html
Normal file
113
docs/Client.html
Normal file
@@ -0,0 +1,113 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="Stylesheet" type="text/css" href="style.css">
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="rss.xml">
|
||||
<title>Client</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>
|
||||
<a href="index.html">Home</a> <a href="TOC.html">TOC</a> <a href="https://github.com/th3r00t/pyShelf.git">github</a>
|
||||
</p>
|
||||
<div id="Client"><h1 id="Client" class="header"><a href="#Client">Client</a></h1></div>
|
||||
<p>
|
||||
<span id="Client-frontend"></span><span class="tag" id="frontend">frontend</span> <span id="Client-ui"></span><span class="tag" id="ui">ui</span>
|
||||
</p>
|
||||
|
||||
<div id="Client-Dependency Management"><h2 id="Dependency Management" class="header"><a href="#Client-Dependency Management">Dependency Management</a></h2></div>
|
||||
<p>
|
||||
<span id="Client-Dependency Management-dependency"></span><span class="tag" id="dependency">dependency</span>
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li class="done4">
|
||||
npm #c5f8d347
|
||||
|
||||
<li class="done4">
|
||||
sass #8f43ab4c
|
||||
|
||||
<li class="done0">
|
||||
pip #14ee256e
|
||||
|
||||
</ul>
|
||||
<div id="Client-[[https://create.t3.gg|T3.gg]]"><h2 id="[[https://create.t3.gg|T3.gg]]" class="header"><a href="#Client-[[https://create.t3.gg|T3.gg]]"><a href="https://create.t3.gg">T3.gg</a></a></h2></div>
|
||||
<div id="Client-[[https://create.t3.gg|T3.gg]]-User System"><h3 id="User System" class="header"><a href="#Client-[[https://create.t3.gg|T3.gg]]-User System">User System</a></h3></div>
|
||||
<ul>
|
||||
<li>
|
||||
NextAuthjs
|
||||
|
||||
<li>
|
||||
Authentication Providers
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
Discord
|
||||
Required: Client ID, Client Secret
|
||||
|
||||
<li>
|
||||
Github
|
||||
Required: Client ID, Client Secret, only one callback per app.
|
||||
<img src="https://next-auth.js.org/providers/github" alt="NextAuthjs Github" />
|
||||
|
||||
</ul>
|
||||
</ul>
|
||||
<div id="Client-[[https://create.t3.gg|T3.gg]]-User Experience"><h3 id="User Experience" class="header"><a href="#Client-[[https://create.t3.gg|T3.gg]]-User Experience">User Experience</a></h3></div>
|
||||
|
||||
|
||||
<p>
|
||||
<span id="Client-[[https://create.t3.gg|T3.gg]]-User Experience-ux"></span><span class="tag" id="ux">ux</span>
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li class="done0">
|
||||
Favorites #0fc67f94
|
||||
|
||||
<li class="done0">
|
||||
Permissions / Roles #9c4f279e
|
||||
|
||||
<li class="done0">
|
||||
User Profile #289e0fe0
|
||||
|
||||
<li class="done0">
|
||||
User Settings #51955b33
|
||||
|
||||
</ul>
|
||||
<div id="Client-[[https://create.t3.gg|T3.gg]]-Data Management"><h3 id="Data Management" class="header"><a href="#Client-[[https://create.t3.gg|T3.gg]]-Data Management">Data Management</a></h3></div>
|
||||
<p>
|
||||
<span id="Client-[[https://create.t3.gg|T3.gg]]-Data Management-data"></span><span class="tag" id="data">data</span>
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li class="done0">
|
||||
Book Management System #92e523d2
|
||||
|
||||
<ul>
|
||||
<li class="done0">
|
||||
add #bda9a0f3
|
||||
|
||||
<li class="done0">
|
||||
remove #4b34e931
|
||||
|
||||
<li class="done0">
|
||||
update #eb959340
|
||||
|
||||
<li class="done0">
|
||||
access level #b00795a4
|
||||
|
||||
<li class="done0">
|
||||
ownership #8c80edc5
|
||||
|
||||
<li class="done0">
|
||||
attach #8acb21a9
|
||||
|
||||
<li class="done0">
|
||||
detach #2e263616
|
||||
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -108,7 +108,7 @@ Filesystem-io
|
||||
<a href="REST API.html">REST API</a> #c7bc51c5
|
||||
|
||||
<li class="done0">
|
||||
<a href="Frontend.html">Frontend</a> #a76c1038
|
||||
<a href="Client.html">Client</a> #c68b0664
|
||||
|
||||
</ul>
|
||||
<div id="pyShelf | project:pyshelf-Development"><h2 id="Development" class="header"><a href="#pyShelf | project:pyshelf-Development">Development</a></h2></div>
|
||||
|
||||
15
src/backend/lib/models.py
vendored
15
src/backend/lib/models.py
vendored
@@ -6,22 +6,20 @@ import datetime
|
||||
|
||||
timestamp = Annotated[
|
||||
datetime.datetime,
|
||||
mapped_column(nullable=False, server_default=func.CURRENT_TIMESTAMP())
|
||||
mapped_column(nullable=False, server_default=func.CURRENT_TIMESTAMP()),
|
||||
]
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
"""Base class for all models."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class Book(Base):
|
||||
"""Book model."""
|
||||
|
||||
__tablename__ = "books"
|
||||
__tablename__ = "Book"
|
||||
|
||||
book_id: Mapped[int] = mapped_column(primary_key=True, nullable=False)
|
||||
id: Mapped[int] = mapped_column(primary_key=True, nullable=False)
|
||||
title: Mapped[str]
|
||||
author: Mapped[Optional[str]]
|
||||
categories: Mapped[Optional[str]]
|
||||
@@ -37,12 +35,11 @@ class Book(Base):
|
||||
publisher: Mapped[Optional[str]]
|
||||
|
||||
|
||||
|
||||
class Collection(Base):
|
||||
"""Collection model."""
|
||||
|
||||
__tablename__ = "collections"
|
||||
__tablename__ = "Collection"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
collection: Mapped[str]
|
||||
book_id: Mapped[int] = mapped_column(ForeignKey(Book.book_id))
|
||||
collection_id: Mapped[int] = mapped_column(primary_key=True)
|
||||
book_id: Mapped[int] = mapped_column(ForeignKey(Book.id))
|
||||
|
||||
76
src/backend/lib/storage.py
vendored
76
src/backend/lib/storage.py
vendored
@@ -1,6 +1,5 @@
|
||||
"""Pyshelf's Main Storage Class."""
|
||||
import re
|
||||
import os
|
||||
from sqlalchemy import create_engine, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -36,8 +35,7 @@ class Storage:
|
||||
self.password = self.config.password
|
||||
self.db_host = self.config.db_host
|
||||
self.db_port = self.config.db_port
|
||||
self.engine = create_engine(self.get_connection_string(),
|
||||
pool_pre_ping=True)
|
||||
self.engine = create_engine(self.get_connection_string(), pool_pre_ping=True)
|
||||
|
||||
def get_connection_string(self):
|
||||
"""Get connection string.
|
||||
@@ -49,7 +47,7 @@ class Storage:
|
||||
str : sqlalchemy Connection String
|
||||
"""
|
||||
if self.config.db_engine == "sqlite":
|
||||
return f"sqlite:////{self.config.root}/pyshelf.db"
|
||||
return f"sqlite:////{self.config.root}/pyshelf.sqlite3"
|
||||
elif self.config.db_engine == "psql":
|
||||
return f"postgresql://{self.user}:{self.password}\
|
||||
@{self.db_host}:{self.db_port}/{self.sql}"
|
||||
@@ -86,6 +84,8 @@ class Storage:
|
||||
cover_image = None
|
||||
if not book[1]:
|
||||
pass
|
||||
# breakpoint()
|
||||
self.parse_collections_from_path(book)
|
||||
_book = Book(
|
||||
title=book[0],
|
||||
author=book[1],
|
||||
@@ -117,6 +117,36 @@ class Storage:
|
||||
session.close()
|
||||
return _result
|
||||
|
||||
def parse_collections_from_path(self, book: dict()) -> list():
|
||||
"""Parse book path's to determine common folder structure.
|
||||
|
||||
Stores collections based on shared paths.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
book : dict()
|
||||
Book object to parse.
|
||||
|
||||
Returns
|
||||
-------
|
||||
collections : list()
|
||||
List of collections.
|
||||
"""
|
||||
collections = []
|
||||
title_regx = re.compile(r"^[0-9][0-9]*|-|\ \B")
|
||||
_pathing = book[3].split(self.config.book_path + "/")[1].split("/")
|
||||
try:
|
||||
_pathing.pop(0)
|
||||
_pathing.pop(-1)
|
||||
except IndexError:
|
||||
pass
|
||||
for _p in _pathing:
|
||||
_s = _p.replace("'", "")
|
||||
_x = re.sub(title_regx, "", _s)
|
||||
_s = _x.strip()
|
||||
collections.append(_s)
|
||||
return collections
|
||||
|
||||
def make_collections(self):
|
||||
"""Parse book path's to determine common folder structure.
|
||||
|
||||
@@ -126,7 +156,7 @@ class Storage:
|
||||
self.config.logger.info("Making collections.")
|
||||
_title_regx = re.compile(r"^[0-9][0-9]*|-|\ \B")
|
||||
session = Session(self.engine)
|
||||
_set = session.execute(select(Book.book_id, Book.file_name)).all()
|
||||
_set = session.execute(select(Book.id, Book.file_name)).all()
|
||||
if _set.__len__() > 0:
|
||||
for book in _set:
|
||||
path = self.config.book_path + "/"
|
||||
@@ -143,25 +173,22 @@ class Storage:
|
||||
_s = _x.strip()
|
||||
_sess = Session(self.engine)
|
||||
_q = _sess.execute(
|
||||
select(Collection.collection_id).where(
|
||||
select(Collection.id).where(
|
||||
Collection.collection == _s,
|
||||
Collection.book_id == book.book_id,
|
||||
Collection.book_id == book.id,
|
||||
)
|
||||
)
|
||||
_sess.close()
|
||||
if _q.fetchone() is None:
|
||||
_collection = Collection(
|
||||
collection=_s, book_id=book.book_id)
|
||||
_collection = Collection(collection=_s, book_id=book.id)
|
||||
with Session(self.engine) as _sess:
|
||||
try:
|
||||
_sess.add(_collection)
|
||||
_sess.commit()
|
||||
_sess.close()
|
||||
self.config.logger.info(
|
||||
f"Collection {_s} added.")
|
||||
self.config.logger.info(f"Collection {_s} added.")
|
||||
except Exception as e:
|
||||
self.config.logger.error(
|
||||
f"Collection {_s} failed: {e}")
|
||||
self.config.logger.error(f"Collection {_s} failed: {e}")
|
||||
_collections.append(_p)
|
||||
self.config.logger.info("Finished making collections.")
|
||||
|
||||
@@ -180,21 +207,23 @@ class Storage:
|
||||
session = Session(self.engine)
|
||||
if collection:
|
||||
_result = session.execute(
|
||||
select(Book).join(Collection)
|
||||
.where(Collection.collection_id == collection)
|
||||
.offset(skip).limit(limit)).all()
|
||||
select(Book)
|
||||
.join(Collection)
|
||||
.where(Collection.id == collection)
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
).all()
|
||||
else:
|
||||
_result = session.execute(
|
||||
select(Book).offset(skip).limit(limit)).all()
|
||||
_result = session.execute(select(Book).offset(skip).limit(limit)).all()
|
||||
session.close()
|
||||
return _result
|
||||
|
||||
def get_book(self, book_id):
|
||||
def get_book(self, id):
|
||||
"""Get book from database.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
book_id : int
|
||||
id : int
|
||||
Book ID to filter by.
|
||||
|
||||
Returns
|
||||
@@ -202,7 +231,7 @@ class Storage:
|
||||
_result : ScalarResult Object
|
||||
"""
|
||||
session = Session(self.engine)
|
||||
_result = session.execute(select(Book).where(Book.book_id == book_id)).first()
|
||||
_result = session.execute(select(Book).where(Book.id == id)).first()
|
||||
session.close()
|
||||
return _result
|
||||
|
||||
@@ -214,9 +243,6 @@ class Storage:
|
||||
_result : ScalarResult Object
|
||||
"""
|
||||
session = Session(self.engine)
|
||||
_result = session.execute(
|
||||
select(Collection)
|
||||
.join(Book)
|
||||
).all()
|
||||
_result = session.execute(select(Collection).join(Book)).all()
|
||||
session.close()
|
||||
return _result
|
||||
|
||||
21
wiki/Automated Collections.wiki
vendored
Normal file
21
wiki/Automated Collections.wiki
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
= Automated Collection Management System =
|
||||
The collection management system needs rewritten from the ground up.
|
||||
The model should look something like this.
|
||||
* [ ] TODO :: Refactor collections algorithm. #8f46262c
|
||||
{{{python
|
||||
Collection {
|
||||
id: int
|
||||
name: String
|
||||
books: list(Book)
|
||||
}
|
||||
}}}
|
||||
|
||||
it is likely the book object will need rewritten as well.
|
||||
{{{python
|
||||
Book {
|
||||
id: int
|
||||
title: String
|
||||
# other book metadata
|
||||
collection: list(Collection.id)
|
||||
}
|
||||
}}}
|
||||
12
wiki/Book Scraping System.wiki
vendored
12
wiki/Book Scraping System.wiki
vendored
@@ -10,14 +10,14 @@ pyShelf features a recursive scraping algorithm that itterates over all
|
||||
sub-folders in your collection.
|
||||
|
||||
== Scrapers ==
|
||||
* [X] epub #75f00edf
|
||||
* [X] mobi #2fe4b161
|
||||
* [X] pdf #ffa7e7f0
|
||||
* [ ] cbz #64b5da95
|
||||
* [X] epub #2899a8e9
|
||||
* [X] mobi #ec035720
|
||||
* [X] pdf #05875e64
|
||||
* [ ] cbz #4a513e39
|
||||
|
||||
== Collections ==
|
||||
* [ ] Manual Collections #2e7e6fcf
|
||||
* [X] Automated Collections #81db675a
|
||||
* [ ] Manual Collections #b07156f4
|
||||
* [ ] [[Automated Collections]] #f258c1f8
|
||||
|
||||
== State ==
|
||||
|
||||
|
||||
41
wiki/Client.wiki
vendored
Normal file
41
wiki/Client.wiki
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
[[index|Home]] [[TOC]] [[https://github.com/th3r00t/pyShelf.git|github]]
|
||||
= Client =
|
||||
:frontend:ui:
|
||||
|
||||
== Dependency Management ==
|
||||
:dependency:
|
||||
|
||||
* [X] npm #c5f8d347
|
||||
* [X] sass #8f43ab4c
|
||||
* [ ] pip #14ee256e
|
||||
|
||||
== [[https://create.t3.gg|T3.gg]] ==
|
||||
=== User System ===
|
||||
- NextAuthjs
|
||||
- Authentication Providers
|
||||
- Discord
|
||||
Required: Client ID, Client Secret
|
||||
- Github
|
||||
Required: Client ID, Client Secret, only one callback per app.
|
||||
{{https://next-auth.js.org/providers/github|NextAuthjs Github}}
|
||||
=== User Experience ===
|
||||
|
||||
|
||||
:ux:
|
||||
|
||||
* [ ] Favorites #0fc67f94
|
||||
* [ ] Permissions / Roles #9c4f279e
|
||||
* [ ] User Profile #289e0fe0
|
||||
* [ ] User Settings #51955b33
|
||||
|
||||
=== Data Management ===
|
||||
:data:
|
||||
|
||||
* [ ] Book Management System #92e523d2
|
||||
* [ ] add #bda9a0f3
|
||||
* [ ] remove #4b34e931
|
||||
* [ ] update #eb959340
|
||||
* [ ] access level #b00795a4
|
||||
* [ ] ownership #8c80edc5
|
||||
* [ ] attach #8acb21a9
|
||||
* [ ] detach #2e263616
|
||||
2
wiki/index.wiki
vendored
2
wiki/index.wiki
vendored
@@ -38,7 +38,7 @@ pyShelf supports the following formats:
|
||||
== TODO ==
|
||||
* [ ] [[Book Scraping System]] #f7edafb1
|
||||
* [ ] [[REST API]] #c7bc51c5
|
||||
* [ ] [[Frontend]] #a76c1038
|
||||
* [ ] [[Client]] #c68b0664
|
||||
|
||||
== Development ==
|
||||
|
||||
|
||||
Reference in New Issue
Block a user