# metadata_fetcher.py import requests import re import os from typing import Dict, List, Optional class EnhancedMetadataFetcher: def __init__(self): self.sources = [ self._fetch_google_books, self._fetch_openlibrary, self._fetch_isbndb, self._fetch_worldcat ] def fetch_metadata(self, isbn: str) -> Dict: all_results = [] for source_func in self.sources: try: result = source_func(isbn) if result and result.get('title'): all_results.append(result) except Exception: continue if not all_results: return self._get_empty_metadata(isbn) return self._merge_results(all_results, isbn) def _fetch_google_books(self, isbn: str) -> Optional[Dict]: try: url = f"https://www.googleapis.com/books/v1/volumes?q=isbn:{isbn}" response = requests.get(url, timeout=10) if response.status_code == 200: data = response.json() if 'items' in data and len(data['items']) > 0: volume_info = data['items'][0]['volumeInfo'] published_date = self._extract_date(volume_info.get('publishedDate', '')) # محاولة استخراج الطبعة edition = volume_info.get('edition', '') if not edition and volume_info.get('title'): edition = self._extract_edition_from_text(volume_info['title']) return { 'source': 'Google Books', 'title': volume_info.get('title', ''), 'authors': volume_info.get('authors', []), 'publisher': volume_info.get('publisher', ''), 'published_date': published_date, 'description': volume_info.get('description', ''), 'page_count': volume_info.get('pageCount'), 'categories': volume_info.get('categories', []), 'language': volume_info.get('language', 'en'), 'isbn_10': self._extract_isbn(volume_info.get('industryIdentifiers', []), 'ISBN_10'), 'isbn_13': self._extract_isbn(volume_info.get('industryIdentifiers', []), 'ISBN_13'), 'edition': edition, 'confidence': 'high' } except: pass return None def _fetch_openlibrary(self, isbn: str) -> Optional[Dict]: try: url = f"https://openlibrary.org/api/books?bibkeys=ISBN:{isbn}&format=json&jscmd=data" response = requests.get(url, timeout=10) if response.status_code == 200: data = response.json() if f'ISBN:{isbn}' in data: book_data = data[f'ISBN:{isbn}'] publish_date = self._extract_date(book_data.get('publish_date', '')) # محاولة استخراج الطبعة edition = book_data.get('edition_name', '') or book_data.get('edition', '') if not edition and book_data.get('title'): edition = self._extract_edition_from_text(book_data['title']) return { 'source': 'OpenLibrary', 'title': book_data.get('title', ''), 'authors': [author.get('name') for author in book_data.get('authors', [])], 'publishers': [pub.get('name') for pub in book_data.get('publishers', [])], 'published_date': publish_date, 'subjects': [subj.get('name') for subj in book_data.get('subjects', [])], 'number_of_pages': book_data.get('number_of_pages'), 'edition': edition, 'confidence': 'medium' } except: pass return None def _fetch_isbndb(self, isbn: str) -> Optional[Dict]: api_key = os.getenv('ISBNDB_KEY') if not api_key: return None try: url = f"https://api.isbndb.com/book/{isbn}" headers = {'Authorization': api_key} response = requests.get(url, headers=headers, timeout=10) if response.status_code == 200: data = response.json() if 'book' in data: book = data['book'] edition = book.get('edition', '') if not edition and book.get('title'): edition = self._extract_edition_from_text(book['title']) return { 'source': 'ISBNdb', 'title': book.get('title', ''), 'authors': book.get('authors', []), 'publisher': book.get('publisher', ''), 'published_date': book.get('date_published', ''), 'synopsis': book.get('synopsis', ''), 'pages': book.get('pages', ''), 'edition': edition, 'confidence': 'high' } except: pass return None def _fetch_worldcat(self, isbn: str) -> Optional[Dict]: api_key = os.getenv('WORLDCAT_KEY') if not api_key: return None try: url = f"http://www.worldcat.org/webservices/catalog/content/isbn/{isbn}?wskey={api_key}" response = requests.get(url, timeout=10) if response.status_code == 200: content = response.text title_match = re.search(r'([^<]+)', content) title = title_match.group(1) if title_match else '' author_match = re.search(r'([^<]+)', content) author = author_match.group(1) if author_match else '' date_match = re.search(r'([^<]+)', content) date = date_match.group(1) if date_match else '' if title: edition = self._extract_edition_from_text(title) return { 'source': 'WorldCat', 'title': title, 'authors': [author] if author else [], 'published_date': self._extract_date(date), 'edition': edition, 'confidence': 'medium' } except: pass return None def _extract_edition_from_text(self, text: str) -> str: """استخراج رقم الطبعة من النص""" if not text: return '' patterns = [ r'(\d+)(?:st|nd|rd|th)?\s+edition', r'(\d+)(?:st|nd|rd|th)?\s+ed\.', r'edition\s+(\d+)', r'(\d+)[- ]volume', r'(\d+)(?:st|nd|rd|th)\s+rev\.?', ] text_lower = text.lower() for pattern in patterns: match = re.search(pattern, text_lower) if match: num = match.group(1) suffix = 'th' if num.endswith('1'): suffix = 'st' elif num.endswith('2'): suffix = 'nd' elif num.endswith('3'): suffix = 'rd' return f"{num}{suffix} ed." return '' def _extract_date(self, date_str: str) -> str: if not date_str: return '' patterns = [ r'(\d{4})', r'(\d{4})-\d{2}-\d{2}', r'\d{2}/(\d{4})', r'(\d{4})-\d{2}', r'(\d{4})\s*年', r'c?\.?\s*(\d{4})' ] for pattern in patterns: match = re.search(pattern, str(date_str)) if match: return match.group(1) numbers = re.findall(r'\d{4}', str(date_str)) if numbers: return numbers[0] return str(date_str)[:4] if len(str(date_str)) >= 4 else '' def _extract_isbn(self, identifiers: List[Dict], isbn_type: str) -> str: for identifier in identifiers: if identifier.get('type') == isbn_type: return identifier.get('identifier', '') return '' def _merge_results(self, results: List[Dict], isbn: str) -> Dict: merged = { 'title': '', 'authors': [], 'publisher': '', 'published_date': '', 'description': '', 'page_count': '', 'categories': [], 'language': 'en', 'isbn_10': '', 'isbn_13': isbn if len(isbn) == 13 else '', 'edition': '', 'sources': len(results), 'confidence': 'low' } high_confidence_sources = [r for r in results if r.get('confidence') == 'high'] medium_confidence_sources = [r for r in results if r.get('confidence') == 'medium'] sources_priority = high_confidence_sources + medium_confidence_sources for source in sources_priority: if not merged['title'] and source.get('title'): merged['title'] = source['title'] if not merged['authors']: if source.get('authors'): merged['authors'] = source['authors'] elif source.get('author'): merged['authors'] = [source['author']] if not merged['publisher']: if source.get('publisher'): merged['publisher'] = source['publisher'] elif source.get('publishers'): merged['publisher'] = ', '.join(source['publishers']) if not merged['published_date'] and source.get('published_date'): merged['published_date'] = source['published_date'] if not merged['description']: if source.get('description'): merged['description'] = source['description'] elif source.get('synopsis'): merged['description'] = source['synopsis'] if not merged['page_count']: if source.get('page_count'): merged['page_count'] = source['page_count'] elif source.get('pages'): merged['page_count'] = source['pages'] elif source.get('number_of_pages'): merged['page_count'] = source['number_of_pages'] if not merged['categories']: if source.get('categories'): merged['categories'] = source['categories'] elif source.get('subjects'): merged['categories'] = source['subjects'] if not merged['isbn_10'] and source.get('isbn_10'): merged['isbn_10'] = source['isbn_10'] if not merged['isbn_13'] and source.get('isbn_13'): merged['isbn_13'] = source['isbn_13'] if not merged['edition'] and source.get('edition'): merged['edition'] = source['edition'] # إذا لم نجد طبعة من المصادر، نحاول استخراجها من العنوان أو الوصف if not merged['edition']: if merged['title']: merged['edition'] = self._extract_edition_from_text(merged['title']) if not merged['edition'] and merged['description']: merged['edition'] = self._extract_edition_from_text(merged['description']) # حساب مستوى الثقة data_points = sum([ 1 if merged['title'] else 0, 1 if merged['authors'] else 0, 1 if merged['publisher'] else 0, 1 if merged['published_date'] else 0, 1 if merged['description'] else 0 ]) if data_points >= 4: merged['confidence'] = 'high' elif data_points >= 2: merged['confidence'] = 'medium' if not isinstance(merged['authors'], list): if merged['authors']: merged['authors'] = [merged['authors']] else: merged['authors'] = [] return merged def _get_empty_metadata(self, isbn: str) -> Dict: return { 'title': '', 'authors': [], 'publisher': '', 'published_date': '', 'description': '', 'page_count': '', 'categories': [], 'language': 'en', 'isbn_10': isbn if len(isbn) == 10 else '', 'isbn_13': isbn if len(isbn) == 13 else '', 'edition': '', 'sources': 0, 'confidence': 'low' }