mirror of https://gitlab.com/bashrc2/epicyon
				
				
				
			
		
			
				
	
	
		
			678 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			678 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Python
		
	
	
| __filename__ = "reading.py"
 | |
| __author__ = "Bob Mottram"
 | |
| __license__ = "AGPL3+"
 | |
| __version__ = "1.5.0"
 | |
| __maintainer__ = "Bob Mottram"
 | |
| __email__ = "bob@libreserver.org"
 | |
| __status__ = "Production"
 | |
| __module_group__ = "Core"
 | |
| 
 | |
| 
 | |
| import os
 | |
| from collections import OrderedDict
 | |
| from utils import get_content_from_post
 | |
| from utils import has_object_dict
 | |
| from utils import remove_id_ending
 | |
| from utils import get_attributed_to
 | |
| from utils import load_json
 | |
| from utils import save_json
 | |
| from utils import remove_html
 | |
| from utils import get_image_extensions
 | |
| from utils import date_epoch
 | |
| from utils import date_from_string_format
 | |
| 
 | |
| 
 | |
| def get_book_link_from_content(content: str) -> str:
 | |
|     """ Returns a book link from the given content
 | |
|     """
 | |
|     if '://' not in content or \
 | |
|        '"' not in content:
 | |
|         return None
 | |
|     sections = content.split('://')
 | |
|     if '"' not in sections[0] or '"' not in sections[1]:
 | |
|         return None
 | |
|     previous_str = sections[0].split('"')[-1]
 | |
|     next_str = sections[1].split('"')[0]
 | |
|     book_url = previous_str + '://' + next_str
 | |
|     return book_url
 | |
| 
 | |
| 
 | |
| def get_book_from_post(post_json_object: {}, debug: bool) -> {}:
 | |
|     """ Returns a book details from the given post
 | |
|     """
 | |
|     if 'tag' not in post_json_object:
 | |
|         if debug:
 | |
|             print('DEBUG: get_book_from_post no tag in post')
 | |
|         return {}
 | |
|     if not isinstance(post_json_object['tag'], list):
 | |
|         if debug:
 | |
|             print('DEBUG: get_book_from_post tag is not a list')
 | |
|         return {}
 | |
|     for tag_dict in post_json_object['tag']:
 | |
|         if 'type' not in tag_dict:
 | |
|             continue
 | |
|         if not isinstance(tag_dict['type'], str):
 | |
|             continue
 | |
|         if tag_dict['type'] != 'Edition':
 | |
|             continue
 | |
|         if not tag_dict.get('href'):
 | |
|             continue
 | |
|         if not isinstance(tag_dict['href'], str):
 | |
|             continue
 | |
|         if not tag_dict.get('name'):
 | |
|             continue
 | |
|         if not isinstance(tag_dict['name'], str):
 | |
|             continue
 | |
|         tag_dict['name'] = tag_dict['name'].replace('@', '')
 | |
|         return tag_dict.copy()
 | |
|     return {}
 | |
| 
 | |
| 
 | |
| def _get_book_image_from_post(post_json_object: {}) -> str:
 | |
|     """ Returns a book image from the given post
 | |
|     """
 | |
|     if 'attachment' not in post_json_object:
 | |
|         return ''
 | |
|     if not isinstance(post_json_object['attachment'], list):
 | |
|         return ''
 | |
|     extensions = get_image_extensions()
 | |
|     for attach_dict in post_json_object['attachment']:
 | |
|         if not isinstance(attach_dict, dict):
 | |
|             continue
 | |
|         if 'url' not in attach_dict:
 | |
|             continue
 | |
|         if not isinstance(attach_dict['url'], str):
 | |
|             continue
 | |
|         for ext in extensions:
 | |
|             if attach_dict['url'].endswith('.' + ext):
 | |
|                 return attach_dict['url']
 | |
|     return ''
 | |
| 
 | |
| 
 | |
| def has_edition_tag(post_json_object: {}) -> bool:
 | |
|     """Checks whether the given post has an Edition tag
 | |
|     indicating that it contains a book event
 | |
|     """
 | |
|     post_obj = post_json_object
 | |
|     if has_object_dict(post_json_object):
 | |
|         post_obj = post_json_object['object']
 | |
| 
 | |
|     if not post_obj.get('tag'):
 | |
|         return False
 | |
|     if not isinstance(post_obj['tag'], list):
 | |
|         return False
 | |
|     for tag in post_obj['tag']:
 | |
|         if not isinstance(tag, dict):
 | |
|             continue
 | |
|         if not tag.get('type'):
 | |
|             continue
 | |
|         if not isinstance(tag['type'], str):
 | |
|             continue
 | |
|         if tag['type'] == 'Edition':
 | |
|             return True
 | |
|     return False
 | |
| 
 | |
| 
 | |
| def get_reading_status(post_json_object: {},
 | |
|                        system_language: str,
 | |
|                        languages_understood: [],
 | |
|                        translate: {},
 | |
|                        debug: bool) -> {}:
 | |
|     """Returns any reading status from the content of a post
 | |
|     """
 | |
|     post_obj = post_json_object
 | |
|     if has_object_dict(post_json_object):
 | |
|         post_obj = post_json_object['object']
 | |
| 
 | |
|     content = get_content_from_post(post_json_object, system_language,
 | |
|                                     languages_understood,
 | |
|                                     "content")
 | |
|     if not content:
 | |
|         if debug:
 | |
|             print('DEBUG: get_reading_status no content')
 | |
|         return {}
 | |
|     book_url = get_book_link_from_content(content)
 | |
|     if not book_url:
 | |
|         if debug:
 | |
|             print('DEBUG: get_reading_status no book url')
 | |
|         return {}
 | |
| 
 | |
|     if not post_obj.get('id'):
 | |
|         if debug:
 | |
|             print('DEBUG: get_reading_status no id')
 | |
|         return {}
 | |
|     if not isinstance(post_obj['id'], str):
 | |
|         if debug:
 | |
|             print('DEBUG: get_reading_status id is not a string')
 | |
|         return {}
 | |
| 
 | |
|     # get the published date
 | |
|     if not post_obj.get('published'):
 | |
|         if debug:
 | |
|             print('DEBUG: get_reading_status no published')
 | |
|         return {}
 | |
|     if not isinstance(post_obj['published'], str):
 | |
|         if debug:
 | |
|             print('DEBUG: get_reading_status published is not a string')
 | |
|         return {}
 | |
|     published = post_obj['published']
 | |
|     if post_obj.get('updated'):
 | |
|         if isinstance(post_obj['updated'], str):
 | |
|             published = post_obj['updated']
 | |
| 
 | |
|     if not post_obj.get('attributedTo'):
 | |
|         if debug:
 | |
|             print('DEBUG: get_reading_status no attributedTo')
 | |
|         return {}
 | |
|     actor = get_attributed_to(post_obj['attributedTo'])
 | |
|     if not actor:
 | |
|         if debug:
 | |
|             print('DEBUG: get_reading_status no actor')
 | |
|         return {}
 | |
| 
 | |
|     book_image_url = _get_book_image_from_post(post_obj)
 | |
| 
 | |
|     # rating of a book
 | |
|     if post_obj.get('rating'):
 | |
|         rating = post_obj['rating']
 | |
|         if isinstance(rating, (float, int)):
 | |
|             translated_str = 'rated'
 | |
|             if translate.get('rated'):
 | |
|                 translated_str = translate['rated']
 | |
|             if translated_str in content or \
 | |
|                'rated' in content:
 | |
|                 book_dict = {
 | |
|                     'id': remove_id_ending(post_obj['id']),
 | |
|                     'actor': actor,
 | |
|                     'type': 'rated',
 | |
|                     'href': book_url,
 | |
|                     'rating': rating,
 | |
|                     'published': published
 | |
|                 }
 | |
|                 if book_image_url:
 | |
|                     book_dict['image_url'] = book_image_url
 | |
|                 return book_dict
 | |
| 
 | |
|     if not has_edition_tag(post_json_object):
 | |
|         return {}
 | |
| 
 | |
|     # get the book details from a post tag
 | |
|     book_dict = get_book_from_post(post_obj, debug)
 | |
|     if not book_dict:
 | |
|         if debug:
 | |
|             print('DEBUG: get_reading_status no book_dict ' +
 | |
|                   str(post_json_object))
 | |
|         return {}
 | |
| 
 | |
|     # want to read a book
 | |
|     translated_str = 'wants to read'
 | |
|     if translate.get('wants to read'):
 | |
|         translated_str = translate['wants to read']
 | |
|     if translated_str in content or \
 | |
|        'wants to read' in content:
 | |
|         book_dict['id'] = remove_id_ending(post_obj['id'])
 | |
|         book_dict['actor'] = actor
 | |
|         book_dict['type'] = 'want'
 | |
|         book_dict['published'] = published
 | |
|         if book_image_url:
 | |
|             book_dict['image_url'] = book_image_url
 | |
|         return book_dict
 | |
| 
 | |
|     translated_str = 'finished reading'
 | |
|     if translate.get('finished reading'):
 | |
|         translated_str = translate['finished reading']
 | |
|     if translated_str in content or \
 | |
|        'finished reading' in content:
 | |
|         book_dict['id'] = remove_id_ending(post_obj['id'])
 | |
|         book_dict['actor'] = actor
 | |
|         book_dict['type'] = 'finished'
 | |
|         book_dict['published'] = published
 | |
|         if book_image_url:
 | |
|             book_dict['image_url'] = book_image_url
 | |
|         return book_dict
 | |
| 
 | |
|     translated_str = 'am reading'
 | |
|     if translate.get('am reading'):
 | |
|         translated_str = translate['am reading']
 | |
|     if translated_str in content or \
 | |
|        'am reading' in content or \
 | |
|        'currently reading' in content or \
 | |
|        'is reading' in content:
 | |
|         book_dict['id'] = remove_id_ending(post_obj['id'])
 | |
|         book_dict['actor'] = actor
 | |
|         book_dict['type'] = 'reading'
 | |
|         book_dict['published'] = published
 | |
|         if book_image_url:
 | |
|             book_dict['image_url'] = book_image_url
 | |
|         return book_dict
 | |
| 
 | |
|     return {}
 | |
| 
 | |
| 
 | |
| def remove_reading_event(base_dir: str,
 | |
|                          actor: str, post_secs_since_epoch: str,
 | |
|                          book_event_type: str,
 | |
|                          books_cache: {},
 | |
|                          debug: bool) -> bool:
 | |
|     """Removes a reading status for the given actor
 | |
|     """
 | |
|     if not book_event_type:
 | |
|         print('remove_reading_event no book event')
 | |
|         return False
 | |
|     reading_path = base_dir + '/accounts/reading'
 | |
|     readers_path = reading_path + '/readers'
 | |
|     reader_books_filename = \
 | |
|         readers_path + '/' + actor.replace('/', '#') + '.json'
 | |
| 
 | |
|     reader_books_json = {}
 | |
|     if 'readers' not in books_cache:
 | |
|         books_cache['readers'] = {}
 | |
|     if books_cache['readers'].get(actor):
 | |
|         reader_books_json = books_cache['readers'][actor]
 | |
|     elif os.path.isfile(reader_books_filename):
 | |
|         # if not in cache then load from file
 | |
|         reader_books_json = load_json(reader_books_filename)
 | |
|     if not reader_books_json:
 | |
|         if debug:
 | |
|             print('remove_reading_event reader_books_json does not exist')
 | |
|         return False
 | |
|     if not reader_books_json.get('timeline'):
 | |
|         if debug:
 | |
|             print('remove_reading_event ' +
 | |
|                   'reader_books_json timeline does not exist')
 | |
|         return False
 | |
|     if not reader_books_json['timeline'].get(post_secs_since_epoch):
 | |
|         if debug:
 | |
|             print('remove_reading_event ' +
 | |
|                   'reader_books_json timeline event does not exist ' +
 | |
|                   str(post_secs_since_epoch))
 | |
|         return False
 | |
|     book_url = reader_books_json['timeline'][post_secs_since_epoch]
 | |
|     if not book_url:
 | |
|         if debug:
 | |
|             print('remove_reading_event no book_url')
 | |
|         return False
 | |
|     if not reader_books_json.get(book_url):
 | |
|         if debug:
 | |
|             print('remove_reading_event ' +
 | |
|                   'book_url not found in reader_books_json ' + book_url)
 | |
|         return False
 | |
|     if not reader_books_json[book_url].get(book_event_type):
 | |
|         if debug:
 | |
|             print('remove_reading_event ' +
 | |
|                   'book event not found in reader_books_json ' +
 | |
|                   book_url + ' ' + book_event_type)
 | |
|         return False
 | |
|     del reader_books_json[book_url][book_event_type]
 | |
|     if not save_json(reader_books_json, reader_books_filename):
 | |
|         if debug:
 | |
|             print('DEBUG: ' +
 | |
|                   'remove_reading_event unable to save reader book event')
 | |
|         return False
 | |
|     print('reading status removed by ' + actor)
 | |
|     return True
 | |
| 
 | |
| 
 | |
| def _add_book_to_reader(reader_books_json: {}, book_dict: {},
 | |
|                         debug: bool) -> bool:
 | |
|     """Updates reader books
 | |
|     """
 | |
|     if not book_dict.get('published'):
 | |
|         if debug:
 | |
|             print('_add_book_to_reader no published field')
 | |
|         return False
 | |
|     book_url = book_dict['href']
 | |
|     book_event_type = book_dict['type']
 | |
|     if not reader_books_json.get(book_url):
 | |
|         reader_books_json[book_url] = {}
 | |
|         if debug:
 | |
|             print('_add_book_to_reader first book')
 | |
|     else:
 | |
|         # has this book event already been stored?
 | |
|         if reader_books_json[book_url].get(book_event_type):
 | |
|             prev_book_dict = reader_books_json[book_url][book_event_type]
 | |
|             if book_dict.get('updated'):
 | |
|                 if prev_book_dict.get('updated'):
 | |
|                     if prev_book_dict['updated'] == book_dict['updated']:
 | |
|                         if debug:
 | |
|                             print('_add_book_to_reader ' +
 | |
|                                   'updated date already seen')
 | |
|                         return False
 | |
|                 else:
 | |
|                     if prev_book_dict['published'] == book_dict['updated']:
 | |
|                         if debug:
 | |
|                             print('_add_book_to_reader ' +
 | |
|                                   'published date already seen')
 | |
|                         return False
 | |
|             if prev_book_dict['published'] == book_dict['published']:
 | |
| 
 | |
|                 return False
 | |
|     # store the book event
 | |
|     reader_books_json[book_url][book_event_type] = book_dict
 | |
|     if 'timeline' not in reader_books_json:
 | |
|         reader_books_json['timeline'] = {}
 | |
|     published = book_dict['published']
 | |
|     if book_dict.get('updated'):
 | |
|         published = book_dict['updated']
 | |
|     if '.' in published:
 | |
|         published = published.split('.')[0] + 'Z'
 | |
|     post_time_object = \
 | |
|         date_from_string_format(published, ["%Y-%m-%dT%H:%M:%S%z",
 | |
|                                             "%Y-%m-%dT%H:%M:%S%Z"])
 | |
|     if post_time_object:
 | |
|         baseline_time = date_epoch()
 | |
|         days_diff = post_time_object - baseline_time
 | |
|         post_secs_since_epoch = days_diff.total_seconds()
 | |
|         reader_books_json['timeline'][post_secs_since_epoch] = book_url
 | |
|         return True
 | |
|     elif debug:
 | |
|         print('_add_book_to_reader published date not recognised ' + published)
 | |
|     return False
 | |
| 
 | |
| 
 | |
| def _add_reader_to_book(book_json: {}, book_dict: {}) -> None:
 | |
|     """Updates book with a new reader
 | |
|     """
 | |
|     book_event_type = book_dict['type']
 | |
|     actor = book_dict['actor']
 | |
|     if not book_json.get(actor):
 | |
|         book_json[actor] = {
 | |
|             book_event_type: book_dict
 | |
|         }
 | |
|         if book_dict.get('name'):
 | |
|             book_json['title'] = remove_html(book_dict['name'])
 | |
|         return
 | |
|     book_json[actor][book_event_type] = book_dict
 | |
|     if book_dict.get('name'):
 | |
|         book_json['title'] = remove_html(book_dict['name'])
 | |
| 
 | |
| 
 | |
| def _update_recent_books_list(base_dir: str, book_id: str,
 | |
|                               debug: bool) -> None:
 | |
|     """prepend a book to the recent books list
 | |
|     """
 | |
|     recent_books_filename = base_dir + '/accounts/recent_books.txt'
 | |
|     if os.path.isfile(recent_books_filename):
 | |
|         try:
 | |
|             with open(recent_books_filename, 'r+',
 | |
|                       encoding='utf-8') as recent_file:
 | |
|                 content = recent_file.read()
 | |
|                 if book_id + '\n' not in content:
 | |
|                     recent_file.seek(0, 0)
 | |
|                     recent_file.write(book_id + '\n' + content)
 | |
|                     if debug:
 | |
|                         print('DEBUG: recent book added')
 | |
|         except OSError as ex:
 | |
|             print('WARN: Failed to write entry to recent books ' +
 | |
|                   recent_books_filename + ' ' + str(ex))
 | |
|     else:
 | |
|         try:
 | |
|             with open(recent_books_filename, 'w+',
 | |
|                       encoding='utf-8') as recent_file:
 | |
|                 recent_file.write(book_id + '\n')
 | |
|         except OSError:
 | |
|             print('EX: unable to write recent books ' +
 | |
|                   recent_books_filename)
 | |
| 
 | |
| 
 | |
| def _deduplicate_recent_books_list(base_dir: str,
 | |
|                                    max_recent_books: int) -> None:
 | |
|     """ Deduplicate and limit the length of the recent books list
 | |
|     """
 | |
|     recent_books_filename = base_dir + '/accounts/recent_books.txt'
 | |
|     if not os.path.isfile(recent_books_filename):
 | |
|         return
 | |
| 
 | |
|     # load recent books as a list
 | |
|     recent_lines = []
 | |
|     try:
 | |
|         with open(recent_books_filename, 'r',
 | |
|                   encoding='utf-8') as recent_file:
 | |
|             recent_lines = recent_file.read().split('\n')
 | |
|     except OSError as ex:
 | |
|         print('WARN: Failed to read recent books trim ' +
 | |
|               recent_books_filename + ' ' + str(ex))
 | |
| 
 | |
|     # deduplicate the list
 | |
|     new_recent_lines = []
 | |
|     for line in recent_lines:
 | |
|         if line not in new_recent_lines:
 | |
|             new_recent_lines.append(line)
 | |
|     if len(new_recent_lines) < len(recent_lines):
 | |
|         recent_lines = new_recent_lines
 | |
|         result = ''
 | |
|         for line in recent_lines:
 | |
|             result += line + '\n'
 | |
|         try:
 | |
|             with open(recent_books_filename, 'w+',
 | |
|                       encoding='utf-8') as recent_file:
 | |
|                 recent_file.write(result)
 | |
|         except OSError:
 | |
|             print('EX: unable to deduplicate recent books ' +
 | |
|                   recent_books_filename)
 | |
| 
 | |
|     # remove excess lines from the list
 | |
|     if len(recent_lines) > max_recent_books:
 | |
|         result = ''
 | |
|         for ctr in range(max_recent_books):
 | |
|             result += recent_lines[ctr] + '\n'
 | |
|         try:
 | |
|             with open(recent_books_filename, 'w+',
 | |
|                       encoding='utf-8') as recent_file:
 | |
|                 recent_file.write(result)
 | |
|         except OSError:
 | |
|             print('EX: unable to trim recent books ' +
 | |
|                   recent_books_filename)
 | |
| 
 | |
| 
 | |
| def store_book_events(base_dir: str,
 | |
|                       post_json_object: {},
 | |
|                       system_language: str,
 | |
|                       languages_understood: [],
 | |
|                       translate: {},
 | |
|                       debug: bool,
 | |
|                       max_recent_books: int,
 | |
|                       books_cache: {},
 | |
|                       max_cached_readers: int) -> bool:
 | |
|     """Saves book events to file under accounts/reading/books
 | |
|     and accounts/reading/readers
 | |
|     """
 | |
|     book_dict = get_reading_status(post_json_object,
 | |
|                                    system_language,
 | |
|                                    languages_understood,
 | |
|                                    translate, debug)
 | |
|     if not book_dict:
 | |
|         if debug:
 | |
|             print('DEBUG: no book event')
 | |
|         return False
 | |
|     reading_path = base_dir + '/accounts/reading'
 | |
|     if not os.path.isdir(base_dir + '/accounts'):
 | |
|         os.mkdir(base_dir + '/accounts')
 | |
|     if not os.path.isdir(reading_path):
 | |
|         os.mkdir(reading_path)
 | |
|     books_path = reading_path + '/books'
 | |
|     if not os.path.isdir(books_path):
 | |
|         os.mkdir(books_path)
 | |
|     readers_path = reading_path + '/readers'
 | |
|     if not os.path.isdir(readers_path):
 | |
|         os.mkdir(readers_path)
 | |
| 
 | |
|     actor = book_dict['actor']
 | |
|     book_url = remove_id_ending(book_dict['href'])
 | |
| 
 | |
|     reader_books_filename = \
 | |
|         readers_path + '/' + actor.replace('/', '#') + '.json'
 | |
|     if debug:
 | |
|         print('reader_books_filename: ' + reader_books_filename)
 | |
|     reader_books_json = {}
 | |
| 
 | |
|     # get the reader from cache if possible
 | |
|     if 'readers' not in books_cache:
 | |
|         books_cache['readers'] = {}
 | |
|     if books_cache['readers'].get(actor):
 | |
|         reader_books_json = books_cache['readers'][actor]
 | |
|     elif os.path.isfile(reader_books_filename):
 | |
|         # if not in cache then load from file
 | |
|         reader_books_json = load_json(reader_books_filename)
 | |
|     if _add_book_to_reader(reader_books_json, book_dict, debug):
 | |
|         if not save_json(reader_books_json, reader_books_filename):
 | |
|             if debug:
 | |
|                 print('DEBUG: unable to save reader book event')
 | |
|             return False
 | |
| 
 | |
|         # update the cache for this reader
 | |
|         books_cache['readers'][actor] = reader_books_json
 | |
|         if 'reader_list' not in books_cache:
 | |
|             books_cache['reader_list'] = []
 | |
|         if actor in books_cache['reader_list']:
 | |
|             books_cache['reader_list'].remove(actor)
 | |
|         books_cache['reader_list'].append(actor)
 | |
|         # avoid too much caching
 | |
|         if len(books_cache['reader_list']) > max_cached_readers:
 | |
|             first_actor = books_cache['reader_list'][0]
 | |
|             books_cache['reader_list'].remove(first_actor)
 | |
|             del books_cache['readers'][actor]
 | |
|     elif debug:
 | |
|         print('_add_book_to_reader failed ' + str(book_dict))
 | |
| 
 | |
|     book_id = book_url.replace('/', '#')
 | |
|     book_filename = books_path + '/' + book_id + '.json'
 | |
|     book_json = {}
 | |
|     if os.path.isfile(book_filename):
 | |
|         book_json = load_json(book_filename)
 | |
|     _add_reader_to_book(book_json, book_dict)
 | |
|     if not save_json(book_json, book_filename):
 | |
|         if debug:
 | |
|             print('DEBUG: unable to save book reader')
 | |
|         return False
 | |
| 
 | |
|     _update_recent_books_list(base_dir, book_id, debug)
 | |
|     _deduplicate_recent_books_list(base_dir, max_recent_books)
 | |
| 
 | |
|     return True
 | |
| 
 | |
| 
 | |
| def html_profile_book_list(base_dir: str, actor: str, no_of_books: int,
 | |
|                            translate: {},
 | |
|                            nickname: str, domain: str,
 | |
|                            authorized: bool) -> str:
 | |
|     """Returns html for displaying a list of books on a profile screen
 | |
|     """
 | |
|     reading_path = base_dir + '/accounts/reading'
 | |
|     readers_path = reading_path + '/readers'
 | |
|     reader_books_filename = \
 | |
|         readers_path + '/' + actor.replace('/', '#') + '.json'
 | |
|     reader_books_json = {}
 | |
|     if not os.path.isfile(reader_books_filename):
 | |
|         return ''
 | |
|     reader_books_json = load_json(reader_books_filename)
 | |
|     if not reader_books_json.get('timeline'):
 | |
|         return ''
 | |
|     # sort the timeline in descending order
 | |
|     recent_books_json = \
 | |
|         OrderedDict(sorted(reader_books_json['timeline'].items(),
 | |
|                            reverse=True))
 | |
|     html_str = '<div class="book_list_section">\n'
 | |
|     html_str += '  <ul class="book_list">\n'
 | |
|     ctr = 0
 | |
|     for published_time_sec, book_url in recent_books_json.items():
 | |
|         if not reader_books_json.get(book_url):
 | |
|             continue
 | |
|         book_rating = None
 | |
|         book_wanted = False
 | |
|         book_reading = False
 | |
|         book_finished = False
 | |
|         book_event_type = ''
 | |
|         for event_type in ('want', 'finished', 'rated'):
 | |
|             if not reader_books_json[book_url].get(event_type):
 | |
|                 continue
 | |
|             book_dict = reader_books_json[book_url][event_type]
 | |
|             if book_dict.get('name'):
 | |
|                 book_title = book_dict['name']
 | |
|             if book_dict.get('image_url'):
 | |
|                 book_image_url = book_dict['image_url']
 | |
|             if event_type == 'rated':
 | |
|                 book_rating = book_dict['rating']
 | |
|                 book_event_type = event_type
 | |
|             elif event_type == 'want':
 | |
|                 book_wanted = True
 | |
|                 book_event_type = event_type
 | |
|             elif event_type == 'reading':
 | |
|                 book_reading = True
 | |
|                 book_event_type = event_type
 | |
|             elif event_type == 'finished':
 | |
|                 book_finished = True
 | |
|                 book_event_type = event_type
 | |
|         if book_title:
 | |
|             book_title = remove_html(book_title)
 | |
|             html_str += '    <li class="book_event">\n'
 | |
|             html_str += '      <span class="book_span">\n'
 | |
|             html_str += '        <div class="book_span_div">\n'
 | |
| 
 | |
|             # book image
 | |
|             if book_image_url:
 | |
|                 html_str += '          <a href="' + book_url + \
 | |
|                     '" target="_blank" rel="nofollow noopener noreferrer">\n'
 | |
|                 html_str += '            <div class="book_image_div">\n'
 | |
|                 html_str += '              <img src="' + \
 | |
|                     book_image_url + '" ' + \
 | |
|                     'alt="' + book_title + '">\n'
 | |
|                 html_str += '            </div>\n'
 | |
|                 html_str += '          </a>\n'
 | |
| 
 | |
|             # book details
 | |
|             html_str += '          <div class="book_details_div">\n'
 | |
|             html_str += '            <a href="' + book_url + \
 | |
|                 '" target="_blank" rel="nofollow noopener noreferrer">\n'
 | |
|             html_str += '              <b>' + book_title.title() + '</b></a>\n'
 | |
|             if book_finished:
 | |
|                 html_str += '            <br>' + \
 | |
|                     translate['finished reading'].title() + '\n'
 | |
|             if book_wanted:
 | |
|                 html_str += '            <br>' + \
 | |
|                     translate['Wanted'] + '\n'
 | |
|             if book_reading:
 | |
|                 html_str += '            <br>' + \
 | |
|                     translate['reading'].title() + '\n'
 | |
|             # book star rating
 | |
|             if book_rating is not None:
 | |
|                 html_str += '            <br>'
 | |
|                 for _ in range(int(book_rating)):
 | |
|                     html_str += '⭐'
 | |
|                 html_str += ' (' + str(book_rating) + ')\n'
 | |
|             # remove button
 | |
|             if authorized:
 | |
|                 if actor.endswith('/users/' + nickname) and \
 | |
|                    '://' + domain in actor:
 | |
|                     html_str += \
 | |
|                         '            <br>\n' + \
 | |
|                         '            <form method="POST" action="' + \
 | |
|                         '/users/' + nickname + '/removereadingstatus">\n' + \
 | |
|                         '              ' + \
 | |
|                         '<input type="hidden" name="actor" value="' + \
 | |
|                         actor + '">\n' + \
 | |
|                         '              ' + \
 | |
|                         '<input type="hidden" ' + \
 | |
|                         'name="publishedtimesec" value="' + \
 | |
|                         str(published_time_sec) + '">\n' + \
 | |
|                         '              ' + \
 | |
|                         '<input type="hidden" ' + \
 | |
|                         'name="bookeventtype" value="' + \
 | |
|                         book_event_type + '">\n' + \
 | |
|                         '              ' + \
 | |
|                         '<button type="submit" class="button" ' + \
 | |
|                         'name="submitRemoveReadingStatus">' + \
 | |
|                         translate['Remove'] + '</button>\n' + \
 | |
|                         '            </form>\n'
 | |
|             html_str += '          </div>\n'
 | |
| 
 | |
|             html_str += '        </div>\n'
 | |
|             html_str += '      </span>\n'
 | |
|             html_str += '    </li>\n'
 | |
|         ctr += 1
 | |
|         if ctr >= no_of_books:
 | |
|             break
 | |
|     html_str += '  </ul>\n'
 | |
|     html_str += '</div>\n'
 | |
|     return html_str
 |