Parcourir la source

Now using MediaStatus enum for MAL and AniList

Lucas Villeneuve il y a 5 ans
Parent
commit
341b6a04d1
6 fichiers modifiés avec 396 ajouts et 240 suppressions
  1. 1 5
      myanimebot.py
  2. 6 33
      myanimebot/anilist.py
  3. 1 1
      myanimebot/discord.py
  4. 8 1
      myanimebot/myanimelist.py
  5. 205 166
      myanimebot/utils.py
  6. 175 34
      tests/test_utils.py

+ 1 - 5
myanimebot.py

@@ -90,7 +90,7 @@ async def background_check_feed(asyncioloop):
 						pubDateRaw = datetime.strptime(feed_data.published, '%a, %d %b %Y %H:%M:%S %z').astimezone(globals.timezone)
 						DateTimezone = pubDateRaw.strftime("%z")[:3] + ':' + pubDateRaw.strftime("%z")[3:]
 						pubDate = pubDateRaw.strftime("%Y-%m-%d %H:%M:%S")
-						feed = myanimelist.build_feed_from_data(feed_data, user, None, pubDateRaw.timestamp(), None)
+						feed = myanimelist.build_feed_from_data(feed_data, user, None, pubDateRaw.timestamp(), feed_type)
 						
 						cursor = globals.conn.cursor(buffered=True)
 						cursor.execute("SELECT published, title, url FROM t_feeds WHERE published=%s AND title=%s AND user=%s", [pubDate, feed.media.name, user.name])
@@ -104,10 +104,6 @@ async def background_check_feed(asyncioloop):
 							if var.total_seconds() < globals.secondMax:
 								globals.logger.info(user.name + ": Item '" + feed.media.name + "' not seen, processing...")
 								
-								if feed.status.startswith('-') :
-									if feed_type == 1 :	feed.status = "Re-Reading " + feed.status
-									else :				feed.status = "Re-Watching " + feed.status
-								
 								cursor.execute("SELECT thumbnail FROM t_animes WHERE guid=%s LIMIT 1", [feed.media.url]) # TODO Change that ?
 								data_img = cursor.fetchone()
 								

+ 6 - 33
myanimebot/anilist.py

@@ -11,33 +11,6 @@ import myanimebot.utils as utils
 from myanimebot.discord import send_embed_wrapper, build_embed
 
 ANILIST_GRAPHQL_URL = 'https://graphql.anilist.co'
-
-class MediaListStatus(Enum):
-    CURRENT=0
-    PLANNING=1
-    COMPLETED=2
-    DROPPED=3
-    PAUSED=4
-    REPEATING=5
-
-    @staticmethod
-    def from_str(label: str):
-        if label.upper().startswith('READ') or \
-            label.upper().startswith('WATCHED') :
-            return MediaListStatus.CURRENT
-        elif label.upper().startswith('PLANS'):
-            return MediaListStatus.PLANNING
-        elif label.upper().startswith('COMPLETED'):
-            return MediaListStatus.COMPLETED
-        elif label.upper().startswith('DROPPED'):
-            return MediaListStatus.DROPPED
-        elif label.upper().startswith('PAUSED'):
-            return MediaListStatus.PAUSED
-        elif label.upper().startswith('REREAD') or \
-              label.upper().startswith('REWATCHED'):
-            return MediaListStatus.REPEATING
-        else:
-            raise NotImplementedError('Error: Cannot convert "{}" to a MediaListStatus'.format(label))
     
 
 def get_media_name(activity):
@@ -65,17 +38,17 @@ def get_progress(activity):
     return progress
 
 
-def build_status_string(activity):
+def build_description_string(activity):
     status_str = activity["status"].capitalize()
-    status = MediaListStatus.from_str(status_str)
+    status = utils.MediaStatus.from_str(status_str)
     progress = get_progress(activity)
     episodes = ''
     media_label = ''
     media_type = utils.MediaType.from_str(activity["type"])
 
     # TODO Manage Completed/Dropped/Planned episodes/chapters count
-    if status == MediaListStatus.CURRENT \
-       or status == MediaListStatus.REPEATING:
+    if status == utils.MediaStatus.CURRENT \
+       or status == utils.MediaStatus.REPEATING:
         if media_type == utils.MediaType.ANIME:
             episodes = activity["media"]["episodes"]
             if episodes is None:
@@ -103,8 +76,8 @@ def build_feed_from_activity(activity, user : utils.User) -> utils.Feed:
     feed = utils.Feed(service=utils.Service.ANILIST,
                         date_publication=datetime.datetime.fromtimestamp(activity["createdAt"], globals.timezone),
                         user=user,
-                        status=build_status_string(activity),
-                        description=activity["status"],
+                        status=utils.MediaStatus.from_str(activity["status"]),
+                        description=build_description_string(activity),
                         media=media)
     return feed
  

+ 1 - 1
myanimebot/discord.py

@@ -19,7 +19,7 @@ def build_embed(feed : utils.Feed):
 		icon_url = globals.ANILIST_ICON_URL
 	else:
 		raise NotImplementedError('Unknown service {}'.format(feed.service))
-	description = "[{}]({})\n```{}```".format(utils.filter_name(feed.media.name), feed.media.url, feed.status)
+	description = "[{}]({})\n```{}```".format(utils.filter_name(feed.media.name), feed.media.url, feed.description)
 	profile_url_label = "{}'s {}".format(feed.user.name, service_name)
 
 	try:	

+ 8 - 1
myanimebot/myanimelist.py

@@ -29,10 +29,17 @@ def build_feed_from_data(data, user : utils.User, image, pubDateRaw, type : util
                         episodes=None,
                         image=image,
                         type=type)
+
+    if data.description.startswith('-') :
+        if type == 1:
+            data.description = "Rereading " + data.description
+        else:
+            data.description = "Rewatching " + data.description								
+
     feed = utils.Feed(service=utils.Service.MAL,
                         date_publication=datetime.datetime.fromtimestamp(pubDateRaw, globals.timezone),
                         user=user,
-                        status=data.description,
+                        status=utils.MediaStatus.from_str(data.description),
                         description=data.description,
                         media=media)
     return feed

+ 205 - 166
myanimebot/utils.py

@@ -9,17 +9,19 @@ import myanimebot.globals as globals
 
 
 class Service(Enum):
-	MAL=globals.SERVICE_MAL
-	ANILIST=globals.SERVICE_ANILIST
+    MAL=globals.SERVICE_MAL
+    ANILIST=globals.SERVICE_ANILIST
 
-	@staticmethod
-	def from_str(label: str):
-		if label.upper() in ('MAL', 'MYANIMELIST', globals.SERVICE_MAL.upper()):
-			return Service.MAL
-		elif label.upper() in ('AL', 'ANILIST', globals.SERVICE_ANILIST.upper()):
-			return Service.ANILIST
-		else:
-			raise NotImplementedError('Error: Cannot convert "{}" to a Service'.format(label))
+    @staticmethod
+    def from_str(label: str):
+        if label is None: raise TypeError
+
+        if label.upper() in ('MAL', 'MYANIMELIST', globals.SERVICE_MAL.upper()):
+            return Service.MAL
+        elif label.upper() in ('AL', 'ANILIST', globals.SERVICE_ANILIST.upper()):
+            return Service.ANILIST
+        else:
+            raise NotImplementedError('Error: Cannot convert "{}" to a Service'.format(label))
 
 
 class MediaType(Enum):
@@ -28,6 +30,8 @@ class MediaType(Enum):
 
     @staticmethod
     def from_str(label: str):
+        if label is None: raise TypeError
+
         if label.upper() in ('ANIME', 'ANIME_LIST'):
             return MediaType.ANIME
         elif label.upper() in ('MANGA', 'MANGA_LIST'):
@@ -36,76 +40,109 @@ class MediaType(Enum):
             raise NotImplementedError('Error: Cannot convert "{}" to a MediaType'.format(label))
 
 
+class MediaStatus(Enum):
+    CURRENT=0
+    PLANNING=1
+    COMPLETED=2
+    DROPPED=3
+    PAUSED=4
+    REPEATING=5
+
+    @staticmethod
+    def from_str(label: str):
+        if label is None: raise TypeError
+
+        first_word = label.split(' ')[0].upper()
+
+        if first_word in ['READ', 'READING', 'WATCHED', 'WATCHING']:
+            return MediaStatus.CURRENT
+        elif first_word in ['PLANS', 'PLAN']:
+            return MediaStatus.PLANNING
+        elif first_word in ['COMPLETED']:
+            return MediaStatus.COMPLETED
+        elif first_word in ['DROPPED']:
+            return MediaStatus.DROPPED
+        elif first_word in ['PAUSED', 'ON-HOLD']:
+            return MediaStatus.PAUSED
+        elif first_word in ['REREAD', 'REREADING', 'REWATCHED', 'REWATCHING']:
+            return MediaStatus.REPEATING
+        else:
+            raise NotImplementedError('Error: Cannot convert "{}" to a MediaStatus'.format(label))
+
+
 class User():
-	data = None
-
-	def __init__(self,
-				  id			: int,
-				  service_id	: int,
-				  name			: str,
-				  servers		: List[int]):
-		self.id = id
-		self.service_id = service_id
-		self.name = name
-		self.servers = servers
+    data = None
+
+    def __init__(self,
+                  id			: int,
+                  service_id	: int,
+                  name			: str,
+                  servers		: List[int]):
+        self.id = id
+        self.service_id = service_id
+        self.name = name
+        self.servers = servers
 
 class Media():
-	def __init__(self,
-				 name		: str,
-				 url		: str,
-				 episodes	: str,
-				 image		: str,
-				 type		: MediaType):
-		self.name = name
-		self.url = url
-		self.episodes = episodes
-		self.image = image
-		self.type = type
-
-	@staticmethod
-	def get_number_episodes(activity): # TODO Dont work for MAL
-		media_type = MediaType.from_str(activity["type"])
-		episodes = '?'
-		if media_type == MediaType.ANIME:
-			episodes = activity["media"]["episodes"]
-		elif media_type == MediaType.MANGA:
-			episodes = activity["media"]["chapters"]
-		else:
-			raise NotImplementedError('Error: Unknown media type "{}"'.format(media_type))
-		if episodes is None:
-			episodes = '?'
-		return episodes
+    def __init__(self,
+                 name		: str,
+                 url		: str,
+                 episodes	: str,
+                 image		: str,
+                 type		: MediaType):
+        self.name = name
+        self.url = url
+        self.episodes = episodes
+        self.image = image
+        self.type = type
+
+    @staticmethod
+    def get_number_episodes(activity): # TODO Dont work for MAL
+        media_type = MediaType.from_str(activity["type"])
+        episodes = '?'
+        if media_type == MediaType.ANIME:
+            episodes = activity["media"]["episodes"]
+        elif media_type == MediaType.MANGA:
+            episodes = activity["media"]["chapters"]
+        else:
+            raise NotImplementedError('Error: Unknown media type "{}"'.format(media_type))
+        if episodes is None:
+            episodes = '?'
+        return episodes
 
 
 class Feed():
-	def __init__(self,
-				 service 		: Service,
-				 date_publication : datetime.datetime,
-				 user 			: User,
-				 status			: str, # TODO Need to change
-				 description	: str, # TODO Need to change
-				 media 			: Media
-				 ):
-		self.service = service
-		self.date_publication = date_publication
-		self.user = user
-		self.status = status
-		self.media = media
-		self.description = description
+    def __init__(self,
+                 service 		: Service,
+                 date_publication : datetime.datetime,
+                 user 			: User,
+                 status			: MediaStatus,
+                 description	: str, # TODO Need to change
+                 media 			: Media
+                 ):
+        self.service = service
+        self.date_publication = date_publication
+        self.user = user
+        self.status = status
+        self.media = media
+        self.description = description
 
 
 def replace_all(text : str, replace_dic : dict) -> str:
-	''' Replace multiple substrings from a string '''
-	
-	for replace_key, replace_value in replace_dic.items():
-		text = text.replace(replace_key, replace_value)
-	return text
+    ''' Replace multiple substrings from a string '''
+    
+    if text is None or replace_dic is None:
+        return text
+
+    for replace_key, replace_value in replace_dic.items():
+        text = text.replace(replace_key, replace_value)
+    return text
 
 
 def filter_name(name : str) -> str:
-	''' Escapes special characters from name '''
+    ''' Escapes special characters from name '''
 
-	dic = {
+    dic = {
         "♥": "\♥",
         "♀": "\♀",
         "♂": "\♂",
@@ -113,145 +150,147 @@ def filter_name(name : str) -> str:
         "☆": "\☆"
         }
 
-	return replace_all(name, dic)
+    return replace_all(name, dic)
 
 # Check if the show's name ends with a show type and truncate it
-def truncate_end_show(show):
-	show_types = (
+def truncate_end_show(media_name : str):
+    if media_name is None: return media_name
+
+    show_types = (
         '- TV',
-		'- Movie',
-		'- Special',
-		'- OVA',
-		'- ONA',
-		'- Manga',
-		'- Manhua',
-		'- Manhwa',
-		'- Novel',
-		'- One-Shot',
-		'- Doujinshi',
-		'- Music',
-		'- OEL',
-		'- Unknown'
+        '- Movie',
+        '- Special',
+        '- OVA',
+        '- ONA',
+        '- Manga',
+        '- Manhua',
+        '- Manhwa',
+        '- Novel',
+        '- One-Shot',
+        '- Doujinshi',
+        '- Music',
+        '- OEL',
+        '- Unknown'
     )
-    
-	for show_type in show_types:
-		if show.endswith(show_type):
-			new_show = show[:-len(show_type)]
-			# Check if space at the end
-			if new_show.endswith(' '):
-				new_show = new_show[:-1]
-			return new_show
-	return show
+
+    for show_type in show_types:
+        if media_name.endswith(show_type):
+            new_show = media_name[:-len(show_type)]
+            # Check if space at the end
+            if new_show.endswith(' '):
+                new_show = new_show[:-1]
+            return new_show
+    return media_name
 
 
 def get_channels(server_id: int) -> dict:
-	''' Returns the registered channels for a server '''
+    ''' Returns the registered channels for a server '''
 
-	if server_id is None:
-		return None
+    if server_id is None:
+        return None
 
-	# TODO Make generic execute
-	cursor = globals.conn.cursor(buffered=True, dictionary=True)
-	cursor.execute("SELECT channel FROM t_servers WHERE server = %s", [server_id])
-	channels = cursor.fetchall()
-	cursor.close()
-	return channels
+    # TODO Make generic execute
+    cursor = globals.conn.cursor(buffered=True, dictionary=True)
+    cursor.execute("SELECT channel FROM t_servers WHERE server = %s", [server_id])
+    channels = cursor.fetchall()
+    cursor.close()
+    return channels
 
 
 def is_server_in_db(server_id : str) -> bool:
-	''' Checks if server is registered in the database '''
+    ''' Checks if server is registered in the database '''
 
-	if server_id is None:
-		return False
+    if server_id is None:
+        return False
 
-	cursor = globals.conn.cursor(buffered=True)
-	cursor.execute("SELECT server FROM t_servers WHERE server=%s", [server_id])
-	data = cursor.fetchone()
-	cursor.close()
-	return data is not None
+    cursor = globals.conn.cursor(buffered=True)
+    cursor.execute("SELECT server FROM t_servers WHERE server=%s", [server_id])
+    data = cursor.fetchone()
+    cursor.close()
+    return data is not None
 
 
 def get_users() -> List[dict]:
-	''' Returns all registered users '''
+    ''' Returns all registered users '''
 
-	cursor = globals.conn.cursor(buffered=True, dictionary=True)
-	cursor.execute('SELECT {}, service, servers FROM t_users'.format(globals.DB_USER_NAME))
-	users = cursor.fetchall()
-	cursor.close()
-	return users
+    cursor = globals.conn.cursor(buffered=True, dictionary=True)
+    cursor.execute('SELECT {}, service, servers FROM t_users'.format(globals.DB_USER_NAME))
+    users = cursor.fetchall()
+    cursor.close()
+    return users
 
 def get_user_servers(user_name : str, service : Service) -> str:
-	''' Returns a list of every registered servers for a user of a specific service, as a string '''
+    ''' Returns a list of every registered servers for a user of a specific service, as a string '''
 
-	if user_name is None or service is None:
-		return
+    if user_name is None or service is None:
+        return
 
-	cursor = globals.conn.cursor(buffered=True, dictionary=True)
-	cursor.execute("SELECT servers FROM t_users WHERE LOWER({})=%s AND service=%s".format(globals.DB_USER_NAME),
-					 [user_name.lower(), service.value])
-	user_servers = cursor.fetchone()
-	cursor.close()
+    cursor = globals.conn.cursor(buffered=True, dictionary=True)
+    cursor.execute("SELECT servers FROM t_users WHERE LOWER({})=%s AND service=%s".format(globals.DB_USER_NAME),
+                     [user_name.lower(), service.value])
+    user_servers = cursor.fetchone()
+    cursor.close()
 
-	if user_servers is not None:
-		return user_servers["servers"]
-	return None
+    if user_servers is not None:
+        return user_servers["servers"]
+    return None
 
 
 def remove_server_from_servers(server : str, servers : str) -> str:
-	''' Removes the server from a comma-separated string containing multiple servers '''
+    ''' Removes the server from a comma-separated string containing multiple servers '''
 
-	servers_list = servers.split(',')
+    servers_list = servers.split(',')
 
-	# If the server is not found, return None
-	if server not in servers_list:
-		return None
+    # If the server is not found, return None
+    if server not in servers_list:
+        return None
 
-	# Remove every occurence of server
-	servers_list = [x for x in servers_list if x != server]
-	# Build server-free string
-	return ','.join(servers_list)
+    # Remove every occurence of server
+    servers_list = [x for x in servers_list if x != server]
+    # Build server-free string
+    return ','.join(servers_list)
 
 
 def delete_user_from_db(user_name : str, service : Service) -> bool:
-	''' Removes the user from the database '''
+    ''' Removes the user from the database '''
 
-	if user_name is None or service is None:
-		globals.logger.warning("Error while trying to delete user '{}' with service '{}'".format(user_name, service))
-		return False
+    if user_name is None or service is None:
+        globals.logger.warning("Error while trying to delete user '{}' with service '{}'".format(user_name, service))
+        return False
 
-	cursor = globals.conn.cursor(buffered=True)
-	cursor.execute("DELETE FROM t_users WHERE LOWER({}) = %s AND service=%s".format(globals.DB_USER_NAME),
-						 [user_name.lower(), service.value])
-	globals.conn.commit()
-	cursor.close()
-	return True
+    cursor = globals.conn.cursor(buffered=True)
+    cursor.execute("DELETE FROM t_users WHERE LOWER({}) = %s AND service=%s".format(globals.DB_USER_NAME),
+                         [user_name.lower(), service.value])
+    globals.conn.commit()
+    cursor.close()
+    return True
 
 
 def update_user_servers_db(user_name : str, service : Service, servers : str) -> bool:
-	if user_name is None or service is None or servers is None:
-		globals.logger.warning("Error while trying to update user's servers. User '{}' with service '{}' and servers '{}'".format(user_name, service, servers))
-		return False
+    if user_name is None or service is None or servers is None:
+        globals.logger.warning("Error while trying to update user's servers. User '{}' with service '{}' and servers '{}'".format(user_name, service, servers))
+        return False
 
-	cursor = globals.conn.cursor(buffered=True)
-	cursor.execute("UPDATE t_users SET servers = %s WHERE LOWER({}) = %s AND service=%s".format(globals.DB_USER_NAME),
-	 					 [servers, user_name.lower(), service.value])
-	globals.conn.commit()
-	cursor.close()
-	return True
+    cursor = globals.conn.cursor(buffered=True)
+    cursor.execute("UPDATE t_users SET servers = %s WHERE LOWER({}) = %s AND service=%s".format(globals.DB_USER_NAME),
+                          [servers, user_name.lower(), service.value])
+    globals.conn.commit()
+    cursor.close()
+    return True
 
 
 def insert_user_into_db(user_name : str, service : Service, servers : str) -> bool:
-	''' Add the user to the database '''
-
-	if user_name is None or service is None or servers is None:
-		globals.logger.warning("Error while trying to add user '{}' with service '{}' and servers '{}'".format(user_name, service, servers))
-		return False
-
-	cursor = globals.conn.cursor(buffered=True)
-	cursor.execute("INSERT INTO t_users ({}, service, servers) VALUES (%s, %s, %s)".format(globals.DB_USER_NAME),
-						[user_name, service.value, servers])
-	globals.conn.commit()
-	cursor.close()
-	return True
+    ''' Add the user to the database '''
+
+    if user_name is None or service is None or servers is None:
+        globals.logger.warning("Error while trying to add user '{}' with service '{}' and servers '{}'".format(user_name, service, servers))
+        return False
+
+    cursor = globals.conn.cursor(buffered=True)
+    cursor.execute("INSERT INTO t_users ({}, service, servers) VALUES (%s, %s, %s)".format(globals.DB_USER_NAME),
+                        [user_name, service.value, servers])
+    globals.conn.commit()
+    cursor.close()
+    return True
 
 # TODO Create a Feed class instead of sending a lot of parameters

+ 175 - 34
tests/test_utils.py

@@ -1,24 +1,27 @@
 import pytest
 
-from myanimebot.utils import MediaType, Service, filter_name, replace_all, truncate_end_show
+from myanimebot.utils import Media, MediaStatus, MediaType, Service, filter_name, replace_all, truncate_end_show
 from myanimebot.globals import SERVICE_MAL, SERVICE_ANILIST
 
 def test_MediaType_from_str():
-    # Testing for ANIME
-    assert MediaType.ANIME == MediaType.from_str('ANIME')
-    assert MediaType.ANIME == MediaType.from_str('anime')
-    assert MediaType.ANIME == MediaType.from_str('ANiMe')
-    assert MediaType.ANIME == MediaType.from_str('anime_list')
-    assert MediaType.ANIME == MediaType.from_str('ANIME_LIST')
-    assert MediaType.ANIME == MediaType.from_str('ANiMe_LiST')
-
-    # Testing for MANGA
-    assert MediaType.MANGA == MediaType.from_str('MANGA')
-    assert MediaType.MANGA == MediaType.from_str('manga')
-    assert MediaType.MANGA == MediaType.from_str('ManGA')
-    assert MediaType.MANGA == MediaType.from_str('manga_list')
-    assert MediaType.MANGA == MediaType.from_str('MANGA_LIST')
-    assert MediaType.MANGA == MediaType.from_str('ManGA_LiSt')
+    try:
+        # Testing for ANIME
+        assert MediaType.ANIME == MediaType.from_str('ANIME')
+        assert MediaType.ANIME == MediaType.from_str('anime')
+        assert MediaType.ANIME == MediaType.from_str('ANiMe')
+        assert MediaType.ANIME == MediaType.from_str('anime_list')
+        assert MediaType.ANIME == MediaType.from_str('ANIME_LIST')
+        assert MediaType.ANIME == MediaType.from_str('ANiMe_LiST')
+
+        # Testing for MANGA
+        assert MediaType.MANGA == MediaType.from_str('MANGA')
+        assert MediaType.MANGA == MediaType.from_str('manga')
+        assert MediaType.MANGA == MediaType.from_str('ManGA')
+        assert MediaType.MANGA == MediaType.from_str('manga_list')
+        assert MediaType.MANGA == MediaType.from_str('MANGA_LIST')
+        assert MediaType.MANGA == MediaType.from_str('ManGA_LiSt')
+    except Exception as e:
+        pytest.fail("Unexpected Exception : {}".format(e))
 
     # Testing incorrect MediaType
     with pytest.raises(NotImplementedError, match='Cannot convert "TEST_LIST" to a MediaType'):
@@ -31,26 +34,33 @@ def test_MediaType_from_str():
         MediaType.from_str('ANIMU')
     with pytest.raises(NotImplementedError, match='Cannot convert "mango" to a MediaType'):
         MediaType.from_str('mango')
+    with pytest.raises(NotImplementedError):
+        MediaType.from_str('')
+    with pytest.raises(TypeError):
+        MediaType.from_str(None)
 
 
 def test_Service_from_str():
-    # Testing for MAL
-    assert Service.MAL == Service.from_str('MAL')
-    assert Service.MAL == Service.from_str('MYANIMELIST')
-    assert Service.MAL == Service.from_str(SERVICE_MAL)
-    assert Service.MAL == Service.from_str('MaL')
-    assert Service.MAL == Service.from_str('mal')
-    assert Service.MAL == Service.from_str('myanimelist')
-    assert Service.MAL == Service.from_str('mYANimEliST')
-
-    # Testing for Anilist
-    assert Service.ANILIST == Service.from_str('AniList')
-    assert Service.ANILIST == Service.from_str('AL')
-    assert Service.ANILIST == Service.from_str(SERVICE_ANILIST)
-    assert Service.ANILIST == Service.from_str('ANILIST')
-    assert Service.ANILIST == Service.from_str('anilist')
-    assert Service.ANILIST == Service.from_str('al')
-    assert Service.ANILIST == Service.from_str('Al')
+    try:
+        # Testing for MAL
+        assert Service.MAL == Service.from_str('MAL')
+        assert Service.MAL == Service.from_str('MYANIMELIST')
+        assert Service.MAL == Service.from_str(SERVICE_MAL)
+        assert Service.MAL == Service.from_str('MaL')
+        assert Service.MAL == Service.from_str('mal')
+        assert Service.MAL == Service.from_str('myanimelist')
+        assert Service.MAL == Service.from_str('mYANimEliST')
+
+        # Testing for Anilist
+        assert Service.ANILIST == Service.from_str('AniList')
+        assert Service.ANILIST == Service.from_str('AL')
+        assert Service.ANILIST == Service.from_str(SERVICE_ANILIST)
+        assert Service.ANILIST == Service.from_str('ANILIST')
+        assert Service.ANILIST == Service.from_str('anilist')
+        assert Service.ANILIST == Service.from_str('al')
+        assert Service.ANILIST == Service.from_str('Al')
+    except Exception as e:
+        pytest.fail("Unexpected Exception : {}".format(e))
 
     # Testing incorrect Services
     with pytest.raises(NotImplementedError, match='Cannot convert "Kitsu" to a Service'):
@@ -61,11 +71,16 @@ def test_Service_from_str():
         Service.from_str('ani list')
     with pytest.raises(NotImplementedError, match='Cannot convert "mla" to a Service'):
         Service.from_str('mla')
+    with pytest.raises(TypeError):
+        Service.from_str(None)
 
 
 def test_replace_all():
     with pytest.raises(AttributeError):
-        replace_all("texte", []) == "texte"
+        replace_all("texte", [])
+
+    assert replace_all(None, {}) == None
+    assert replace_all("toto", None) == "toto"
 
     assert replace_all("texte", {}) == "texte"
     assert replace_all("I is a string", {"is": "am"}) == "I am a string"
@@ -97,6 +112,7 @@ def test_filter_name():
     assert filter_name("♥ Bonjour ♥") == "\♥ Bonjour \♥"
     assert filter_name("♥♪☆♂☆♀♀ ♥") == "\♥\♪\☆\♂\☆\♀\♀ \♥"
     assert filter_name("♣") == "♣"
+    assert filter_name(None) == None
 
 
 def test_truncate_end_show():
@@ -119,4 +135,129 @@ def test_truncate_end_show():
     assert truncate_end_show("Toto- Music") == "Toto"
     assert truncate_end_show("Titi-Music") == "Titi-Music"
     assert truncate_end_show("- Music") == ""
+    assert truncate_end_show(None) == None
+
+
+def test_media_status():
+    print('testing')
+    # Testing Current
+    try:
+        current = MediaStatus.CURRENT
+        assert MediaStatus.from_str('read') == current
+        assert MediaStatus.from_str('READ') == current
+        assert MediaStatus.from_str('ReaD') == current
+        assert MediaStatus.from_str('Reading') == current
+        assert MediaStatus.from_str('READING') == current
+        assert MediaStatus.from_str('reading') == current
+        assert MediaStatus.from_str('watched') == current
+        assert MediaStatus.from_str('WATCHING') == current
+        assert MediaStatus.from_str('WATCHED') == current
+        assert MediaStatus.from_str('watChing') == current
+    except Exception as e:
+        pytest.fail("Unexpected Exception : {}".format(e))
+
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('Watchh')
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('Watches')
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('Red')
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('Watc hed')
+
+    # Testing Planning
+    try:
+        planning = MediaStatus.PLANNING
+        assert MediaStatus.from_str('PLANS') == planning
+        assert MediaStatus.from_str('plans') == planning
+        assert MediaStatus.from_str('PlAns') == planning
+        assert MediaStatus.from_str('Plan') == planning
+        assert MediaStatus.from_str('plan') == planning
+        assert MediaStatus.from_str('PLAN') == planning
+    except Exception as e:
+        pytest.fail("Unexpected Exception : {}".format(e))
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('planned')
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('Pla')
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('pla n')
+
+    # Testing Completed
+    try:
+        completed = MediaStatus.COMPLETED
+        assert MediaStatus.from_str('Completed') == completed
+        assert MediaStatus.from_str('COMPLETED') == completed
+        assert MediaStatus.from_str('completed') == completed
+    except Exception as e:
+        pytest.fail("Unexpected Exception : {}".format(e))
+
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('Complete')
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('Compl eted')
+
+    # Testing Dropped
+    try:
+        dropped = MediaStatus.DROPPED
+        assert MediaStatus.from_str('DroPPed') == dropped
+        assert MediaStatus.from_str('DROPPED') == dropped
+        assert MediaStatus.from_str('dropped') == dropped
+    except Exception as e:
+        pytest.fail("Unexpected Exception : {}".format(e))
+
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('Drop')
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('Drops')
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str(' Dropped')
+
+    # Testing Paused
+    try:
+        paused = MediaStatus.PAUSED
+        assert MediaStatus.from_str('PAUSED') == paused
+        assert MediaStatus.from_str('paused') == paused
+        assert MediaStatus.from_str('PaUSed') == paused
+        assert MediaStatus.from_str('ON-HOLD') == paused
+        assert MediaStatus.from_str('on-hold') == paused
+        assert MediaStatus.from_str('ON-hold') == paused
+        assert MediaStatus.from_str('on-HOLD') == paused
+    except Exception as e:
+        pytest.fail("Unexpected Exception : {}".format(e))
+
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('pauses')
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('on hold')
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('onhold')
+
+    # Testing Repeating
+    try:
+        repeating = MediaStatus.REPEATING
+        assert MediaStatus.from_str('reread') == repeating
+        assert MediaStatus.from_str('REREAD') == repeating
+        assert MediaStatus.from_str('reReaD') == repeating
+        assert MediaStatus.from_str('reReading') == repeating
+        assert MediaStatus.from_str('REREADING') == repeating
+        assert MediaStatus.from_str('rereading') == repeating
+        assert MediaStatus.from_str('rewatched') == repeating
+        assert MediaStatus.from_str('REWATCHING') == repeating
+        assert MediaStatus.from_str('reWATCHED') == repeating
+        assert MediaStatus.from_str('RewatChing') == repeating
+    except Exception as e:
+        pytest.fail("Unexpected Exception : {}".format(e))
+
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('rreread')
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('rewatches')
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('re read')
 
+    # Testing incorrect uses cases
+    with pytest.raises(NotImplementedError):
+        MediaStatus.from_str('')
+    with pytest.raises(TypeError):
+        MediaStatus.from_str(None)