Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
D
douban-api-proxy
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
captainwong
douban-api-proxy
Commits
7bb5afa5
Commit
7bb5afa5
authored
Apr 18, 2020
by
Ozzieisaacs
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'kobo/shelves' into Develop
parents
42ac06c1
41a3623f
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
325 additions
and
103 deletions
+325
-103
kobo.py
cps/kobo.py
+235
-19
opds.py
cps/opds.py
+2
-2
SyncToken.py
cps/services/SyncToken.py
+8
-3
shelf.py
cps/shelf.py
+44
-55
detail.html
cps/templates/detail.html
+0
-22
ub.py
cps/ub.py
+36
-2
No files found.
cps/kobo.py
View file @
7bb5afa5
...
@@ -17,9 +17,11 @@
...
@@ -17,9 +17,11 @@
# You should have received a copy of the GNU General Public License
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import
base64
import
datetime
import
datetime
import
itertools
import
json
import
sys
import
sys
import
base64
import
os
import
os
import
uuid
import
uuid
from
time
import
gmtime
,
strftime
from
time
import
gmtime
,
strftime
...
@@ -45,7 +47,7 @@ from sqlalchemy import func
...
@@ -45,7 +47,7 @@ from sqlalchemy import func
from
sqlalchemy.sql.expression
import
and_
,
or_
from
sqlalchemy.sql.expression
import
and_
,
or_
import
requests
import
requests
from
.
import
config
,
logger
,
kobo_auth
,
db
,
helper
,
ub
from
.
import
config
,
logger
,
kobo_auth
,
db
,
helper
,
shelf
as
shelf_lib
,
ub
from
.services
import
SyncToken
as
SyncToken
from
.services
import
SyncToken
as
SyncToken
from
.web
import
download_required
from
.web
import
download_required
from
.kobo_auth
import
requires_kobo_auth
from
.kobo_auth
import
requires_kobo_auth
...
@@ -120,6 +122,7 @@ def redirect_or_proxy_request():
...
@@ -120,6 +122,7 @@ def redirect_or_proxy_request():
def
convert_to_kobo_timestamp_string
(
timestamp
):
def
convert_to_kobo_timestamp_string
(
timestamp
):
return
timestamp
.
strftime
(
"
%
Y-
%
m-
%
dT
%
H:
%
M:
%
SZ"
)
return
timestamp
.
strftime
(
"
%
Y-
%
m-
%
dT
%
H:
%
M:
%
SZ"
)
@
kobo
.
route
(
"/v1/library/sync"
)
@
kobo
.
route
(
"/v1/library/sync"
)
@
requires_kobo_auth
@
requires_kobo_auth
@
download_required
@
download_required
...
@@ -203,24 +206,23 @@ def HandleSyncRequest():
...
@@ -203,24 +206,23 @@ def HandleSyncRequest():
ub
.
KoboReadingState
.
user_id
==
current_user
.
id
,
ub
.
KoboReadingState
.
user_id
==
current_user
.
id
,
ub
.
KoboReadingState
.
book_id
.
notin_
(
reading_states_in_new_entitlements
))))
ub
.
KoboReadingState
.
book_id
.
notin_
(
reading_states_in_new_entitlements
))))
for
kobo_reading_state
in
changed_reading_states
.
all
():
for
kobo_reading_state
in
changed_reading_states
.
all
():
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
kobo_reading_state
.
book_id
)
.
one
()
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
kobo_reading_state
.
book_id
)
.
one_or_none
()
sync_results
.
append
({
if
book
:
"ChangedReadingState"
:
{
sync_results
.
append
({
"ReadingState"
:
get_kobo_reading_state_response
(
book
,
kobo_reading_state
)
"ChangedReadingState"
:
{
}
"ReadingState"
:
get_kobo_reading_state_response
(
book
,
kobo_reading_state
)
})
}
new_reading_state_last_modified
=
max
(
new_reading_state_last_modified
,
kobo_reading_state
.
last_modified
)
})
new_reading_state_last_modified
=
max
(
new_reading_state_last_modified
,
kobo_reading_state
.
last_modified
)
sync_shelves
(
sync_token
,
sync_results
)
sync_token
.
books_last_created
=
new_books_last_created
sync_token
.
books_last_created
=
new_books_last_created
sync_token
.
books_last_modified
=
new_books_last_modified
sync_token
.
books_last_modified
=
new_books_last_modified
sync_token
.
archive_last_modified
=
new_archived_last_modified
sync_token
.
archive_last_modified
=
new_archived_last_modified
sync_token
.
reading_state_last_modified
=
new_reading_state_last_modified
sync_token
.
reading_state_last_modified
=
new_reading_state_last_modified
if
config
.
config_kobo_proxy
:
return
generate_sync_response
(
request
,
sync_token
,
sync_results
)
return
generate_sync_response
(
request
,
sync_token
,
sync_results
)
return
make_response
(
jsonify
(
sync_results
))
# Missing feature: Detect server-side book deletions.
def
generate_sync_response
(
request
,
sync_token
,
sync_results
):
def
generate_sync_response
(
request
,
sync_token
,
sync_results
):
...
@@ -392,6 +394,222 @@ def get_metadata(book):
...
@@ -392,6 +394,222 @@ def get_metadata(book):
return
metadata
return
metadata
@
kobo
.
route
(
"/v1/library/tags"
,
methods
=
[
"POST"
])
@
login_required
# Creates a Shelf with the given items, and returns the shelf's uuid.
def
HandleTagCreate
():
shelf_request
=
request
.
json
name
,
items
=
None
,
None
try
:
name
=
shelf_request
[
"Name"
]
items
=
shelf_request
[
"Items"
]
except
KeyError
:
log
.
debug
(
"Received malformed v1/library/tags request."
)
abort
(
400
,
description
=
"Malformed tags POST request. Data is missing 'Name' or 'Items' field"
)
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
and_
(
ub
.
Shelf
.
name
)
==
name
,
ub
.
Shelf
.
user_id
==
current_user
.
id
)
.
one_or_none
()
if
shelf
and
not
shelf_lib
.
check_shelf_edit_permissions
(
shelf
):
abort
(
401
,
description
=
"User is unauthaurized to edit shelf."
)
if
not
shelf
:
shelf
=
ub
.
Shelf
(
user_id
=
current_user
.
id
,
name
=
name
,
uuid
=
uuid
.
uuid4
())
ub
.
session
.
add
(
shelf
)
items_unknown_to_calibre
=
add_items_to_shelf
(
items
,
shelf
)
if
items_unknown_to_calibre
:
log
.
debug
(
"Received request to add unknown books to a collection. Silently ignoring items."
)
ub
.
session
.
commit
()
return
make_response
(
jsonify
(
str
(
shelf
.
uuid
)),
201
)
@
kobo
.
route
(
"/v1/library/tags/<tag_id>"
,
methods
=
[
"DELETE"
,
"PUT"
])
def
HandleTagUpdate
(
tag_id
):
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
and_
(
ub
.
Shelf
.
uuid
)
==
tag_id
,
ub
.
Shelf
.
user_id
==
current_user
.
id
)
.
one_or_none
()
if
not
shelf
:
log
.
debug
(
"Received Kobo tag update request on a collection unknown to CalibreWeb"
)
if
config
.
config_kobo_proxy
:
return
redirect_or_proxy_request
()
else
:
abort
(
404
,
description
=
"Collection isn't known to CalibreWeb"
)
if
not
shelf_lib
.
check_shelf_edit_permissions
(
shelf
):
abort
(
401
,
description
=
"User is unauthaurized to edit shelf."
)
if
request
.
method
==
"DELETE"
:
shelf_lib
.
delete_shelf_helper
(
shelf
)
else
:
shelf_request
=
request
.
json
name
=
None
try
:
name
=
shelf_request
[
"Name"
]
except
KeyError
:
log
.
debug
(
"Received malformed v1/library/tags rename request."
)
abort
(
400
,
description
=
"Malformed tags POST request. Data is missing 'Name' field"
)
shelf
.
name
=
name
ub
.
session
.
merge
(
shelf
)
ub
.
session
.
commit
()
return
make_response
(
' '
,
200
)
# Adds items to the given shelf.
def
add_items_to_shelf
(
items
,
shelf
):
book_ids_already_in_shelf
=
set
([
book_shelf
.
book_id
for
book_shelf
in
shelf
.
books
])
items_unknown_to_calibre
=
[]
for
item
in
items
:
if
item
[
"Type"
]
!=
"ProductRevisionTagItem"
:
items_unknown_to_calibre
.
append
(
item
)
continue
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
uuid
==
item
[
"RevisionId"
])
.
one_or_none
()
if
not
book
:
items_unknown_to_calibre
.
append
(
item
)
continue
book_id
=
book
.
id
if
book_id
not
in
book_ids_already_in_shelf
:
shelf
.
books
.
append
(
ub
.
BookShelf
(
book_id
=
book_id
))
return
items_unknown_to_calibre
@
kobo
.
route
(
"/v1/library/tags/<tag_id>/items"
,
methods
=
[
"POST"
])
@
login_required
def
HandleTagAddItem
(
tag_id
):
tag_request
=
request
.
json
items
=
None
try
:
items
=
tag_request
[
"Items"
]
except
KeyError
:
log
.
debug
(
"Received malformed v1/library/tags/<tag_id>/items/delete request."
)
abort
(
400
,
description
=
"Malformed tags POST request. Data is missing 'Items' field"
)
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
and_
(
ub
.
Shelf
.
uuid
)
==
tag_id
,
ub
.
Shelf
.
user_id
==
current_user
.
id
)
.
one_or_none
()
if
not
shelf
:
log
.
debug
(
"Received Kobo request on a collection unknown to CalibreWeb"
)
abort
(
404
,
description
=
"Collection isn't known to CalibreWeb"
)
if
not
shelf_lib
.
check_shelf_edit_permissions
(
shelf
):
abort
(
401
,
description
=
"User is unauthaurized to edit shelf."
)
items_unknown_to_calibre
=
add_items_to_shelf
(
items
,
shelf
)
if
items_unknown_to_calibre
:
log
.
debug
(
"Received request to add an unknown book to a collecition. Silently ignoring item."
)
ub
.
session
.
merge
(
shelf
)
ub
.
session
.
commit
()
return
make_response
(
''
,
201
)
@
kobo
.
route
(
"/v1/library/tags/<tag_id>/items/delete"
,
methods
=
[
"POST"
])
@
login_required
def
HandleTagRemoveItem
(
tag_id
):
tag_request
=
request
.
json
items
=
None
try
:
items
=
tag_request
[
"Items"
]
except
KeyError
:
log
.
debug
(
"Received malformed v1/library/tags/<tag_id>/items/delete request."
)
abort
(
400
,
description
=
"Malformed tags POST request. Data is missing 'Items' field"
)
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
uuid
==
tag_id
,
ub
.
Shelf
.
user_id
==
current_user
.
id
)
.
one_or_none
()
if
not
shelf
:
log
.
debug
(
"Received a request to remove an item from a Collection unknown to CalibreWeb."
)
abort
(
404
,
description
=
"Collection isn't known to CalibreWeb"
)
if
not
shelf_lib
.
check_shelf_edit_permissions
(
shelf
):
abort
(
401
,
description
=
"User is unauthaurized to edit shelf."
)
items_unknown_to_calibre
=
[]
for
item
in
items
:
if
item
[
"Type"
]
!=
"ProductRevisionTagItem"
:
items_unknown_to_calibre
.
append
(
item
)
continue
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
uuid
==
item
[
"RevisionId"
])
.
one_or_none
()
if
not
book
:
items_unknown_to_calibre
.
append
(
item
)
continue
shelf
.
books
.
filter
(
ub
.
BookShelf
.
book_id
==
book
.
id
)
.
delete
()
ub
.
session
.
commit
()
if
items_unknown_to_calibre
:
log
.
debug
(
"Received request to remove an unknown book to a collecition. Silently ignoring item."
)
return
make_response
(
''
,
200
)
# Add new, changed, or deleted shelves to the sync_results.
# Note: Public shelves that aren't owned by the user aren't supported.
def
sync_shelves
(
sync_token
,
sync_results
):
new_tags_last_modified
=
sync_token
.
tags_last_modified
for
shelf
in
ub
.
session
.
query
(
ub
.
ShelfArchive
)
.
filter
(
func
.
datetime
(
ub
.
ShelfArchive
.
last_modified
)
>
sync_token
.
tags_last_modified
,
ub
.
ShelfArchive
.
user_id
==
current_user
.
id
):
new_tags_last_modified
=
max
(
shelf
.
last_modified
,
new_tags_last_modified
)
sync_results
.
append
({
"DeletedTag"
:
{
"Tag"
:
{
"Id"
:
shelf
.
uuid
,
"LastModified"
:
convert_to_kobo_timestamp_string
(
shelf
.
last_modified
)
}
}
})
for
shelf
in
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
func
.
datetime
(
ub
.
Shelf
.
last_modified
)
>
sync_token
.
tags_last_modified
,
ub
.
Shelf
.
user_id
==
current_user
.
id
):
if
not
shelf_lib
.
check_shelf_view_permissions
(
shelf
):
continue
new_tags_last_modified
=
max
(
shelf
.
last_modified
,
new_tags_last_modified
)
tag
=
create_kobo_tag
(
shelf
)
if
not
tag
:
continue
if
shelf
.
created
>
sync_token
.
tags_last_modified
:
sync_results
.
append
({
"NewTag"
:
tag
})
else
:
sync_results
.
append
({
"ChangedTag"
:
tag
})
sync_token
.
tags_last_modified
=
new_tags_last_modified
ub
.
session
.
commit
()
# Creates a Kobo "Tag" object from a ub.Shelf object
def
create_kobo_tag
(
shelf
):
tag
=
{
"Created"
:
convert_to_kobo_timestamp_string
(
shelf
.
created
),
"Id"
:
shelf
.
uuid
,
"Items"
:
[],
"LastModified"
:
convert_to_kobo_timestamp_string
(
shelf
.
last_modified
),
"Name"
:
shelf
.
name
,
"Type"
:
"UserTag"
}
for
book_shelf
in
shelf
.
books
:
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_shelf
.
book_id
)
.
one_or_none
()
if
not
book
:
log
.
info
(
u"Book (id:
%
s) in BookShelf (id:
%
s) not found in book database"
,
book_shelf
.
book_id
,
shelf
.
id
)
return
None
tag
[
"Items"
]
.
append
(
{
"RevisionId"
:
book
.
uuid
,
"Type"
:
"ProductRevisionTagItem"
}
)
return
{
"Tag"
:
tag
}
@
kobo
.
route
(
"/v1/library/<book_uuid>/state"
,
methods
=
[
"GET"
,
"PUT"
])
@
kobo
.
route
(
"/v1/library/<book_uuid>/state"
,
methods
=
[
"GET"
,
"PUT"
])
@
login_required
@
login_required
def
HandleStateRequest
(
book_uuid
):
def
HandleStateRequest
(
book_uuid
):
...
@@ -589,10 +807,7 @@ def HandleBookDeletionRequest(book_uuid):
...
@@ -589,10 +807,7 @@ def HandleBookDeletionRequest(book_uuid):
# TODO: Implement the following routes
# TODO: Implement the following routes
@
kobo
.
route
(
"/v1/library/<dummy>"
,
methods
=
[
"DELETE"
,
"GET"
])
@
kobo
.
route
(
"/v1/library/<dummy>"
,
methods
=
[
"DELETE"
,
"GET"
])
@
kobo
.
route
(
"/v1/library/tags"
,
methods
=
[
"POST"
])
def
HandleUnimplementedRequest
(
dummy
=
None
):
@
kobo
.
route
(
"/v1/library/tags/<shelf_name>"
,
methods
=
[
"POST"
])
@
kobo
.
route
(
"/v1/library/tags/<tag_id>"
,
methods
=
[
"DELETE"
])
def
HandleUnimplementedRequest
(
dummy
=
None
,
book_uuid
=
None
,
shelf_name
=
None
,
tag_id
=
None
):
log
.
debug
(
"Unimplemented Library Request received:
%
s"
,
request
.
base_url
)
log
.
debug
(
"Unimplemented Library Request received:
%
s"
,
request
.
base_url
)
return
redirect_or_proxy_request
()
return
redirect_or_proxy_request
()
...
@@ -612,6 +827,7 @@ def HandleUserRequest(dummy=None):
...
@@ -612,6 +827,7 @@ def HandleUserRequest(dummy=None):
@
kobo
.
route
(
"/v1/products/<dummy>/recommendations"
,
methods
=
[
"GET"
,
"POST"
])
@
kobo
.
route
(
"/v1/products/<dummy>/recommendations"
,
methods
=
[
"GET"
,
"POST"
])
@
kobo
.
route
(
"/v1/products/<dummy>/nextread"
,
methods
=
[
"GET"
,
"POST"
])
@
kobo
.
route
(
"/v1/products/<dummy>/nextread"
,
methods
=
[
"GET"
,
"POST"
])
@
kobo
.
route
(
"/v1/products/<dummy>/reviews"
,
methods
=
[
"GET"
,
"POST"
])
@
kobo
.
route
(
"/v1/products/<dummy>/reviews"
,
methods
=
[
"GET"
,
"POST"
])
@
kobo
.
route
(
"/v1/products/books/series/<dummy>"
,
methods
=
[
"GET"
,
"POST"
])
@
kobo
.
route
(
"/v1/products/books/<dummy>"
,
methods
=
[
"GET"
,
"POST"
])
@
kobo
.
route
(
"/v1/products/books/<dummy>"
,
methods
=
[
"GET"
,
"POST"
])
@
kobo
.
route
(
"/v1/products/dailydeal"
,
methods
=
[
"GET"
,
"POST"
])
@
kobo
.
route
(
"/v1/products/dailydeal"
,
methods
=
[
"GET"
,
"POST"
])
@
kobo
.
route
(
"/v1/products"
,
methods
=
[
"GET"
,
"POST"
])
@
kobo
.
route
(
"/v1/products"
,
methods
=
[
"GET"
,
"POST"
])
...
@@ -624,7 +840,7 @@ def HandleProductsRequest(dummy=None):
...
@@ -624,7 +840,7 @@ def HandleProductsRequest(dummy=None):
def
handle_404
(
err
):
def
handle_404
(
err
):
# This handler acts as a catch-all for endpoints that we don't have an interest in
# This handler acts as a catch-all for endpoints that we don't have an interest in
# implementing (e.g: v1/analytics/gettests, v1/user/recommendations, etc)
# implementing (e.g: v1/analytics/gettests, v1/user/recommendations, etc)
log
.
debug
(
"Unknown Request received:
%
s
"
,
request
.
base_url
)
log
.
debug
(
"Unknown Request received:
%
s
, method:
%
s, data:
%
s"
,
request
.
base_url
,
request
.
method
,
request
.
data
)
return
redirect_or_proxy_request
()
return
redirect_or_proxy_request
()
...
...
cps/opds.py
View file @
7bb5afa5
...
@@ -326,12 +326,12 @@ def feed_shelfindex():
...
@@ -326,12 +326,12 @@ def feed_shelfindex():
def
feed_shelf
(
book_id
):
def
feed_shelf
(
book_id
):
off
=
request
.
args
.
get
(
"offset"
)
or
0
off
=
request
.
args
.
get
(
"offset"
)
or
0
if
current_user
.
is_anonymous
:
if
current_user
.
is_anonymous
:
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
is_public
==
1
,
ub
.
Shelf
.
id
==
book_id
)
.
first
()
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
is_public
==
1
,
ub
.
Shelf
.
id
==
book_id
,
not
ub
.
Shelf
.
deleted
)
.
first
()
else
:
else
:
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
or_
(
and_
(
ub
.
Shelf
.
user_id
==
int
(
current_user
.
id
),
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
or_
(
and_
(
ub
.
Shelf
.
user_id
==
int
(
current_user
.
id
),
ub
.
Shelf
.
id
==
book_id
),
ub
.
Shelf
.
id
==
book_id
),
and_
(
ub
.
Shelf
.
is_public
==
1
,
and_
(
ub
.
Shelf
.
is_public
==
1
,
ub
.
Shelf
.
id
==
book_id
)))
.
first
()
ub
.
Shelf
.
id
==
book_id
))
,
not
ub
.
Shelf
.
deleted
)
.
first
()
result
=
list
()
result
=
list
()
# user is allowed to access shelf
# user is allowed to access shelf
if
shelf
:
if
shelf
:
...
...
cps/services/SyncToken.py
View file @
7bb5afa5
...
@@ -78,6 +78,7 @@ class SyncToken():
...
@@ -78,6 +78,7 @@ class SyncToken():
"books_last_created"
:
{
"type"
:
"string"
},
"books_last_created"
:
{
"type"
:
"string"
},
"archive_last_modified"
:
{
"type"
:
"string"
},
"archive_last_modified"
:
{
"type"
:
"string"
},
"reading_state_last_modified"
:
{
"type"
:
"string"
},
"reading_state_last_modified"
:
{
"type"
:
"string"
},
"tags_last_modified"
:
{
"type"
:
"string"
},
},
},
}
}
...
@@ -88,13 +89,14 @@ class SyncToken():
...
@@ -88,13 +89,14 @@ class SyncToken():
books_last_modified
=
datetime
.
min
,
books_last_modified
=
datetime
.
min
,
archive_last_modified
=
datetime
.
min
,
archive_last_modified
=
datetime
.
min
,
reading_state_last_modified
=
datetime
.
min
,
reading_state_last_modified
=
datetime
.
min
,
tags_last_modified
=
datetime
.
min
,
):
):
self
.
raw_kobo_store_token
=
raw_kobo_store_token
self
.
raw_kobo_store_token
=
raw_kobo_store_token
self
.
books_last_created
=
books_last_created
self
.
books_last_created
=
books_last_created
self
.
books_last_modified
=
books_last_modified
self
.
books_last_modified
=
books_last_modified
self
.
archive_last_modified
=
archive_last_modified
self
.
archive_last_modified
=
archive_last_modified
self
.
reading_state_last_modified
=
reading_state_last_modified
self
.
reading_state_last_modified
=
reading_state_last_modified
self
.
tags_last_modified
=
tags_last_modified
@
staticmethod
@
staticmethod
def
from_headers
(
headers
):
def
from_headers
(
headers
):
...
@@ -128,6 +130,7 @@ class SyncToken():
...
@@ -128,6 +130,7 @@ class SyncToken():
books_last_created
=
get_datetime_from_json
(
data_json
,
"books_last_created"
)
books_last_created
=
get_datetime_from_json
(
data_json
,
"books_last_created"
)
archive_last_modified
=
get_datetime_from_json
(
data_json
,
"archive_last_modified"
)
archive_last_modified
=
get_datetime_from_json
(
data_json
,
"archive_last_modified"
)
reading_state_last_modified
=
get_datetime_from_json
(
data_json
,
"reading_state_last_modified"
)
reading_state_last_modified
=
get_datetime_from_json
(
data_json
,
"reading_state_last_modified"
)
tags_last_modified
=
get_datetime_from_json
(
data_json
,
"tags_last_modified"
)
except
TypeError
:
except
TypeError
:
log
.
error
(
"SyncToken timestamps don't parse to a datetime."
)
log
.
error
(
"SyncToken timestamps don't parse to a datetime."
)
return
SyncToken
(
raw_kobo_store_token
=
raw_kobo_store_token
)
return
SyncToken
(
raw_kobo_store_token
=
raw_kobo_store_token
)
...
@@ -137,7 +140,8 @@ class SyncToken():
...
@@ -137,7 +140,8 @@ class SyncToken():
books_last_created
=
books_last_created
,
books_last_created
=
books_last_created
,
books_last_modified
=
books_last_modified
,
books_last_modified
=
books_last_modified
,
archive_last_modified
=
archive_last_modified
,
archive_last_modified
=
archive_last_modified
,
reading_state_last_modified
=
reading_state_last_modified
reading_state_last_modified
=
reading_state_last_modified
,
tags_last_modified
=
tags_last_modified
)
)
def
set_kobo_store_header
(
self
,
store_headers
):
def
set_kobo_store_header
(
self
,
store_headers
):
...
@@ -159,7 +163,8 @@ class SyncToken():
...
@@ -159,7 +163,8 @@ class SyncToken():
"books_last_modified"
:
to_epoch_timestamp
(
self
.
books_last_modified
),
"books_last_modified"
:
to_epoch_timestamp
(
self
.
books_last_modified
),
"books_last_created"
:
to_epoch_timestamp
(
self
.
books_last_created
),
"books_last_created"
:
to_epoch_timestamp
(
self
.
books_last_created
),
"archive_last_modified"
:
to_epoch_timestamp
(
self
.
archive_last_modified
),
"archive_last_modified"
:
to_epoch_timestamp
(
self
.
archive_last_modified
),
"reading_state_last_modified"
:
to_epoch_timestamp
(
self
.
reading_state_last_modified
)
"reading_state_last_modified"
:
to_epoch_timestamp
(
self
.
reading_state_last_modified
),
"tags_last_modified"
:
to_epoch_timestamp
(
self
.
tags_last_modified
)
},
},
}
}
return
b64encode_json
(
token
)
return
b64encode_json
(
token
)
cps/shelf.py
View file @
7bb5afa5
...
@@ -35,6 +35,24 @@ from .helper import common_filters
...
@@ -35,6 +35,24 @@ from .helper import common_filters
shelf
=
Blueprint
(
'shelf'
,
__name__
)
shelf
=
Blueprint
(
'shelf'
,
__name__
)
log
=
logger
.
create
()
log
=
logger
.
create
()
def
check_shelf_edit_permissions
(
cur_shelf
):
if
not
cur_shelf
.
is_public
and
not
cur_shelf
.
user_id
==
int
(
current_user
.
id
):
log
.
error
(
"User
%
s not allowed to edit shelf
%
s"
,
current_user
,
cur_shelf
)
return
False
if
cur_shelf
.
is_public
and
not
current_user
.
role_edit_shelfs
():
log
.
info
(
"User
%
s not allowed to edit public shelves"
,
current_user
)
return
False
return
True
def
check_shelf_view_permissions
(
cur_shelf
):
if
cur_shelf
.
is_public
:
return
True
if
current_user
.
is_anonymous
or
cur_shelf
.
user_id
!=
current_user
.
id
:
log
.
error
(
"User is unauthorized to view non-public shelf:
%
s"
,
cur_shelf
)
return
False
return
True
@
shelf
.
route
(
"/shelf/add/<int:shelf_id>/<int:book_id>"
)
@
shelf
.
route
(
"/shelf/add/<int:shelf_id>/<int:book_id>"
)
@
login_required
@
login_required
...
@@ -48,21 +66,13 @@ def add_to_shelf(shelf_id, book_id):
...
@@ -48,21 +66,13 @@ def add_to_shelf(shelf_id, book_id):
return
redirect
(
url_for
(
'web.index'
))
return
redirect
(
url_for
(
'web.index'
))
return
"Invalid shelf specified"
,
400
return
"Invalid shelf specified"
,
400
if
not
shelf
.
is_public
and
not
shelf
.
user_id
==
int
(
current_user
.
id
):
if
not
check_shelf_edit_permissions
(
shelf
):
log
.
error
(
"User
%
s not allowed to add a book to
%
s"
,
current_user
,
shelf
)
if
not
xhr
:
if
not
xhr
:
flash
(
_
(
u"Sorry you are not allowed to add a book to the the shelf:
%(shelfname)
s"
,
shelfname
=
shelf
.
name
),
flash
(
_
(
u"Sorry you are not allowed to add a book to the the shelf:
%(shelfname)
s"
,
shelfname
=
shelf
.
name
),
category
=
"error"
)
category
=
"error"
)
return
redirect
(
url_for
(
'web.index'
))
return
redirect
(
url_for
(
'web.index'
))
return
"Sorry you are not allowed to add a book to the the shelf:
%
s"
%
shelf
.
name
,
403
return
"Sorry you are not allowed to add a book to the the shelf:
%
s"
%
shelf
.
name
,
403
if
shelf
.
is_public
and
not
current_user
.
role_edit_shelfs
():
log
.
info
(
"User
%
s not allowed to edit public shelves"
,
current_user
)
if
not
xhr
:
flash
(
_
(
u"You are not allowed to edit public shelves"
),
category
=
"error"
)
return
redirect
(
url_for
(
'web.index'
))
return
"User is not allowed to edit public shelves"
,
403
book_in_shelf
=
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
,
book_in_shelf
=
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
,
ub
.
BookShelf
.
book_id
==
book_id
)
.
first
()
ub
.
BookShelf
.
book_id
==
book_id
)
.
first
()
if
book_in_shelf
:
if
book_in_shelf
:
...
@@ -78,8 +88,8 @@ def add_to_shelf(shelf_id, book_id):
...
@@ -78,8 +88,8 @@ def add_to_shelf(shelf_id, book_id):
else
:
else
:
maxOrder
=
maxOrder
[
0
]
maxOrder
=
maxOrder
[
0
]
ins
=
ub
.
BookShelf
(
shelf
=
shelf
.
id
,
book_id
=
book_id
,
order
=
maxOrder
+
1
)
shelf
.
books
.
append
(
ub
.
BookShelf
(
shelf
=
shelf
.
id
,
book_id
=
book_id
,
order
=
maxOrder
+
1
)
)
ub
.
session
.
add
(
ins
)
ub
.
session
.
merge
(
shelf
)
ub
.
session
.
commit
()
ub
.
session
.
commit
()
if
not
xhr
:
if
not
xhr
:
flash
(
_
(
u"Book has been added to shelf:
%(sname)
s"
,
sname
=
shelf
.
name
),
category
=
"success"
)
flash
(
_
(
u"Book has been added to shelf:
%(sname)
s"
,
sname
=
shelf
.
name
),
category
=
"success"
)
...
@@ -99,16 +109,10 @@ def search_to_shelf(shelf_id):
...
@@ -99,16 +109,10 @@ def search_to_shelf(shelf_id):
flash
(
_
(
u"Invalid shelf specified"
),
category
=
"error"
)
flash
(
_
(
u"Invalid shelf specified"
),
category
=
"error"
)
return
redirect
(
url_for
(
'web.index'
))
return
redirect
(
url_for
(
'web.index'
))
if
not
shelf
.
is_public
and
not
shelf
.
user_id
==
int
(
current_user
.
id
):
if
not
check_shelf_edit_permissions
(
shelf
):
log
.
error
(
"User
%
s not allowed to add a book to
%
s"
,
current_user
,
shelf
)
flash
(
_
(
u"You are not allowed to add a book to the the shelf:
%(name)
s"
,
name
=
shelf
.
name
),
category
=
"error"
)
flash
(
_
(
u"You are not allowed to add a book to the the shelf:
%(name)
s"
,
name
=
shelf
.
name
),
category
=
"error"
)
return
redirect
(
url_for
(
'web.index'
))
return
redirect
(
url_for
(
'web.index'
))
if
shelf
.
is_public
and
not
current_user
.
role_edit_shelfs
():
log
.
error
(
"User
%
s not allowed to edit public shelves"
,
current_user
)
flash
(
_
(
u"User is not allowed to edit public shelves"
),
category
=
"error"
)
return
redirect
(
url_for
(
'web.index'
))
if
current_user
.
id
in
searched_ids
and
searched_ids
[
current_user
.
id
]:
if
current_user
.
id
in
searched_ids
and
searched_ids
[
current_user
.
id
]:
books_for_shelf
=
list
()
books_for_shelf
=
list
()
books_in_shelf
=
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
)
.
all
()
books_in_shelf
=
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
)
.
all
()
...
@@ -135,8 +139,8 @@ def search_to_shelf(shelf_id):
...
@@ -135,8 +139,8 @@ def search_to_shelf(shelf_id):
for
book
in
books_for_shelf
:
for
book
in
books_for_shelf
:
maxOrder
=
maxOrder
+
1
maxOrder
=
maxOrder
+
1
ins
=
ub
.
BookShelf
(
shelf
=
shelf
.
id
,
book_id
=
book
,
order
=
maxOrder
)
shelf
.
books
.
append
(
ub
.
BookShelf
(
shelf
=
shelf
.
id
,
book_id
=
book
,
order
=
maxOrder
)
)
ub
.
session
.
add
(
ins
)
ub
.
session
.
merge
(
shelf
)
ub
.
session
.
commit
()
ub
.
session
.
commit
()
flash
(
_
(
u"Books have been added to shelf:
%(sname)
s"
,
sname
=
shelf
.
name
),
category
=
"success"
)
flash
(
_
(
u"Books have been added to shelf:
%(sname)
s"
,
sname
=
shelf
.
name
),
category
=
"success"
)
else
:
else
:
...
@@ -163,8 +167,7 @@ def remove_from_shelf(shelf_id, book_id):
...
@@ -163,8 +167,7 @@ def remove_from_shelf(shelf_id, book_id):
# true 0 x 1
# true 0 x 1
# false 0 x 0
# false 0 x 0
if
(
not
shelf
.
is_public
and
shelf
.
user_id
==
int
(
current_user
.
id
))
\
if
check_shelf_edit_permissions
(
shelf
):
or
(
shelf
.
is_public
and
current_user
.
role_edit_shelfs
()):
book_shelf
=
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
,
book_shelf
=
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
,
ub
.
BookShelf
.
book_id
==
book_id
)
.
first
()
ub
.
BookShelf
.
book_id
==
book_id
)
.
first
()
...
@@ -185,7 +188,6 @@ def remove_from_shelf(shelf_id, book_id):
...
@@ -185,7 +188,6 @@ def remove_from_shelf(shelf_id, book_id):
return
redirect
(
url_for
(
'web.index'
))
return
redirect
(
url_for
(
'web.index'
))
return
""
,
204
return
""
,
204
else
:
else
:
log
.
error
(
"User
%
s not allowed to remove a book from
%
s"
,
current_user
,
shelf
)
if
not
xhr
:
if
not
xhr
:
flash
(
_
(
u"Sorry you are not allowed to remove a book from this shelf:
%(sname)
s"
,
sname
=
shelf
.
name
),
flash
(
_
(
u"Sorry you are not allowed to remove a book from this shelf:
%(sname)
s"
,
sname
=
shelf
.
name
),
category
=
"error"
)
category
=
"error"
)
...
@@ -238,7 +240,7 @@ def create_shelf():
...
@@ -238,7 +240,7 @@ def create_shelf():
@
login_required
@
login_required
def
edit_shelf
(
shelf_id
):
def
edit_shelf
(
shelf_id
):
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
id
==
shelf_id
)
.
first
()
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
id
==
shelf_id
)
.
first
()
if
request
.
method
==
"POST"
:
if
request
.
method
==
"POST"
:
to_save
=
request
.
form
.
to_dict
()
to_save
=
request
.
form
.
to_dict
()
is_shelf_name_unique
=
False
is_shelf_name_unique
=
False
...
@@ -275,41 +277,33 @@ def edit_shelf(shelf_id):
...
@@ -275,41 +277,33 @@ def edit_shelf(shelf_id):
return
render_title_template
(
'shelf_edit.html'
,
shelf
=
shelf
,
title
=
_
(
u"Edit a shelf"
),
page
=
"shelfedit"
)
return
render_title_template
(
'shelf_edit.html'
,
shelf
=
shelf
,
title
=
_
(
u"Edit a shelf"
),
page
=
"shelfedit"
)
def
delete_shelf_helper
(
cur_shelf
):
if
not
cur_shelf
or
not
check_shelf_edit_permissions
(
cur_shelf
):
return
shelf_id
=
cur_shelf
.
id
ub
.
session
.
delete
(
cur_shelf
)
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
)
.
delete
()
ub
.
session
.
add
(
ub
.
ShelfArchive
(
uuid
=
cur_shelf
.
uuid
,
user_id
=
cur_shelf
.
uuid
))
ub
.
session
.
commit
()
log
.
info
(
"successfully deleted
%
s"
,
cur_shelf
)
@
shelf
.
route
(
"/shelf/delete/<int:shelf_id>"
)
@
shelf
.
route
(
"/shelf/delete/<int:shelf_id>"
)
@
login_required
@
login_required
def
delete_shelf
(
shelf_id
):
def
delete_shelf
(
shelf_id
):
cur_shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
id
==
shelf_id
)
.
first
()
cur_shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
id
==
shelf_id
)
.
first
()
deleted
=
None
delete_shelf_helper
(
cur_shelf
)
if
current_user
.
role_admin
():
deleted
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
id
==
shelf_id
)
.
delete
()
else
:
if
(
not
cur_shelf
.
is_public
and
cur_shelf
.
user_id
==
int
(
current_user
.
id
))
\
or
(
cur_shelf
.
is_public
and
current_user
.
role_edit_shelfs
()):
deleted
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
or_
(
and_
(
ub
.
Shelf
.
user_id
==
int
(
current_user
.
id
),
ub
.
Shelf
.
id
==
shelf_id
),
and_
(
ub
.
Shelf
.
is_public
==
1
,
ub
.
Shelf
.
id
==
shelf_id
)))
.
delete
()
if
deleted
:
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
)
.
delete
()
ub
.
session
.
commit
()
log
.
info
(
"successfully deleted
%
s"
,
cur_shelf
)
return
redirect
(
url_for
(
'web.index'
))
return
redirect
(
url_for
(
'web.index'
))
# @shelf.route("/shelfdown/<int:shelf_id>")
# @shelf.route("/shelfdown/<int:shelf_id>")
@
shelf
.
route
(
"/shelf/<int:shelf_id>"
,
defaults
=
{
'shelf_type'
:
1
})
@
shelf
.
route
(
"/shelf/<int:shelf_id>"
,
defaults
=
{
'shelf_type'
:
1
})
@
shelf
.
route
(
"/shelf/<int:shelf_id>/<int:shelf_type>"
)
@
shelf
.
route
(
"/shelf/<int:shelf_id>/<int:shelf_type>"
)
def
show_shelf
(
shelf_type
,
shelf_id
):
def
show_shelf
(
shelf_type
,
shelf_id
):
if
current_user
.
is_anonymous
:
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
id
==
shelf_id
)
.
first
()
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
is_public
==
1
,
ub
.
Shelf
.
id
==
shelf_id
)
.
first
()
else
:
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
or_
(
and_
(
ub
.
Shelf
.
user_id
==
int
(
current_user
.
id
),
ub
.
Shelf
.
id
==
shelf_id
),
and_
(
ub
.
Shelf
.
is_public
==
1
,
ub
.
Shelf
.
id
==
shelf_id
)))
.
first
()
result
=
list
()
result
=
list
()
# user is allowed to access shelf
# user is allowed to access shelf
if
shelf
:
if
shelf
and
check_shelf_view_permissions
(
shelf
)
:
page
=
"shelf.html"
if
shelf_type
==
1
else
'shelfdown.html'
page
=
"shelf.html"
if
shelf_type
==
1
else
'shelfdown.html'
books_in_shelf
=
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
)
\
books_in_shelf
=
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
)
\
...
@@ -344,15 +338,10 @@ def order_shelf(shelf_id):
...
@@ -344,15 +338,10 @@ def order_shelf(shelf_id):
setattr
(
book
,
'order'
,
to_save
[
str
(
book
.
book_id
)])
setattr
(
book
,
'order'
,
to_save
[
str
(
book
.
book_id
)])
counter
+=
1
counter
+=
1
ub
.
session
.
commit
()
ub
.
session
.
commit
()
if
current_user
.
is_anonymous
:
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
is_public
==
1
,
ub
.
Shelf
.
id
==
shelf_id
)
.
first
()
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
id
==
shelf_id
)
.
first
()
else
:
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
or_
(
and_
(
ub
.
Shelf
.
user_id
==
int
(
current_user
.
id
),
ub
.
Shelf
.
id
==
shelf_id
),
and_
(
ub
.
Shelf
.
is_public
==
1
,
ub
.
Shelf
.
id
==
shelf_id
)))
.
first
()
result
=
list
()
result
=
list
()
if
shelf
:
if
shelf
and
check_shelf_view_permissions
(
shelf
)
:
books_in_shelf2
=
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
)
\
books_in_shelf2
=
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
)
\
.
order_by
(
ub
.
BookShelf
.
order
.
asc
())
.
all
()
.
order_by
(
ub
.
BookShelf
.
order
.
asc
())
.
all
()
for
book
in
books_in_shelf2
:
for
book
in
books_in_shelf2
:
...
...
cps/templates/detail.html
View file @
7bb5afa5
...
@@ -233,18 +233,6 @@
...
@@ -233,18 +233,6 @@
<span
class=
"caret"
></span>
<span
class=
"caret"
></span>
</button>
</button>
<ul
id=
"add-to-shelves"
class=
"dropdown-menu"
aria-labelledby=
"add-to-shelf"
>
<ul
id=
"add-to-shelves"
class=
"dropdown-menu"
aria-labelledby=
"add-to-shelf"
>
{% for shelf in g.user.shelf %}
{% if not shelf.id in books_shelfs and shelf.is_public != 1 %}
<li>
<a
href=
"{{ url_for('shelf.add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
data-remove-href=
"{{ url_for('shelf.remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
data-shelf-action=
"add"
>
{{shelf.name}}
</a>
</li>
{% endif %}
{%endfor%}
{% for shelf in g.shelves_access %}
{% for shelf in g.shelves_access %}
{% if not shelf.id in books_shelfs %}
{% if not shelf.id in books_shelfs %}
<li>
<li>
...
@@ -261,16 +249,6 @@
...
@@ -261,16 +249,6 @@
</div>
</div>
<div
id=
"remove-from-shelves"
class=
"btn-group"
role=
"group"
aria-label=
"Remove from shelves"
>
<div
id=
"remove-from-shelves"
class=
"btn-group"
role=
"group"
aria-label=
"Remove from shelves"
>
{% if books_shelfs %}
{% if books_shelfs %}
{% for shelf in g.user.shelf %}
{% if shelf.id in books_shelfs and shelf.is_public != 1 %}
<a
href=
"{{ url_for('shelf.remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
data-add-href=
"{{ url_for('shelf.add_to_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
class=
"btn btn-sm btn-default"
role=
"button"
data-shelf-action=
"remove"
>
<span
class=
"glyphicon glyphicon-remove"
></span>
{{shelf.name}}
</a>
{% endif %}
{%endfor%}
{% for shelf in g.shelves_access %}
{% for shelf in g.shelves_access %}
{% if shelf.id in books_shelfs %}
{% if shelf.id in books_shelfs %}
<a
href=
"{{ url_for('shelf.remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
<a
href=
"{{ url_for('shelf.remove_from_shelf', book_id=entry.id, shelf_id=shelf.id) }}"
...
...
cps/ub.py
View file @
7bb5afa5
...
@@ -21,6 +21,7 @@ from __future__ import division, print_function, unicode_literals
...
@@ -21,6 +21,7 @@ from __future__ import division, print_function, unicode_literals
import
os
import
os
import
datetime
import
datetime
import
itertools
import
itertools
import
uuid
from
binascii
import
hexlify
from
binascii
import
hexlify
from
flask
import
g
from
flask
import
g
...
@@ -40,8 +41,8 @@ except ImportError:
...
@@ -40,8 +41,8 @@ except ImportError:
from
sqlalchemy
import
create_engine
,
exc
,
exists
,
event
from
sqlalchemy
import
create_engine
,
exc
,
exists
,
event
from
sqlalchemy
import
Column
,
ForeignKey
from
sqlalchemy
import
Column
,
ForeignKey
from
sqlalchemy
import
String
,
Integer
,
SmallInteger
,
Boolean
,
DateTime
,
Float
from
sqlalchemy
import
String
,
Integer
,
SmallInteger
,
Boolean
,
DateTime
,
Float
from
sqlalchemy.orm
import
backref
,
foreign
,
relationship
,
remote
,
sessionmaker
,
Session
from
sqlalchemy.ext.declarative
import
declarative_base
from
sqlalchemy.ext.declarative
import
declarative_base
from
sqlalchemy.orm
import
backref
,
foreign
,
relationship
,
remote
,
sessionmaker
,
Session
from
sqlalchemy.sql.expression
import
and_
from
sqlalchemy.sql.expression
import
and_
from
werkzeug.security
import
generate_password_hash
from
werkzeug.security
import
generate_password_hash
...
@@ -270,9 +271,13 @@ class Shelf(Base):
...
@@ -270,9 +271,13 @@ class Shelf(Base):
__tablename__
=
'shelf'
__tablename__
=
'shelf'
id
=
Column
(
Integer
,
primary_key
=
True
)
id
=
Column
(
Integer
,
primary_key
=
True
)
uuid
=
Column
(
String
,
default
=
lambda
:
str
(
uuid
.
uuid4
()))
name
=
Column
(
String
)
name
=
Column
(
String
)
is_public
=
Column
(
Integer
,
default
=
0
)
is_public
=
Column
(
Integer
,
default
=
0
)
user_id
=
Column
(
Integer
,
ForeignKey
(
'user.id'
))
user_id
=
Column
(
Integer
,
ForeignKey
(
'user.id'
))
books
=
relationship
(
"BookShelf"
,
backref
=
"ub_shelf"
,
cascade
=
"all, delete-orphan"
,
lazy
=
"dynamic"
)
created
=
Column
(
DateTime
,
default
=
datetime
.
datetime
.
utcnow
)
last_modified
=
Column
(
DateTime
,
default
=
datetime
.
datetime
.
utcnow
,
onupdate
=
datetime
.
datetime
.
utcnow
)
def
__repr__
(
self
):
def
__repr__
(
self
):
return
'<Shelf
%
d:
%
r>'
%
(
self
.
id
,
self
.
name
)
return
'<Shelf
%
d:
%
r>'
%
(
self
.
id
,
self
.
name
)
...
@@ -286,11 +291,22 @@ class BookShelf(Base):
...
@@ -286,11 +291,22 @@ class BookShelf(Base):
book_id
=
Column
(
Integer
)
book_id
=
Column
(
Integer
)
order
=
Column
(
Integer
)
order
=
Column
(
Integer
)
shelf
=
Column
(
Integer
,
ForeignKey
(
'shelf.id'
))
shelf
=
Column
(
Integer
,
ForeignKey
(
'shelf.id'
))
date_added
=
Column
(
DateTime
,
default
=
datetime
.
datetime
.
utcnow
)
def
__repr__
(
self
):
def
__repr__
(
self
):
return
'<Book
%
r>'
%
self
.
id
return
'<Book
%
r>'
%
self
.
id
# This table keeps track of deleted Shelves so that deletes can be propagated to any paired Kobo device.
class
ShelfArchive
(
Base
):
__tablename__
=
'shelf_archive'
id
=
Column
(
Integer
,
primary_key
=
True
)
uuid
=
Column
(
String
)
user_id
=
Column
(
Integer
,
ForeignKey
(
'user.id'
))
last_modified
=
Column
(
DateTime
,
default
=
datetime
.
datetime
.
utcnow
)
class
ReadBook
(
Base
):
class
ReadBook
(
Base
):
__tablename__
=
'book_read_link'
__tablename__
=
'book_read_link'
...
@@ -375,6 +391,10 @@ def receive_before_flush(session, flush_context, instances):
...
@@ -375,6 +391,10 @@ def receive_before_flush(session, flush_context, instances):
if
isinstance
(
change
,
(
ReadBook
,
KoboStatistics
,
KoboBookmark
)):
if
isinstance
(
change
,
(
ReadBook
,
KoboStatistics
,
KoboBookmark
)):
if
change
.
kobo_reading_state
:
if
change
.
kobo_reading_state
:
change
.
kobo_reading_state
.
last_modified
=
datetime
.
datetime
.
utcnow
()
change
.
kobo_reading_state
.
last_modified
=
datetime
.
datetime
.
utcnow
()
# Maintain the last_modified bit for the Shelf table.
for
change
in
itertools
.
chain
(
session
.
new
,
session
.
deleted
):
if
isinstance
(
change
,
BookShelf
):
change
.
ub_shelf
.
last_modified
=
datetime
.
datetime
.
utcnow
()
# Baseclass representing Downloads from calibre-web in app.db
# Baseclass representing Downloads from calibre-web in app.db
...
@@ -468,7 +488,21 @@ def migrate_Database(session):
...
@@ -468,7 +488,21 @@ def migrate_Database(session):
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'last_time_started_reading' DATETIME"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'last_time_started_reading' DATETIME"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'times_started_reading' INTEGER DEFAULT 0"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'times_started_reading' INTEGER DEFAULT 0"
)
session
.
commit
()
session
.
commit
()
try
:
session
.
query
(
exists
()
.
where
(
Shelf
.
uuid
))
.
scalar
()
except
exc
.
OperationalError
:
conn
=
engine
.
connect
()
conn
.
execute
(
"ALTER TABLE shelf ADD column 'uuid' STRING"
)
conn
.
execute
(
"ALTER TABLE shelf ADD column 'created' DATETIME"
)
conn
.
execute
(
"ALTER TABLE shelf ADD column 'last_modified' DATETIME"
)
conn
.
execute
(
"ALTER TABLE book_shelf_link ADD column 'date_added' DATETIME"
)
for
shelf
in
session
.
query
(
Shelf
)
.
all
():
shelf
.
uuid
=
str
(
uuid
.
uuid4
())
shelf
.
created
=
datetime
.
datetime
.
now
()
shelf
.
last_modified
=
datetime
.
datetime
.
now
()
for
book_shelf
in
session
.
query
(
BookShelf
)
.
all
():
book_shelf
.
date_added
=
datetime
.
datetime
.
now
()
session
.
commit
()
# Handle table exists, but no content
# Handle table exists, but no content
cnt
=
session
.
query
(
Registration
)
.
count
()
cnt
=
session
.
query
(
Registration
)
.
count
()
if
not
cnt
:
if
not
cnt
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment