Książka ma {0} stron(y).
" + PUBLISH_DATE_TEMPLATE = "Data pierwszego wydania: {0}
" + PUBLISH_DATE_PL_TEMPLATE = ( + "Data pierwszego wydania w Polsce: {0}
" + ) + + def __init__(self, root: HtmlElement, metadata: Metadata) -> None: + self.root = root + self.metadata = metadata + + def parse_search_results(self) -> List[MetaRecord]: + matches = [] + results = self.root.xpath(LubimyCzytac.BOOK_SEARCH_RESULT_XPATH) + for result in results: + title = self._parse_xpath_node( + root=result, + xpath=f"{LubimyCzytac.SINGLE_BOOK_RESULT_XPATH}" + f"{LubimyCzytac.TITLE_TEXT_PATH}", + ) + + book_url = self._parse_xpath_node( + root=result, + xpath=f"{LubimyCzytac.SINGLE_BOOK_RESULT_XPATH}" + f"{LubimyCzytac.URL_PATH}", + ) + authors = self._parse_xpath_node( + root=result, + xpath=f"{LubimyCzytac.SINGLE_BOOK_RESULT_XPATH}" + f"{LubimyCzytac.AUTHORS_PATH}", + take_first=False, + ) + if not all([title, book_url, authors]): + continue + matches.append( + MetaRecord( + id=book_url.replace(f"/ksiazka/", "").split("/")[0], + title=title, + authors=[strip_accents(author) for author in authors], + url=LubimyCzytac.BASE_URL + book_url, + source=MetaSourceInfo( + id=self.metadata.__id__, + description=self.metadata.__name__, + link=LubimyCzytac.BASE_URL, + ), + ) + ) + return matches + + def parse_single_book( + self, match: MetaRecord, generic_cover: str, locale: str + ) -> MetaRecord: + response = requests.get(match.url) + self.root = fromstring(response.text) + match.cover = self._parse_cover(generic_cover=generic_cover) + match.description = self._parse_description() + match.languages = self._parse_languages(locale=locale) + match.publisher = self._parse_publisher() + match.publishedDate = self._parse_from_summary(attribute_name="datePublished") + match.rating = self._parse_rating() + match.series, match.series_index = self._parse_series() + match.tags = self._parse_tags() + match.identifiers = { + "isbn": self._parse_isbn(), + "lubimyczytac": match.id, + } + return match + + def _parse_xpath_node( + self, + xpath: str, + root: HtmlElement = None, + take_first: bool = True, + strip_element: bool = True, + ) -> Optional[Union[str, List[str]]]: + root = root if root is not None else self.root + node = root.xpath(xpath) + if not node: + return None + return ( + (node[0].strip() if strip_element else node[0]) + if take_first + else [x.strip() for x in node] + ) + + def _parse_cover(self, generic_cover) -> Optional[str]: + return ( + self._parse_xpath_node(xpath=LubimyCzytac.COVER, take_first=True) + or generic_cover + ) + + def _parse_publisher(self) -> Optional[str]: + return self._parse_xpath_node(xpath=LubimyCzytac.PUBLISHER, take_first=True) + + def _parse_languages(self, locale: str) -> List[str]: + languages = list() + lang = self._parse_xpath_node(xpath=LubimyCzytac.LANGUAGES, take_first=True) + if lang: + if "polski" in lang: + languages.append("pol") + if "angielski" in lang: + languages.append("eng") + return [get_language_name(locale, language) for language in languages] + + def _parse_series(self) -> Tuple[Optional[str], Optional[Union[float, int]]]: + series_index = 0 + series = self._parse_xpath_node(xpath=LubimyCzytac.SERIES, take_first=True) + if series: + if "tom " in series: + series_name, series_info = series.split(" (tom ", 1) + series_info = series_info.replace(" ", "").replace(")", "") + # Check if book is not a bundle, i.e. chapter 1-3 + if "-" in series_info: + series_info = series_info.split("-", 1)[0] + if series_info.replace(".", "").isdigit() is True: + series_index = get_int_or_float(series_info) + return series_name, series_index + return None, None + + def _parse_tags(self) -> List[str]: + tags = self._parse_xpath_node(xpath=LubimyCzytac.TAGS, take_first=False) + return [ + strip_accents(w.replace(", itd.", " itd.")) + for w in tags + if isinstance(w, str) + ] + + def _parse_from_summary(self, attribute_name: str) -> Optional[str]: + value = None + summary_text = self._parse_xpath_node(xpath=LubimyCzytac.SUMMARY) + if summary_text: + data = json.loads(summary_text) + value = data.get(attribute_name) + return value.strip() if value is not None else value + + def _parse_rating(self) -> Optional[str]: + rating = self._parse_xpath_node(xpath=LubimyCzytac.RATING) + return round(float(rating.replace(",", ".")) / 2) if rating else rating + + def _parse_date(self, xpath="first_publish") -> Optional[datetime.datetime]: + options = { + "first_publish": LubimyCzytac.FIRST_PUBLISH_DATE, + "first_publish_pl": LubimyCzytac.FIRST_PUBLISH_DATE_PL, + } + date = self._parse_xpath_node(xpath=options.get(xpath)) + return parser.parse(date) if date else None + + def _parse_isbn(self) -> Optional[str]: + return self._parse_xpath_node(xpath=LubimyCzytac.ISBN) + + def _parse_description(self) -> str: + description = "" + description_node = self._parse_xpath_node( + xpath=LubimyCzytac.DESCRIPTION, strip_element=False + ) + if description_node is not None: + for source in self.root.xpath('//p[@class="source"]'): + source.getparent().remove(source) + description = tostring(description_node, method="html") + description = sanitize_comments_html(description) + + else: + description_node = self._parse_xpath_node(xpath=LubimyCzytac.META_TITLE) + if description_node is not None: + description = description_node + description = sanitize_comments_html(description) + description = self._add_extra_info_to_description(description=description) + return description + + def _add_extra_info_to_description(self, description: str) -> str: + pages = self._parse_from_summary(attribute_name="numberOfPages") + if pages: + description += LubimyCzytacParser.PAGES_TEMPLATE.format(pages) + + first_publish_date = self._parse_date() + if first_publish_date: + description += LubimyCzytacParser.PUBLISH_DATE_TEMPLATE.format( + first_publish_date.strftime("%d.%m.%Y") + ) + + first_publish_date_pl = self._parse_date(xpath="first_publish_pl") + if first_publish_date_pl: + description += LubimyCzytacParser.PUBLISH_DATE_PL_TEMPLATE.format( + first_publish_date_pl.strftime("%d.%m.%Y") + ) + + return description diff --git a/cps/metadata_provider/scholar.py b/cps/metadata_provider/scholar.py index df3878318..dc5368d8c 100644 --- a/cps/metadata_provider/scholar.py +++ b/cps/metadata_provider/scholar.py @@ -15,46 +15,52 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, seeStart Time: 2022-01-26 07:52:00
+Start Time: 2022-01-27 10:57:52
Stop Time: 2022-01-26 11:49:33
+Stop Time: 2022-01-27 14:58:14
Duration: 3h 16 min
+Duration: 3h 19 min
Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_ebook_convert.py", line 201, in test_convert_email - self.assertEqual(ret[-2]['result'], 'Finished') -IndexError: list index out of range-
Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_ebook_convert.py", line 245, in test_convert_failed_and_email - self.assertEqual(ret[-1]['result'], 'Failed') -AssertionError: 'Finished' != 'Failed' -- Finished -+ Failed-
Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_ebook_convert.py", line 164, in test_convert_parameter - self.assertEqual(ret[-1]['result'], 'Finished') -AssertionError: 'Failed' != 'Finished' -- Failed -+ Finished-
Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_ebook_convert.py", line 110, in test_convert_wrong_excecutable - self.assertTrue(self.check_element_on_page((By.ID, "flash_success"))) -AssertionError: False is not true-
Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_ebook_convert.py", line 519, in test_convert_xss - self.assertEqual(ret[-1]['result'], 'Finished') -AssertionError: 'Failed' != 'Finished' -- Failed -+ Finished-
Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_ebook_convert.py", line 441, in test_email_failed - self.assertTrue(self.check_element_on_page((By.ID, "flash_success"))) -AssertionError: False is not true-
Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_ebook_convert.py", line 412, in test_email_only - self.assertEqual(ret[-1]['result'], 'Finished') -AssertionError: 'Failed' != 'Finished' -- Failed -+ Finished-
Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_ebook_convert.py", line 553, in test_user_convert_xss - self.assertEqual(ret[-1]['result'], 'Finished') -AssertionError: 'Failed' != 'Finished' -- Failed -+ Finished-
Traceback (most recent call last):
- File "/home/ozzie/Development/calibre-web-test/test/test_ebook_convert_gdrive.py", line 194, in test_convert_email
- self.assertTrue("Convert" in ret[-2]['task'])
-AssertionError: False is not true
- Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_ebook_convert_gdrive.py", line 234, in test_convert_failed_and_email - self.assertEqual(ret[-1]['result'], 'Failed') -AssertionError: 'Finished' != 'Failed' -- Finished -+ Failed-
Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_ebook_convert_gdrive.py", line 158, in test_convert_parameter - self.assertEqual(ret[-1]['result'], 'Finished') -AssertionError: 'Failed' != 'Finished' -- Failed -+ Finished-
Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_ebook_convert_gdrive.py", line 421, in test_email_failed - self.assertTrue(self.check_element_on_page((By.ID, "flash_success"))) -AssertionError: False is not true-
Traceback (most recent call last): - File "/home/ozzie/Development/calibre-web-test/test/test_ebook_convert_gdrive.py", line 389, in test_email_only - self.assertEqual(ret[-1]['result'], 'Finished') -AssertionError: 'Failed' != 'Finished' -- Failed -+ Finished-
Traceback (most recent call last):
- File "/home/ozzie/Development/calibre-web-test/test/test_edit_additional_books.py", line 664, in test_upload_edit_role
- self.login('admin', 'admin123')
- File "/home/ozzie/Development/calibre-web-test/test/helper_ui.py", line 87, in login
- WebDriverWait(cls.driver, 5).until(EC.presence_of_element_located((By.ID, "username")))
- File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.8/site-packages/selenium/webdriver/support/wait.py", line 89, in until
- raise TimeoutException(message, screen, stacktrace)
-selenium.common.exceptions.TimeoutException: Message:
-Stacktrace:
-WebDriverError@chrome://remote/content/shared/webdriver/Errors.jsm:183:5
-NoSuchElementError@chrome://remote/content/shared/webdriver/Errors.jsm:395:5
-element.find/</<@chrome://remote/content/marionette/element.js:300:16
- Traceback (most recent call last):
- File "/home/ozzie/Development/calibre-web-test/test/test_edit_additional_books.py", line 148, in test_upload_metadata_cbr
- self.fill_basic_config({'config_uploading': 1})
- File "/home/ozzie/Development/calibre-web-test/test/helper_ui.py", line 358, in fill_basic_config
- cls._fill_basic_config(elements)
- File "/home/ozzie/Development/calibre-web-test/test/helper_ui.py", line 268, in _fill_basic_config
- WebDriverWait(cls.driver, 5).until(EC.presence_of_element_located((By.ID, "config_port")))
- File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.8/site-packages/selenium/webdriver/support/wait.py", line 89, in until
- raise TimeoutException(message, screen, stacktrace)
-selenium.common.exceptions.TimeoutException: Message:
-Stacktrace:
-WebDriverError@chrome://remote/content/shared/webdriver/Errors.jsm:183:5
-NoSuchElementError@chrome://remote/content/shared/webdriver/Errors.jsm:395:5
-element.find/</<@chrome://remote/content/marionette/element.js:300:16
- Traceback (most recent call last):
- File "/home/ozzie/Development/calibre-web-test/test/test_edit_additional_books.py", line 185, in test_upload_metadata_cbt
- self.fill_basic_config({'config_uploading': 1})
- File "/home/ozzie/Development/calibre-web-test/test/helper_ui.py", line 358, in fill_basic_config
- cls._fill_basic_config(elements)
- File "/home/ozzie/Development/calibre-web-test/test/helper_ui.py", line 268, in _fill_basic_config
- WebDriverWait(cls.driver, 5).until(EC.presence_of_element_located((By.ID, "config_port")))
- File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.8/site-packages/selenium/webdriver/support/wait.py", line 89, in until
- raise TimeoutException(message, screen, stacktrace)
-selenium.common.exceptions.TimeoutException: Message:
-Stacktrace:
-WebDriverError@chrome://remote/content/shared/webdriver/Errors.jsm:183:5
-NoSuchElementError@chrome://remote/content/shared/webdriver/Errors.jsm:395:5
-element.find/</<@chrome://remote/content/marionette/element.js:300:16
- Traceback (most recent call last):
- File "/home/ozzie/Development/calibre-web-test/test/test_edit_additional_books.py", line 344, in test_writeonly_path
- self.fill_basic_config({'config_rarfile_location': unrar_path(), "config_unicode_filename": 1})
- File "/home/ozzie/Development/calibre-web-test/test/helper_ui.py", line 358, in fill_basic_config
- cls._fill_basic_config(elements)
- File "/home/ozzie/Development/calibre-web-test/test/helper_ui.py", line 268, in _fill_basic_config
- WebDriverWait(cls.driver, 5).until(EC.presence_of_element_located((By.ID, "config_port")))
- File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.8/site-packages/selenium/webdriver/support/wait.py", line 89, in until
- raise TimeoutException(message, screen, stacktrace)
-selenium.common.exceptions.TimeoutException: Message:
-Stacktrace:
-WebDriverError@chrome://remote/content/shared/webdriver/Errors.jsm:183:5
-NoSuchElementError@chrome://remote/content/shared/webdriver/Errors.jsm:395:5
-element.find/</<@chrome://remote/content/marionette/element.js:300:16
- Traceback (most recent call last):
- File "/home/ozzie/Development/calibre-web-test/test/test_edit_additional_books.py", line 44, in tearDownClass
- cls.stop_calibre_web()
- File "/home/ozzie/Development/calibre-web-test/test/helper_ui.py", line 446, in stop_calibre_web
- cls.driver.find_element(By.ID, 'admin_stop').click()
- File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py", line 1244, in find_element
- return self.execute(Command.FIND_ELEMENT, {
- File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py", line 424, in execute
- self.error_handler.check_response(response)
- File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.8/site-packages/selenium/webdriver/remote/errorhandler.py", line 247, in check_response
- raise exception_class(message, screen, stacktrace)
-selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: [id="admin_stop"]
-Stacktrace:
-WebDriverError@chrome://remote/content/shared/webdriver/Errors.jsm:183:5
-NoSuchElementError@chrome://remote/content/shared/webdriver/Errors.jsm:395:5
-element.find/</<@chrome://remote/content/marionette/element.js:300:16
-