Deleted book formats remove book from synced to kobo table

updated teststatus
This commit is contained in:
Ozzie Isaacs 2022-01-23 13:11:02 +01:00
parent 3b216bfa07
commit 35f6f4c727
6 changed files with 128 additions and 151 deletions

View File

@ -341,7 +341,8 @@ def delete_book_from_table(book_id, book_format, jsonResponse):
else: else:
calibre_db.session.query(db.Data).filter(db.Data.book == book.id).\ calibre_db.session.query(db.Data).filter(db.Data.book == book.id).\
filter(db.Data.format == book_format).delete() filter(db.Data.format == book_format).delete()
kobo_sync_status.remove_synced_book(book.id, True) if book_format.upper() in ['KEPUB', 'EPUB', 'EPUB3']:
kobo_sync_status.remove_synced_book(book.id, True)
calibre_db.session.commit() calibre_db.session.commit()
except Exception as ex: except Exception as ex:
log.debug_or_exception(ex) log.debug_or_exception(ex)

View File

@ -17,18 +17,18 @@
# 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 sys
import os import os
import io import io
import mimetypes import mimetypes
import re import re
import shutil import shutil
import time import socket
import unicodedata import unicodedata
from datetime import datetime, timedelta from datetime import datetime, timedelta
from tempfile import gettempdir from tempfile import gettempdir
from urllib.parse import urlparse
import requests import requests
from babel.dates import format_datetime from babel.dates import format_datetime
from babel.units import format_unit from babel.units import format_unit
from flask import send_from_directory, make_response, redirect, abort, url_for from flask import send_from_directory, make_response, redirect, abort, url_for
@ -584,10 +584,16 @@ def get_book_cover_internal(book, use_generic_cover_on_failure):
# saves book cover from url # saves book cover from url
def save_cover_from_url(url, book_path): def save_cover_from_url(url, book_path):
try: try:
# 127.0.x.x, localhost, [::1], [::ffff:7f00:1]
ip = socket.getaddrinfo(urlparse(url).hostname, 0)[0][4][0]
if ip.startswith("127.") or ip.startswith('::ffff:7f') or ip == "::1":
log.error("Localhost was accessed for cover upload")
return False, _("You are not allowed to access localhost for cover uploads")
img = requests.get(url, timeout=(10, 200)) # ToDo: Error Handling img = requests.get(url, timeout=(10, 200)) # ToDo: Error Handling
img.raise_for_status() img.raise_for_status()
return save_cover(img, book_path) return save_cover(img, book_path)
except (requests.exceptions.HTTPError, except (socket.gaierror,
requests.exceptions.HTTPError,
requests.exceptions.ConnectionError, requests.exceptions.ConnectionError,
requests.exceptions.Timeout) as ex: requests.exceptions.Timeout) as ex:
log.info(u'Cover Download Error %s', ex) log.info(u'Cover Download Error %s', ex)

View File

@ -21,6 +21,7 @@ from flask_login import current_user
from . import ub from . import ub
import datetime import datetime
from sqlalchemy.sql.expression import or_, and_, true from sqlalchemy.sql.expression import or_, and_, true
from sqlalchemy import exc
# Add the current book id to kobo_synced_books table for current user, if entry is already present, # Add the current book id to kobo_synced_books table for current user, if entry is already present,
# do nothing (safety precaution) # do nothing (safety precaution)
@ -36,14 +37,18 @@ def add_synced_books(book_id):
# Select all entries of current book in kobo_synced_books table, which are from current user and delete them # Select all entries of current book in kobo_synced_books table, which are from current user and delete them
def remove_synced_book(book_id, all=False): def remove_synced_book(book_id, all=False, session=None):
if not all: if not all:
user = ub.KoboSyncedBooks.user_id == current_user.id user = ub.KoboSyncedBooks.user_id == current_user.id
else: else:
user = true() user = true()
ub.session.query(ub.KoboSyncedBooks).filter(ub.KoboSyncedBooks.book_id == book_id) \ if not session:
.filter(user).delete() ub.session.query(ub.KoboSyncedBooks).filter(ub.KoboSyncedBooks.book_id == book_id).filter(user).delete()
ub.session_commit() ub.session_commit()
else:
session.query(ub.KoboSyncedBooks).filter(ub.KoboSyncedBooks.book_id == book_id).filter(user).delete()
ub.session_commit(sess=session)
def change_archived_books(book_id, state=None, message=None): def change_archived_books(book_id, state=None, message=None):

View File

@ -16,7 +16,6 @@
# 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 sys
import os import os
import re import re
@ -31,7 +30,8 @@ from cps import db
from cps import logger, config from cps import logger, config
from cps.subproc_wrapper import process_open from cps.subproc_wrapper import process_open
from flask_babel import gettext as _ from flask_babel import gettext as _
from flask import url_for from cps.kobo_sync_status import remove_synced_book
from cps.ub import ini
from cps.tasks.mail import TaskEmail from cps.tasks.mail import TaskEmail
from cps import gdriveutils from cps import gdriveutils
@ -147,6 +147,10 @@ class TaskConvert(CalibreTask):
try: try:
local_db.session.merge(new_format) local_db.session.merge(new_format)
local_db.session.commit() local_db.session.commit()
if self.settings['new_book_format'].upper() in ['KEPUB', 'EPUB', 'EPUB3']:
ub_session = ini()
remove_synced_book(book_id, True, ub_session)
ub_session.close()
except SQLAlchemyError as e: except SQLAlchemyError as e:
local_db.session.rollback() local_db.session.rollback()
log.error("Database error: %s", e) log.error("Database error: %s", e)

View File

@ -773,6 +773,14 @@ def create_admin_user(session):
except Exception: except Exception:
session.rollback() session.rollback()
def ini():
global app_DB_path
engine = create_engine(u'sqlite:///{0}'.format(app_DB_path), echo=False)
Session = scoped_session(sessionmaker())
Session.configure(bind=engine)
return Session()
def init_db(app_db_path): def init_db(app_db_path):
# Open session for database connection # Open session for database connection
@ -830,12 +838,13 @@ def dispose():
except Exception: except Exception:
pass pass
def session_commit(success=None): def session_commit(success=None, sess=None):
s = sess if sess else session
try: try:
session.commit() s.commit()
if success: if success:
log.info(success) log.info(success)
except (exc.OperationalError, exc.InvalidRequestError) as e: except (exc.OperationalError, exc.InvalidRequestError) as e:
session.rollback() s.rollback()
log.debug_or_exception(e) log.debug_or_exception(e)
return "" return ""

View File

@ -37,20 +37,20 @@
<div class="row"> <div class="row">
<div class="col-xs-6 col-md-6 col-sm-offset-3" style="margin-top:50px;"> <div class="col-xs-6 col-md-6 col-sm-offset-3" style="margin-top:50px;">
<p class='text-justify attribute'><strong>Start Time: </strong>2022-01-18 21:11:17</p> <p class='text-justify attribute'><strong>Start Time: </strong>2022-01-23 05:50:54</p>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-6 col-md-6 col-sm-offset-3"> <div class="col-xs-6 col-md-6 col-sm-offset-3">
<p class='text-justify attribute'><strong>Stop Time: </strong>2022-01-19 01:03:52</p> <p class='text-justify attribute'><strong>Stop Time: </strong>2022-01-23 10:25:22</p>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-6 col-md-6 col-sm-offset-3"> <div class="col-xs-6 col-md-6 col-sm-offset-3">
<p class='text-justify attribute'><strong>Duration: </strong>3h 12 min</p> <p class='text-justify attribute'><strong>Duration: </strong>3h 54 min</p>
</div> </div>
</div> </div>
</div> </div>
@ -714,15 +714,15 @@
<tr id="su" class="skipClass"> <tr id="su" class="errorClass">
<td>TestEditAdditionalBooks</td> <td>TestEditAdditionalBooks</td>
<td class="text-center">17</td> <td class="text-center">18</td>
<td class="text-center">15</td> <td class="text-center">15</td>
<td class="text-center">0</td> <td class="text-center">0</td>
<td class="text-center">0</td> <td class="text-center">1</td>
<td class="text-center">2</td> <td class="text-center">2</td>
<td class="text-center"> <td class="text-center">
<a onclick="showClassDetail('c10', 17)">Detail</a> <a onclick="showClassDetail('c10', 18)">Detail</a>
</td> </td>
</tr> </tr>
@ -809,7 +809,36 @@
<tr id='pt10.10' class='hiddenRow bg-success'> <tr id="et10.10" class="none bg-info">
<td>
<div class='testcase'>TestEditAdditionalBooks - test_upload_cbz_coverformats</div>
</td>
<td colspan='6'>
<div class="text-center">
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_et10.10')">ERROR</a>
</div>
<!--css div popup start-->
<div id="div_et10.10" class="popup_window test_output" style="display:block;">
<div class='close_button pull-right'>
<button type="button" class="close" aria-label="Close" onfocus="this.blur();"
onclick="document.getElementById('div_et10.10').style.display='none'"><span
aria-hidden="true">&times;</span></button>
</div>
<div class="text-left pull-left">
<pre class="text-left">Traceback (most recent call last):
File &#34;/home/ozzie/Development/calibre-web-test/test/test_edit_additional_books.py&#34;, line 59, in test_upload_cbz_coverformats
original_cover = self.check_element_on_page((By.ID, &#34;detailcover&#34;)).screenshot_as_png
AttributeError: &#39;bool&#39; object has no attribute &#39;screenshot_as_png&#39;</pre>
</div>
<div class="clearfix"></div>
</div>
<!--css div popup end-->
</td>
</tr>
<tr id='pt10.11' class='hiddenRow bg-success'>
<td> <td>
<div class='testcase'>TestEditAdditionalBooks - test_upload_edit_role</div> <div class='testcase'>TestEditAdditionalBooks - test_upload_edit_role</div>
</td> </td>
@ -818,7 +847,7 @@
<tr id='pt10.11' class='hiddenRow bg-success'> <tr id='pt10.12' class='hiddenRow bg-success'>
<td> <td>
<div class='testcase'>TestEditAdditionalBooks - test_upload_metadata_cbr</div> <div class='testcase'>TestEditAdditionalBooks - test_upload_metadata_cbr</div>
</td> </td>
@ -827,7 +856,7 @@
<tr id='pt10.12' class='hiddenRow bg-success'> <tr id='pt10.13' class='hiddenRow bg-success'>
<td> <td>
<div class='testcase'>TestEditAdditionalBooks - test_upload_metadata_cbt</div> <div class='testcase'>TestEditAdditionalBooks - test_upload_metadata_cbt</div>
</td> </td>
@ -836,19 +865,19 @@
<tr id="st10.13" class="none bg-warning"> <tr id="st10.14" class="none bg-warning">
<td> <td>
<div class='testcase'>TestEditAdditionalBooks - test_writeonly_calibre_database</div> <div class='testcase'>TestEditAdditionalBooks - test_writeonly_calibre_database</div>
</td> </td>
<td colspan='6'> <td colspan='6'>
<div class="text-center"> <div class="text-center">
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_st10.13')">SKIP</a> <a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_st10.14')">SKIP</a>
</div> </div>
<!--css div popup start--> <!--css div popup start-->
<div id="div_st10.13" class="popup_window test_output" style="display:none;"> <div id="div_st10.14" class="popup_window test_output" style="display:none;">
<div class='close_button pull-right'> <div class='close_button pull-right'>
<button type="button" class="close" aria-label="Close" onfocus="this.blur();" <button type="button" class="close" aria-label="Close" onfocus="this.blur();"
onclick="document.getElementById('div_st10.13').style.display='none'"><span onclick="document.getElementById('div_st10.14').style.display='none'"><span
aria-hidden="true">&times;</span></button> aria-hidden="true">&times;</span></button>
</div> </div>
<div class="text-left pull-left"> <div class="text-left pull-left">
@ -862,7 +891,7 @@
<tr id='pt10.14' class='hiddenRow bg-success'> <tr id='pt10.15' class='hiddenRow bg-success'>
<td> <td>
<div class='testcase'>TestEditAdditionalBooks - test_writeonly_path</div> <div class='testcase'>TestEditAdditionalBooks - test_writeonly_path</div>
</td> </td>
@ -871,7 +900,7 @@
<tr id='st10.15' class='none bg-warning'> <tr id='st10.16' class='none bg-warning'>
<td> <td>
<div class='testcase'>TestEditAdditionalBooks - test_xss_author_edit</div> <div class='testcase'>TestEditAdditionalBooks - test_xss_author_edit</div>
</td> </td>
@ -880,7 +909,7 @@
<tr id='pt10.16' class='hiddenRow bg-success'> <tr id='pt10.17' class='hiddenRow bg-success'>
<td> <td>
<div class='testcase'>TestEditAdditionalBooks - test_xss_comment_edit</div> <div class='testcase'>TestEditAdditionalBooks - test_xss_comment_edit</div>
</td> </td>
@ -889,7 +918,7 @@
<tr id='pt10.17' class='hiddenRow bg-success'> <tr id='pt10.18' class='hiddenRow bg-success'>
<td> <td>
<div class='testcase'>TestEditAdditionalBooks - test_xss_custom_comment_edit</div> <div class='testcase'>TestEditAdditionalBooks - test_xss_custom_comment_edit</div>
</td> </td>
@ -901,13 +930,13 @@
<tr id="su" class="skipClass"> <tr id="su" class="skipClass">
<td>TestEditBooks</td> <td>TestEditBooks</td>
<td class="text-center">36</td>
<td class="text-center">35</td> <td class="text-center">35</td>
<td class="text-center">34</td>
<td class="text-center">0</td> <td class="text-center">0</td>
<td class="text-center">0</td> <td class="text-center">0</td>
<td class="text-center">1</td> <td class="text-center">1</td>
<td class="text-center"> <td class="text-center">
<a onclick="showClassDetail('c11', 35)">Detail</a> <a onclick="showClassDetail('c11', 36)">Detail</a>
</td> </td>
</tr> </tr>
@ -1237,6 +1266,15 @@
<tr id='pt11.35' class='hiddenRow bg-success'> <tr id='pt11.35' class='hiddenRow bg-success'>
<td>
<div class='testcase'>TestEditBooks - test_upload_cbz_coverformats</div>
</td>
<td colspan='6' align='center'>PASS</td>
</tr>
<tr id='pt11.36' class='hiddenRow bg-success'>
<td> <td>
<div class='testcase'>TestEditBooks - test_upload_cover_hdd</div> <div class='testcase'>TestEditBooks - test_upload_cover_hdd</div>
</td> </td>
@ -1423,13 +1461,13 @@
<tr id="su" class="failClass"> <tr id="su" class="passClass">
<td>TestLoadMetadata</td> <td>TestLoadMetadata</td>
<td class="text-center">1</td> <td class="text-center">1</td>
<td class="text-center">0</td>
<td class="text-center">1</td> <td class="text-center">1</td>
<td class="text-center">0</td> <td class="text-center">0</td>
<td class="text-center">0</td> <td class="text-center">0</td>
<td class="text-center">0</td>
<td class="text-center"> <td class="text-center">
<a onclick="showClassDetail('c13', 1)">Detail</a> <a onclick="showClassDetail('c13', 1)">Detail</a>
</td> </td>
@ -1437,42 +1475,22 @@
<tr id="ft13.1" class="none bg-danger"> <tr id='pt13.1' class='hiddenRow bg-success'>
<td> <td>
<div class='testcase'>TestLoadMetadata - test_load_metadata</div> <div class='testcase'>TestLoadMetadata - test_load_metadata</div>
</td> </td>
<td colspan='6'> <td colspan='6' align='center'>PASS</td>
<div class="text-center">
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft13.1')">FAIL</a>
</div>
<!--css div popup start-->
<div id="div_ft13.1" class="popup_window test_output" style="display:block;">
<div class='close_button pull-right'>
<button type="button" class="close" aria-label="Close" onfocus="this.blur();"
onclick="document.getElementById('div_ft13.1').style.display='none'"><span
aria-hidden="true">&times;</span></button>
</div>
<div class="text-left pull-left">
<pre class="text-left">Traceback (most recent call last):
File &#34;/home/ozzie/Development/calibre-web-test/test/test_edit_books_metadata.py&#34;, line 136, in test_load_metadata
self.assertGreaterEqual(diff(BytesIO(cover), BytesIO(original_cover), delete_diff_file=True), 0.05)
AssertionError: 0.0 not greater than or equal to 0.05</pre>
</div>
<div class="clearfix"></div>
</div>
<!--css div popup end-->
</td>
</tr> </tr>
<tr id="su" class="errorClass"> <tr id="su" class="passClass">
<td>TestEditBooksOnGdrive</td> <td>TestEditBooksOnGdrive</td>
<td class="text-center">20</td> <td class="text-center">20</td>
<td class="text-center">19</td> <td class="text-center">20</td>
<td class="text-center">0</td>
<td class="text-center">0</td> <td class="text-center">0</td>
<td class="text-center">1</td>
<td class="text-center">0</td> <td class="text-center">0</td>
<td class="text-center"> <td class="text-center">
<a onclick="showClassDetail('c14', 20)">Detail</a> <a onclick="showClassDetail('c14', 20)">Detail</a>
@ -1616,31 +1634,11 @@ AssertionError: 0.0 not greater than or equal to 0.05</pre>
<tr id="et14.16" class="none bg-info"> <tr id='pt14.16' class='hiddenRow bg-success'>
<td> <td>
<div class='testcase'>TestEditBooksOnGdrive - test_edit_title</div> <div class='testcase'>TestEditBooksOnGdrive - test_edit_title</div>
</td> </td>
<td colspan='6'> <td colspan='6' align='center'>PASS</td>
<div class="text-center">
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_et14.16')">ERROR</a>
</div>
<!--css div popup start-->
<div id="div_et14.16" class="popup_window test_output" style="display:block;">
<div class='close_button pull-right'>
<button type="button" class="close" aria-label="Close" onfocus="this.blur();"
onclick="document.getElementById('div_et14.16').style.display='none'"><span
aria-hidden="true">&times;</span></button>
</div>
<div class="text-left pull-left">
<pre class="text-left">Traceback (most recent call last):
File &#34;/home/ozzie/Development/calibre-web-test/test/test_edit_ebooks_gdrive.py&#34;, line 245, in test_edit_title
self.assertEqual(ele.text, u&#39;Very long extra super turbo cool title without any issue of displaying including ö utf-8 characters&#39;)
AttributeError: &#39;bool&#39; object has no attribute &#39;text&#39;</pre>
</div>
<div class="clearfix"></div>
</div>
<!--css div popup end-->
</td>
</tr> </tr>
@ -2008,11 +2006,11 @@ AttributeError: &#39;bool&#39; object has no attribute &#39;text&#39;</pre>
<tr id="su" class="failClass"> <tr id="su" class="passClass">
<td>TestKoboSync</td> <td>TestKoboSync</td>
<td class="text-center">11</td> <td class="text-center">11</td>
<td class="text-center">10</td> <td class="text-center">11</td>
<td class="text-center">1</td> <td class="text-center">0</td>
<td class="text-center">0</td> <td class="text-center">0</td>
<td class="text-center">0</td> <td class="text-center">0</td>
<td class="text-center"> <td class="text-center">
@ -2094,31 +2092,11 @@ AttributeError: &#39;bool&#39; object has no attribute &#39;text&#39;</pre>
<tr id="ft23.9" class="none bg-danger"> <tr id='pt23.9' class='hiddenRow bg-success'>
<td> <td>
<div class='testcase'>TestKoboSync - test_sync_shelf</div> <div class='testcase'>TestKoboSync - test_sync_shelf</div>
</td> </td>
<td colspan='6'> <td colspan='6' align='center'>PASS</td>
<div class="text-center">
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft23.9')">FAIL</a>
</div>
<!--css div popup start-->
<div id="div_ft23.9" class="popup_window test_output" style="display:block;">
<div class='close_button pull-right'>
<button type="button" class="close" aria-label="Close" onfocus="this.blur();"
onclick="document.getElementById('div_ft23.9').style.display='none'"><span
aria-hidden="true">&times;</span></button>
</div>
<div class="text-left pull-left">
<pre class="text-left">Traceback (most recent call last):
File &#34;/home/ozzie/Development/calibre-web-test/test/test_kobo_sync.py&#34;, line 350, in test_sync_shelf
self.assertEqual(1, len(data), data)
AssertionError: 1 != 0 : []</pre>
</div>
<div class="clearfix"></div>
</div>
<!--css div popup end-->
</td>
</tr> </tr>
@ -3013,11 +2991,11 @@ AssertionError: 1 != 0 : []</pre>
<tr id="su" class="failClass"> <tr id="su" class="passClass">
<td>TestReader</td> <td>TestReader</td>
<td class="text-center">5</td> <td class="text-center">5</td>
<td class="text-center">4</td> <td class="text-center">5</td>
<td class="text-center">1</td> <td class="text-center">0</td>
<td class="text-center">0</td> <td class="text-center">0</td>
<td class="text-center">0</td> <td class="text-center">0</td>
<td class="text-center"> <td class="text-center">
@ -3054,37 +3032,11 @@ AssertionError: 1 != 0 : []</pre>
<tr id="ft33.4" class="none bg-danger"> <tr id='pt33.4' class='hiddenRow bg-success'>
<td> <td>
<div class='testcase'>TestReader - test_sound_listener</div> <div class='testcase'>TestReader - test_sound_listener</div>
</td> </td>
<td colspan='6'> <td colspan='6' align='center'>PASS</td>
<div class="text-center">
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft33.4')">FAIL</a>
</div>
<!--css div popup start-->
<div id="div_ft33.4" class="popup_window test_output" style="display:block;">
<div class='close_button pull-right'>
<button type="button" class="close" aria-label="Close" onfocus="this.blur();"
onclick="document.getElementById('div_ft33.4').style.display='none'"><span
aria-hidden="true">&times;</span></button>
</div>
<div class="text-left pull-left">
<pre class="text-left">Traceback (most recent call last):
File &#34;/home/ozzie/Development/calibre-web-test/test/test_reader.py&#34;, line 230, in test_sound_listener
self.sound_test(&#39;music.flac&#39;, &#39;Unknown - music&#39;, &#39;0:02&#39;)
File &#34;/home/ozzie/Development/calibre-web-test/test/test_reader.py&#34;, line 219, in sound_test
self.assertEqual(duration, duration_item.text)
AssertionError: &#39;0:02&#39; != &#39;0:01&#39;
- 0:02
? ^
+ 0:01
? ^</pre>
</div>
<div class="clearfix"></div>
</div>
<!--css div popup end-->
</td>
</tr> </tr>
@ -4427,9 +4379,9 @@ AssertionError: &#39;0:02&#39; != &#39;0:01&#39;
<tr id='total_row' class="text-center bg-grey"> <tr id='total_row' class="text-center bg-grey">
<td>Total</td> <td>Total</td>
<td>386</td> <td>388</td>
<td>374</td> <td>379</td>
<td>3</td> <td>0</td>
<td>1</td> <td>1</td>
<td>8</td> <td>8</td>
<td>&nbsp;</td> <td>&nbsp;</td>
@ -4459,7 +4411,7 @@ AssertionError: &#39;0:02&#39; != &#39;0:01&#39;
<tr> <tr>
<th>Platform</th> <th>Platform</th>
<td>Linux 5.13.0-25-generic #26~20.04.1-Ubuntu SMP Fri Jan 7 16:27:40 UTC 2022 x86_64 x86_64</td> <td>Linux 5.13.0-27-generic #29~20.04.1-Ubuntu SMP Fri Jan 14 00:32:30 UTC 2022 x86_64 x86_64</td>
<td>Basic</td> <td>Basic</td>
</tr> </tr>
@ -4561,7 +4513,7 @@ AssertionError: &#39;0:02&#39; != &#39;0:01&#39;
<tr> <tr>
<th>SQLAlchemy</th> <th>SQLAlchemy</th>
<td>1.4.29</td> <td>1.4.31</td>
<td>Basic</td> <td>Basic</td>
</tr> </tr>
@ -4591,7 +4543,7 @@ AssertionError: &#39;0:02&#39; != &#39;0:01&#39;
<tr> <tr>
<th>google-api-python-client</th> <th>google-api-python-client</th>
<td>2.35.0</td> <td>2.36.0</td>
<td>TestCliGdrivedb</td> <td>TestCliGdrivedb</td>
</tr> </tr>
@ -4621,7 +4573,7 @@ AssertionError: &#39;0:02&#39; != &#39;0:01&#39;
<tr> <tr>
<th>google-api-python-client</th> <th>google-api-python-client</th>
<td>2.35.0</td> <td>2.36.0</td>
<td>TestEbookConvertCalibreGDrive</td> <td>TestEbookConvertCalibreGDrive</td>
</tr> </tr>
@ -4651,7 +4603,7 @@ AssertionError: &#39;0:02&#39; != &#39;0:01&#39;
<tr> <tr>
<th>google-api-python-client</th> <th>google-api-python-client</th>
<td>2.35.0</td> <td>2.36.0</td>
<td>TestEbookConvertGDriveKepubify</td> <td>TestEbookConvertGDriveKepubify</td>
</tr> </tr>
@ -4681,7 +4633,7 @@ AssertionError: &#39;0:02&#39; != &#39;0:01&#39;
<tr> <tr>
<th>comicapi</th> <th>comicapi</th>
<td>2.2.0</td> <td>2.2.1</td>
<td>TestEditAdditionalBooks</td> <td>TestEditAdditionalBooks</td>
</tr> </tr>
@ -4693,7 +4645,7 @@ AssertionError: &#39;0:02&#39; != &#39;0:01&#39;
<tr> <tr>
<th>google-api-python-client</th> <th>google-api-python-client</th>
<td>2.35.0</td> <td>2.36.0</td>
<td>TestEditBooksOnGdrive</td> <td>TestEditBooksOnGdrive</td>
</tr> </tr>
@ -4729,7 +4681,7 @@ AssertionError: &#39;0:02&#39; != &#39;0:01&#39;
<tr> <tr>
<th>google-api-python-client</th> <th>google-api-python-client</th>
<td>2.35.0</td> <td>2.36.0</td>
<td>TestSetupGdrive</td> <td>TestSetupGdrive</td>
</tr> </tr>
@ -4819,7 +4771,7 @@ AssertionError: &#39;0:02&#39; != &#39;0:01&#39;
</div> </div>
<script> <script>
drawCircle(374, 3, 1, 8); drawCircle(379, 0, 1, 8);
showCase(5); showCase(5);
</script> </script>