milkomeda22 commited on
Commit
685c4cb
·
verified ·
1 Parent(s): 0f3eda2

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +781 -19
index.html CHANGED
@@ -1,19 +1,781 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Anime Database Application using OOP and Modern Data Engineering Practices
3
+ Built with anycoder
4
+ """
5
+
6
+ from dataclasses import dataclass, field
7
+ from typing import List, Dict, Optional, Set
8
+ from enum import Enum, auto
9
+ from datetime import datetime
10
+ import uuid
11
+ import json
12
+ from abc import ABC, abstractmethod
13
+ import logging
14
+ from functools import cached_property
15
+ import re
16
+ from pathlib import Path
17
+
18
+ # Configure logging
19
+ logging.basicConfig(level=logging.INFO)
20
+ logger = logging.getLogger(__name__)
21
+
22
+ class Genre(Enum):
23
+ """Enum representing anime genres"""
24
+ ACTION = auto()
25
+ ADVENTURE = auto()
26
+ COMEDY = auto()
27
+ DRAMA = auto()
28
+ FANTASY = auto()
29
+ HORROR = auto()
30
+ ROMANCE = auto()
31
+ SCI_FI = auto()
32
+ SLICE_OF_LIFE = auto()
33
+ SPORTS = auto()
34
+ SUPERNATURAL = auto()
35
+ THRILLER = auto()
36
+
37
+ def __str__(self):
38
+ return self.name.replace('_', ' ').title()
39
+
40
+ class AnimeStatus(Enum):
41
+ """Enum representing anime status"""
42
+ ONGOING = auto()
43
+ COMPLETED = auto()
44
+ UPCOMING = auto()
45
+ HIATUS = auto()
46
+ CANCELLED = auto()
47
+
48
+ def __str__(self):
49
+ return self.name.title()
50
+
51
+ @dataclass
52
+ class Anime:
53
+ """Data class representing an anime entry"""
54
+ id: str = field(default_factory=lambda: str(uuid.uuid4()))
55
+ title: str
56
+ alternative_titles: List[str] = field(default_factory=list)
57
+ episodes: int = 0
58
+ status: AnimeStatus = AnimeStatus.ONGOING
59
+ genres: Set[Genre] = field(default_factory=set)
60
+ release_date: Optional[datetime] = None
61
+ end_date: Optional[datetime] = None
62
+ synopsis: str = ""
63
+ rating: float = 0.0
64
+ studio: str = ""
65
+ source: str = ""
66
+ duration: str = ""
67
+ season: Optional[str] = None
68
+ year: Optional[int] = None
69
+ popularity_rank: Optional[int] = None
70
+ favorites: int = 0
71
+ created_at: datetime = field(default_factory=datetime.now)
72
+ updated_at: datetime = field(default_factory=datetime.now)
73
+
74
+ def __post_init__(self):
75
+ """Validate and process data after initialization"""
76
+ if not self.title:
77
+ raise ValueError("Title cannot be empty")
78
+
79
+ if self.episodes < 0:
80
+ raise ValueError("Episodes cannot be negative")
81
+
82
+ if self.rating < 0 or self.rating > 10:
83
+ raise ValueError("Rating must be between 0 and 10")
84
+
85
+ if self.release_date and self.end_date:
86
+ if self.release_date > self.end_date:
87
+ raise ValueError("Release date cannot be after end date")
88
+
89
+ if self.year and (self.year < 1900 or self.year > datetime.now().year + 5):
90
+ raise ValueError("Invalid year")
91
+
92
+ # Extract year from release_date if not provided
93
+ if not self.year and self.release_date:
94
+ self.year = self.release_date.year
95
+
96
+ def to_dict(self) -> Dict:
97
+ """Convert anime object to dictionary"""
98
+ return {
99
+ "id": self.id,
100
+ "title": self.title,
101
+ "alternative_titles": self.alternative_titles,
102
+ "episodes": self.episodes,
103
+ "status": self.status.name,
104
+ "genres": [genre.name for genre in self.genres],
105
+ "release_date": self.release_date.isoformat() if self.release_date else None,
106
+ "end_date": self.end_date.isoformat() if self.end_date else None,
107
+ "synopsis": self.synopsis,
108
+ "rating": self.rating,
109
+ "studio": self.studio,
110
+ "source": self.source,
111
+ "duration": self.duration,
112
+ "season": self.season,
113
+ "year": self.year,
114
+ "popularity_rank": self.popularity_rank,
115
+ "favorites": self.favorites,
116
+ "created_at": self.created_at.isoformat(),
117
+ "updated_at": self.updated_at.isoformat()
118
+ }
119
+
120
+ @classmethod
121
+ def from_dict(cls, data: Dict) -> 'Anime':
122
+ """Create anime object from dictionary"""
123
+ try:
124
+ return cls(
125
+ id=data.get("id", str(uuid.uuid4())),
126
+ title=data["title"],
127
+ alternative_titles=data.get("alternative_titles", []),
128
+ episodes=data.get("episodes", 0),
129
+ status=AnimeStatus[data.get("status", "ONGOING")],
130
+ genres={Genre[genre] for genre in data.get("genres", [])},
131
+ release_date=datetime.fromisoformat(data["release_date"]) if data.get("release_date") else None,
132
+ end_date=datetime.fromisoformat(data["end_date"]) if data.get("end_date") else None,
133
+ synopsis=data.get("synopsis", ""),
134
+ rating=data.get("rating", 0.0),
135
+ studio=data.get("studio", ""),
136
+ source=data.get("source", ""),
137
+ duration=data.get("duration", ""),
138
+ season=data.get("season"),
139
+ year=data.get("year"),
140
+ popularity_rank=data.get("popularity_rank"),
141
+ favorites=data.get("favorites", 0),
142
+ created_at=datetime.fromisoformat(data["created_at"]) if data.get("created_at") else datetime.now(),
143
+ updated_at=datetime.fromisoformat(data["updated_at"]) if data.get("updated_at") else datetime.now()
144
+ )
145
+ except Exception as e:
146
+ logger.error(f"Error creating anime from dict: {e}")
147
+ raise
148
+
149
+ class AnimeRepositoryInterface(ABC):
150
+ """Abstract base class for anime repository"""
151
+ @abstractmethod
152
+ def add_anime(self, anime: Anime) -> None:
153
+ pass
154
+
155
+ @abstractmethod
156
+ def get_anime(self, anime_id: str) -> Optional[Anime]:
157
+ pass
158
+
159
+ @abstractmethod
160
+ def update_anime(self, anime: Anime) -> None:
161
+ pass
162
+
163
+ @abstractmethod
164
+ def delete_anime(self, anime_id: str) -> None:
165
+ pass
166
+
167
+ @abstractmethod
168
+ def get_all_anime(self) -> List[Anime]:
169
+ pass
170
+
171
+ @abstractmethod
172
+ def search_anime(self, query: str) -> List[Anime]:
173
+ pass
174
+
175
+ @abstractmethod
176
+ def filter_by_genre(self, genre: Genre) -> List[Anime]:
177
+ pass
178
+
179
+ @abstractmethod
180
+ def filter_by_status(self, status: AnimeStatus) -> List[Anime]:
181
+ pass
182
+
183
+ class InMemoryAnimeRepository(AnimeRepositoryInterface):
184
+ """In-memory implementation of anime repository"""
185
+ def __init__(self):
186
+ self._anime_db: Dict[str, Anime] = {}
187
+
188
+ def add_anime(self, anime: Anime) -> None:
189
+ """Add anime to repository"""
190
+ if anime.id in self._anime_db:
191
+ raise ValueError(f"Anime with ID {anime.id} already exists")
192
+ self._anime_db[anime.id] = anime
193
+ logger.info(f"Added anime: {anime.title}")
194
+
195
+ def get_anime(self, anime_id: str) -> Optional[Anime]:
196
+ """Get anime by ID"""
197
+ return self._anime_db.get(anime_id)
198
+
199
+ def update_anime(self, anime: Anime) -> None:
200
+ """Update existing anime"""
201
+ if anime.id not in self._anime_db:
202
+ raise ValueError(f"Anime with ID {anime.id} not found")
203
+ anime.updated_at = datetime.now()
204
+ self._anime_db[anime.id] = anime
205
+ logger.info(f"Updated anime: {anime.title}")
206
+
207
+ def delete_anime(self, anime_id: str) -> None:
208
+ """Delete anime by ID"""
209
+ if anime_id not in self._anime_db:
210
+ raise ValueError(f"Anime with ID {anime_id} not found")
211
+ anime_title = self._anime_db[anime_id].title
212
+ del self._anime_db[anime_id]
213
+ logger.info(f"Deleted anime: {anime_title}")
214
+
215
+ def get_all_anime(self) -> List[Anime]:
216
+ """Get all anime in repository"""
217
+ return list(self._anime_db.values())
218
+
219
+ def search_anime(self, query: str) -> List[Anime]:
220
+ """Search anime by title or alternative titles"""
221
+ query = query.lower()
222
+ return [
223
+ anime for anime in self._anime_db.values()
224
+ if (query in anime.title.lower() or
225
+ any(query in alt.lower() for alt in anime.alternative_titles))
226
+ ]
227
+
228
+ def filter_by_genre(self, genre: Genre) -> List[Anime]:
229
+ """Filter anime by genre"""
230
+ return [anime for anime in self._anime_db.values() if genre in anime.genres]
231
+
232
+ def filter_by_status(self, status: AnimeStatus) -> List[Anime]:
233
+ """Filter anime by status"""
234
+ return [anime for anime in self._anime_db.values() if anime.status == status]
235
+
236
+ class JSONAnimeRepository(AnimeRepositoryInterface):
237
+ """JSON file-based implementation of anime repository"""
238
+ def __init__(self, file_path: str = "anime_database.json"):
239
+ self.file_path = Path(file_path)
240
+ self._anime_db: Dict[str, Anime] = {}
241
+ self._load_database()
242
+
243
+ def _load_database(self) -> None:
244
+ """Load database from JSON file"""
245
+ try:
246
+ if self.file_path.exists():
247
+ with open(self.file_path, 'r', encoding='utf-8') as f:
248
+ data = json.load(f)
249
+ self._anime_db = {
250
+ anime_id: Anime.from_dict(anime_data)
251
+ for anime_id, anime_data in data.items()
252
+ }
253
+ logger.info(f"Loaded {len(self._anime_db)} anime entries from {self.file_path}")
254
+ else:
255
+ logger.info(f"Creating new database at {self.file_path}")
256
+ except Exception as e:
257
+ logger.error(f"Error loading database: {e}")
258
+ raise
259
+
260
+ def _save_database(self) -> None:
261
+ """Save database to JSON file"""
262
+ try:
263
+ data = {
264
+ anime_id: anime.to_dict()
265
+ for anime_id, anime in self._anime_db.items()
266
+ }
267
+ with open(self.file_path, 'w', encoding='utf-8') as f:
268
+ json.dump(data, f, indent=2, ensure_ascii=False)
269
+ logger.info(f"Saved {len(self._anime_db)} anime entries to {self.file_path}")
270
+ except Exception as e:
271
+ logger.error(f"Error saving database: {e}")
272
+ raise
273
+
274
+ def add_anime(self, anime: Anime) -> None:
275
+ """Add anime to repository"""
276
+ if anime.id in self._anime_db:
277
+ raise ValueError(f"Anime with ID {anime.id} already exists")
278
+ self._anime_db[anime.id] = anime
279
+ self._save_database()
280
+ logger.info(f"Added anime: {anime.title}")
281
+
282
+ def get_anime(self, anime_id: str) -> Optional[Anime]:
283
+ """Get anime by ID"""
284
+ return self._anime_db.get(anime_id)
285
+
286
+ def update_anime(self, anime: Anime) -> None:
287
+ """Update existing anime"""
288
+ if anime.id not in self._anime_db:
289
+ raise ValueError(f"Anime with ID {anime.id} not found")
290
+ anime.updated_at = datetime.now()
291
+ self._anime_db[anime.id] = anime
292
+ self._save_database()
293
+ logger.info(f"Updated anime: {anime.title}")
294
+
295
+ def delete_anime(self, anime_id: str) -> None:
296
+ """Delete anime by ID"""
297
+ if anime_id not in self._anime_db:
298
+ raise ValueError(f"Anime with ID {anime_id} not found")
299
+ anime_title = self._anime_db[anime_id].title
300
+ del self._anime_db[anime_id]
301
+ self._save_database()
302
+ logger.info(f"Deleted anime: {anime_title}")
303
+
304
+ def get_all_anime(self) -> List[Anime]:
305
+ """Get all anime in repository"""
306
+ return list(self._anime_db.values())
307
+
308
+ def search_anime(self, query: str) -> List[Anime]:
309
+ """Search anime by title or alternative titles"""
310
+ query = query.lower()
311
+ return [
312
+ anime for anime in self._anime_db.values()
313
+ if (query in anime.title.lower() or
314
+ any(query in alt.lower() for alt in anime.alternative_titles))
315
+ ]
316
+
317
+ def filter_by_genre(self, genre: Genre) -> List[Anime]:
318
+ """Filter anime by genre"""
319
+ return [anime for anime in self._anime_db.values() if genre in anime.genres]
320
+
321
+ def filter_by_status(self, status: AnimeStatus) -> List[Anime]:
322
+ """Filter anime by status"""
323
+ return [anime for anime in self._anime_db.values() if anime.status == status]
324
+
325
+ class AnimeService:
326
+ """Service class for anime operations"""
327
+ def __init__(self, repository: AnimeRepositoryInterface):
328
+ self.repository = repository
329
+
330
+ def add_anime(self, anime_data: Dict) -> Anime:
331
+ """Add new anime to database"""
332
+ try:
333
+ anime = Anime.from_dict(anime_data)
334
+ self.repository.add_anime(anime)
335
+ return anime
336
+ except Exception as e:
337
+ logger.error(f"Error adding anime: {e}")
338
+ raise
339
+
340
+ def get_anime(self, anime_id: str) -> Optional[Anime]:
341
+ """Get anime by ID"""
342
+ return self.repository.get_anime(anime_id)
343
+
344
+ def update_anime(self, anime_id: str, anime_data: Dict) -> Optional[Anime]:
345
+ """Update existing anime"""
346
+ existing_anime = self.repository.get_anime(anime_id)
347
+ if not existing_anime:
348
+ return None
349
+
350
+ try:
351
+ # Update only the fields that are provided
352
+ for key, value in anime_data.items():
353
+ if hasattr(existing_anime, key):
354
+ if key == "genres":
355
+ setattr(existing_anime, key, {Genre[genre] for genre in value})
356
+ elif key == "status":
357
+ setattr(existing_anime, key, AnimeStatus[value])
358
+ else:
359
+ setattr(existing_anime, key, value)
360
+
361
+ existing_anime.updated_at = datetime.now()
362
+ self.repository.update_anime(existing_anime)
363
+ return existing_anime
364
+ except Exception as e:
365
+ logger.error(f"Error updating anime: {e}")
366
+ raise
367
+
368
+ def delete_anime(self, anime_id: str) -> bool:
369
+ """Delete anime by ID"""
370
+ try:
371
+ self.repository.delete_anime(anime_id)
372
+ return True
373
+ except Exception as e:
374
+ logger.error(f"Error deleting anime: {e}")
375
+ return False
376
+
377
+ def get_all_anime(self) -> List[Anime]:
378
+ """Get all anime"""
379
+ return self.repository.get_all_anime()
380
+
381
+ def search_anime(self, query: str) -> List[Anime]:
382
+ """Search anime by query"""
383
+ return self.repository.search_anime(query)
384
+
385
+ def filter_by_genre(self, genre: str) -> List[Anime]:
386
+ """Filter anime by genre"""
387
+ try:
388
+ genre_enum = Genre[genre.upper()]
389
+ return self.repository.filter_by_genre(genre_enum)
390
+ except KeyError:
391
+ logger.error(f"Invalid genre: {genre}")
392
+ return []
393
+
394
+ def filter_by_status(self, status: str) -> List[Anime]:
395
+ """Filter anime by status"""
396
+ try:
397
+ status_enum = AnimeStatus[status.upper()]
398
+ return self.repository.filter_by_status(status_enum)
399
+ except KeyError:
400
+ logger.error(f"Invalid status: {status}")
401
+ return []
402
+
403
+ def get_top_rated(self, limit: int = 10) -> List[Anime]:
404
+ """Get top rated anime"""
405
+ return sorted(
406
+ self.repository.get_all_anime(),
407
+ key=lambda x: x.rating,
408
+ reverse=True
409
+ )[:limit]
410
+
411
+ def get_most_popular(self, limit: int = 10) -> List[Anime]:
412
+ """Get most popular anime"""
413
+ return sorted(
414
+ self.repository.get_all_anime(),
415
+ key=lambda x: x.popularity_rank if x.popularity_rank else float('inf'),
416
+ reverse=False
417
+ )[:limit]
418
+
419
+ class AnimeDatabase:
420
+ """Main anime database application class"""
421
+ def __init__(self, repository_type: str = "json"):
422
+ """Initialize anime database with specified repository type"""
423
+ self.repository_type = repository_type.lower()
424
+
425
+ if self.repository_type == "json":
426
+ self.repository = JSONAnimeRepository()
427
+ else:
428
+ self.repository = InMemoryAnimeRepository()
429
+
430
+ self.service = AnimeService(self.repository)
431
+
432
+ def run(self):
433
+ """Run the anime database application"""
434
+ print("Anime Database Application")
435
+ print("Built with anycoder")
436
+ print(f"Using {self.repository_type} repository")
437
+ print("Type 'help' for available commands")
438
+
439
+ while True:
440
+ try:
441
+ command = input("\n> ").strip().lower()
442
+
443
+ if command == "exit":
444
+ break
445
+
446
+ elif command == "help":
447
+ self._show_help()
448
+
449
+ elif command == "add":
450
+ self._add_anime()
451
+
452
+ elif command == "list":
453
+ self._list_anime()
454
+
455
+ elif command == "search":
456
+ self._search_anime()
457
+
458
+ elif command == "filter":
459
+ self._filter_anime()
460
+
461
+ elif command == "top":
462
+ self._show_top_rated()
463
+
464
+ elif command == "popular":
465
+ self._show_most_popular()
466
+
467
+ elif command == "delete":
468
+ self._delete_anime()
469
+
470
+ elif command == "update":
471
+ self._update_anime()
472
+
473
+ else:
474
+ print("Unknown command. Type 'help' for available commands.")
475
+
476
+ except KeyboardInterrupt:
477
+ print("\nExiting...")
478
+ break
479
+ except Exception as e:
480
+ print(f"Error: {e}")
481
+
482
+ def _show_help(self):
483
+ """Display help information"""
484
+ print("\nAvailable commands:")
485
+ print(" help - Show this help message")
486
+ print(" add - Add new anime to database")
487
+ print(" list - List all anime in database")
488
+ print(" search - Search anime by title")
489
+ print(" filter - Filter anime by genre or status")
490
+ print(" top - Show top rated anime")
491
+ print(" popular - Show most popular anime")
492
+ print(" update - Update existing anime")
493
+ print(" delete - Delete anime from database")
494
+ print(" exit - Exit the application")
495
+
496
+ def _add_anime(self):
497
+ """Add new anime to database"""
498
+ print("\nAdd New Anime")
499
+ print("Enter anime details (press Enter to skip optional fields)")
500
+
501
+ try:
502
+ title = input("Title (required): ").strip()
503
+ if not title:
504
+ print("Title is required")
505
+ return
506
+
507
+ anime_data = {
508
+ "title": title,
509
+ "alternative_titles": input("Alternative titles (comma separated): ").strip().split(","),
510
+ "episodes": int(input("Episodes: ").strip() or 0),
511
+ "status": input("Status (ONGOING/COMPLETED/UPCOMING/HIATUS/CANCELLED): ").strip().upper() or "ONGOING",
512
+ "genres": input("Genres (comma separated, e.g., ACTION,COMEDY): ").strip().upper().split(","),
513
+ "release_date": input("Release date (YYYY-MM-DD): ").strip(),
514
+ "end_date": input("End date (YYYY-MM-DD): ").strip(),
515
+ "synopsis": input("Synopsis: ").strip(),
516
+ "rating": float(input("Rating (0-10): ").strip() or 0),
517
+ "studio": input("Studio: ").strip(),
518
+ "source": input("Source: ").strip(),
519
+ "duration": input("Duration (e.g., 24 min): ").strip(),
520
+ "season": input("Season (e.g., Winter 2023): ").strip(),
521
+ "year": int(input("Year: ").strip() or datetime.now().year),
522
+ "popularity_rank": int(input("Popularity rank: ").strip() or 0) or None,
523
+ "favorites": int(input("Favorites count: ").strip() or 0)
524
+ }
525
+
526
+ # Process dates
527
+ if anime_data["release_date"]:
528
+ anime_data["release_date"] = datetime.strptime(anime_data["release_date"], "%Y-%m-%d").isoformat()
529
+ if anime_data["end_date"]:
530
+ anime_data["end_date"] = datetime.strptime(anime_data["end_date"], "%Y-%m-%d").isoformat()
531
+
532
+ # Remove empty alternative titles
533
+ anime_data["alternative_titles"] = [t.strip() for t in anime_data["alternative_titles"] if t.strip()]
534
+
535
+ # Remove empty genres
536
+ anime_data["genres"] = [g.strip() for g in anime_data["genres"] if g.strip()]
537
+
538
+ anime = self.service.add_anime(anime_data)
539
+ print(f"Successfully added anime: {anime.title}")
540
+
541
+ except Exception as e:
542
+ print(f"Error adding anime: {e}")
543
+
544
+ def _list_anime(self):
545
+ """List all anime in database"""
546
+ anime_list = self.service.get_all_anime()
547
+
548
+ if not anime_list:
549
+ print("No anime in database")
550
+ return
551
+
552
+ print(f"\nListing {len(anime_list)} anime:")
553
+ for i, anime in enumerate(anime_list, 1):
554
+ print(f"\n{i}. {anime.title}")
555
+ print(f" ID: {anime.id}")
556
+ print(f" Status: {anime.status}")
557
+ print(f" Episodes: {anime.episodes}")
558
+ print(f" Rating: {anime.rating}/10")
559
+ print(f" Genres: {', '.join(str(g) for g in anime.genres)}")
560
+ if anime.release_date:
561
+ print(f" Released: {anime.release_date.strftime('%Y-%m-%d')}")
562
+ print(f" Studio: {anime.studio}")
563
+ print(f" Synopsis: {anime.synopsis[:100]}...")
564
+
565
+ def _search_anime(self):
566
+ """Search anime by title"""
567
+ query = input("\nEnter search query: ").strip()
568
+ if not query:
569
+ print("Please enter a search query")
570
+ return
571
+
572
+ results = self.service.search_anime(query)
573
+
574
+ if not results:
575
+ print("No results found")
576
+ return
577
+
578
+ print(f"\nFound {len(results)} results:")
579
+ for i, anime in enumerate(results, 1):
580
+ print(f"\n{i}. {anime.title}")
581
+ print(f" ID: {anime.id}")
582
+ print(f" Status: {anime.status}")
583
+ print(f" Rating: {anime.rating}/10")
584
+
585
+ def _filter_anime(self):
586
+ """Filter anime by genre or status"""
587
+ print("\nFilter Options:")
588
+ print("1. By Genre")
589
+ print("2. By Status")
590
+
591
+ choice = input("Choose filter type (1/2): ").strip()
592
+
593
+ if choice == "1":
594
+ print("\nAvailable Genres:")
595
+ for genre in Genre:
596
+ print(f" - {genre}")
597
+
598
+ genre_input = input("Enter genre: ").strip().upper()
599
+ try:
600
+ genre = Genre[genre_input]
601
+ results = self.service.filter_by_genre(genre.name)
602
+ except KeyError:
603
+ print("Invalid genre")
604
+ return
605
+
606
+ elif choice == "2":
607
+ print("\nAvailable Statuses:")
608
+ for status in AnimeStatus:
609
+ print(f" - {status}")
610
+
611
+ status_input = input("Enter status: ").strip().upper()
612
+ try:
613
+ results = self.service.filter_by_status(status_input)
614
+ except KeyError:
615
+ print("Invalid status")
616
+ return
617
+
618
+ else:
619
+ print("Invalid choice")
620
+ return
621
+
622
+ if not results:
623
+ print("No results found")
624
+ return
625
+
626
+ print(f"\nFound {len(results)} results:")
627
+ for i, anime in enumerate(results, 1):
628
+ print(f"\n{i}. {anime.title}")
629
+ print(f" ID: {anime.id}")
630
+ print(f" Rating: {anime.rating}/10")
631
+
632
+ def _show_top_rated(self):
633
+ """Show top rated anime"""
634
+ limit = input("\nEnter number of top rated anime to show (default 10): ").strip()
635
+ try:
636
+ limit = int(limit) if limit else 10
637
+ results = self.service.get_top_rated(limit)
638
+
639
+ if not results:
640
+ print("No anime in database")
641
+ return
642
+
643
+ print(f"\nTop {len(results)} Rated Anime:")
644
+ for i, anime in enumerate(results, 1):
645
+ print(f"\n{i}. {anime.title}")
646
+ print(f" Rating: {anime.rating}/10")
647
+ print(f" Episodes: {anime.episodes}")
648
+ print(f" Genres: {', '.join(str(g) for g in anime.genres)}")
649
+
650
+ except ValueError:
651
+ print("Please enter a valid number")
652
+
653
+ def _show_most_popular(self):
654
+ """Show most popular anime"""
655
+ limit = input("\nEnter number of most popular anime to show (default 10): ").strip()
656
+ try:
657
+ limit = int(limit) if limit else 10
658
+ results = self.service.get_most_popular(limit)
659
+
660
+ if not results:
661
+ print("No anime in database")
662
+ return
663
+
664
+ print(f"\nTop {len(results)} Most Popular Anime:")
665
+ for i, anime in enumerate(results, 1):
666
+ print(f"\n{i}. {anime.title}")
667
+ print(f" Popularity Rank: {anime.popularity_rank}")
668
+ print(f" Favorites: {anime.favorites}")
669
+ print(f" Rating: {anime.rating}/10")
670
+
671
+ except ValueError:
672
+ print("Please enter a valid number")
673
+
674
+ def _delete_anime(self):
675
+ """Delete anime from database"""
676
+ anime_id = input("\nEnter anime ID to delete: ").strip()
677
+ if not anime_id:
678
+ print("Please enter an anime ID")
679
+ return
680
+
681
+ anime = self.service.get_anime(anime_id)
682
+ if not anime:
683
+ print("Anime not found")
684
+ return
685
+
686
+ confirm = input(f"Are you sure you want to delete '{anime.title}'? (y/n): ").strip().lower()
687
+ if confirm == 'y':
688
+ if self.service.delete_anime(anime_id):
689
+ print(f"Successfully deleted anime: {anime.title}")
690
+ else:
691
+ print("Failed to delete anime")
692
+ else:
693
+ print("Deletion cancelled")
694
+
695
+ def _update_anime(self):
696
+ """Update existing anime"""
697
+ anime_id = input("\nEnter anime ID to update: ").strip()
698
+ if not anime_id:
699
+ print("Please enter an anime ID")
700
+ return
701
+
702
+ anime = self.service.get_anime(anime_id)
703
+ if not anime:
704
+ print("Anime not found")
705
+ return
706
+
707
+ print(f"\nCurrent anime details for '{anime.title}':")
708
+ print(f"1. Title: {anime.title}")
709
+ print(f"2. Alternative Titles: {', '.join(anime.alternative_titles)}")
710
+ print(f"3. Episodes: {anime.episodes}")
711
+ print(f"4. Status: {anime.status}")
712
+ print(f"5. Genres: {', '.join(str(g) for g in anime.genres)}")
713
+ print(f"6. Release Date: {anime.release_date}")
714
+ print(f"7. End Date: {anime.end_date}")
715
+ print(f"8. Synopsis: {anime.synopsis}")
716
+ print(f"9. Rating: {anime.rating}")
717
+ print(f"10. Studio: {anime.studio}")
718
+ print(f"11. Source: {anime.source}")
719
+ print(f"12. Duration: {anime.duration}")
720
+ print(f"13. Season: {anime.season}")
721
+ print(f"14. Year: {anime.year}")
722
+ print(f"15. Popularity Rank: {anime.popularity_rank}")
723
+ print(f"16. Favorites: {anime.favorites}")
724
+
725
+ update_data = {}
726
+
727
+ while True:
728
+ field_num = input("\nEnter field number to update (0 to finish): ").strip()
729
+ if field_num == "0":
730
+ break
731
+
732
+ try:
733
+ field_num = int(field_num)
734
+ if field_num < 1 or field_num > 16:
735
+ print("Invalid field number")
736
+ continue
737
+
738
+ field_names = [
739
+ "title", "alternative_titles", "episodes", "status", "genres",
740
+ "release_date", "end_date", "synopsis", "rating", "studio",
741
+ "source", "duration", "season", "year", "popularity_rank", "favorites"
742
+ ]
743
+ field_name = field_names[field_num - 1]
744
+
745
+ if field_name == "alternative_titles":
746
+ value = input(f"Enter new {field_name} (comma separated): ").strip().split(",")
747
+ elif field_name in ["release_date", "end_date"]:
748
+ value = input(f"Enter new {field_name} (YYYY-MM-DD): ").strip()
749
+ if value:
750
+ value = datetime.strptime(value, "%Y-%m-%d").isoformat()
751
+ elif field_name in ["episodes", "year", "popularity_rank", "favorites"]:
752
+ value = int(input(f"Enter new {field_name}: ").strip())
753
+ elif field_name == "rating":
754
+ value = float(input(f"Enter new {field_name}: ").strip())
755
+ elif field_name == "genres":
756
+ value = input(f"Enter new {field_name} (comma separated): ").strip().upper().split(",")
757
+ else:
758
+ value = input(f"Enter new {field_name}: ").strip()
759
+
760
+ if value or value == 0: # Allow 0 for numeric fields
761
+ update_data[field_name] = value
762
+
763
+ except ValueError as e:
764
+ print(f"Invalid input: {e}")
765
+ except Exception as e:
766
+ print(f"Error: {e}")
767
+
768
+ if update_data:
769
+ updated_anime = self.service.update_anime(anime_id, update_data)
770
+ if updated_anime:
771
+ print(f"Successfully updated anime: {updated_anime.title}")
772
+ else:
773
+ print("Failed to update anime")
774
+ else:
775
+ print("No changes made")
776
+
777
+ if __name__ == "__main__":
778
+ # Initialize and run the anime database application
779
+ print("Initializing Anime Database...")
780
+ db = AnimeDatabase(repository_type="json")
781
+ db.run()