2021-06-12 15:29:17 +00:00
import logging , os , re , signal , sys , time , traceback
2021-02-25 21:59:56 +00:00
from datetime import datetime
2021-05-19 21:30:20 +00:00
from pathvalidate import is_valid_filename , sanitize_filename
2021-05-11 01:22:18 +00:00
from plexapi . exceptions import BadRequest , NotFound , Unauthorized
2021-01-20 21:37:59 +00:00
try :
import msvcrt
windows = True
except ModuleNotFoundError :
windows = False
logger = logging . getLogger ( " Plex Meta Manager " )
class TimeoutExpired ( Exception ) :
pass
class Failed ( Exception ) :
pass
2021-06-30 15:02:55 +00:00
class ImageData :
2021-06-12 15:29:17 +00:00
def __init__ ( self , attribute , location , prefix = " " , is_poster = True , is_url = True ) :
self . attribute = attribute
self . location = location
self . prefix = prefix
self . is_poster = is_poster
self . is_url = is_url
self . compare = location if is_url else os . stat ( location ) . st_size
self . message = f " { prefix } { ' poster ' if is_poster else ' background ' } to [ { ' URL ' if is_url else ' File ' } ] { location } "
2021-01-20 21:37:59 +00:00
def retry_if_not_failed ( exception ) :
return not isinstance ( exception , Failed )
2021-05-11 01:22:18 +00:00
def retry_if_not_plex ( exception ) :
return not isinstance ( exception , ( BadRequest , NotFound , Unauthorized ) )
2021-02-24 06:42:58 +00:00
separating_character = " = "
2021-01-20 21:37:59 +00:00
screen_width = 100
2021-05-26 13:25:32 +00:00
spacing = 0
2021-01-20 21:37:59 +00:00
days_alias = {
" monday " : 0 , " mon " : 0 , " m " : 0 ,
" tuesday " : 1 , " tues " : 1 , " tue " : 1 , " tu " : 1 , " t " : 1 ,
" wednesday " : 2 , " wed " : 2 , " w " : 2 ,
" thursday " : 3 , " thurs " : 3 , " thur " : 3 , " thu " : 3 , " th " : 3 , " r " : 3 ,
" friday " : 4 , " fri " : 4 , " f " : 4 ,
" saturday " : 5 , " sat " : 5 , " s " : 5 ,
" sunday " : 6 , " sun " : 6 , " su " : 6 , " u " : 6
}
pretty_days = {
0 : " Monday " ,
1 : " Tuesday " ,
2 : " Wednesday " ,
3 : " Thursday " ,
4 : " Friday " ,
5 : " Saturday " ,
6 : " Sunday "
}
pretty_months = {
1 : " January " ,
2 : " February " ,
3 : " March " ,
4 : " April " ,
5 : " May " ,
6 : " June " ,
7 : " July " ,
8 : " August " ,
9 : " September " ,
10 : " October " ,
11 : " November " ,
12 : " December "
}
pretty_seasons = {
" winter " : " Winter " ,
" spring " : " Spring " ,
" summer " : " Summer " ,
" fall " : " Fall "
}
pretty_names = {
" anidb_id " : " AniDB ID " ,
" anidb_relation " : " AniDB Relation " ,
" anidb_popular " : " AniDB Popular " ,
2021-03-20 04:22:51 +00:00
" anilist_genre " : " AniList Genre " ,
2021-03-10 16:51:27 +00:00
" anilist_id " : " AniList ID " ,
" anilist_popular " : " AniList Popular " ,
" anilist_relations " : " AniList Relations " ,
" anilist_season " : " AniList Season " ,
" anilist_studio " : " AniList Studio " ,
2021-03-20 04:22:51 +00:00
" anilist_tag " : " AniList Tag " ,
2021-03-10 16:51:27 +00:00
" anilist_top_rated " : " AniList Top Rated " ,
2021-06-14 15:27:33 +00:00
" icheckmovies_list " : " I Check Movies List " ,
2021-01-20 21:37:59 +00:00
" imdb_list " : " IMDb List " ,
" imdb_id " : " IMDb ID " ,
2021-03-04 20:05:51 +00:00
" letterboxd_list " : " Letterboxd List " ,
2021-03-08 21:54:19 +00:00
" letterboxd_list_details " : " Letterboxd List " ,
2021-01-20 21:37:59 +00:00
" mal_id " : " MyAnimeList ID " ,
" mal_all " : " MyAnimeList All " ,
" mal_airing " : " MyAnimeList Airing " ,
" mal_upcoming " : " MyAnimeList Upcoming " ,
" mal_tv " : " MyAnimeList TV " ,
" mal_ova " : " MyAnimeList OVA " ,
" mal_movie " : " MyAnimeList Movie " ,
" mal_special " : " MyAnimeList Special " ,
" mal_popular " : " MyAnimeList Popular " ,
" mal_favorite " : " MyAnimeList Favorite " ,
" mal_season " : " MyAnimeList Season " ,
" mal_suggested " : " MyAnimeList Suggested " ,
" mal_userlist " : " MyAnimeList Userlist " ,
" plex_all " : " Plex All " ,
" plex_collection " : " Plex Collection " ,
" plex_search " : " Plex Search " ,
" tautulli_popular " : " Tautulli Popular " ,
" tautulli_watched " : " Tautulli Watched " ,
2021-02-28 02:47:41 +00:00
" tmdb_actor " : " TMDb Actor " ,
2021-03-02 05:28:32 +00:00
" tmdb_actor_details " : " TMDb Actor " ,
2021-01-20 21:37:59 +00:00
" tmdb_collection " : " TMDb Collection " ,
" tmdb_collection_details " : " TMDb Collection " ,
" tmdb_company " : " TMDb Company " ,
2021-02-28 02:47:41 +00:00
" tmdb_crew " : " TMDb Crew " ,
2021-03-02 05:28:32 +00:00
" tmdb_crew_details " : " TMDb Crew " ,
2021-02-28 02:47:41 +00:00
" tmdb_director " : " TMDb Director " ,
2021-03-02 05:28:32 +00:00
" tmdb_director_details " : " TMDb Director " ,
2021-01-20 21:37:59 +00:00
" tmdb_discover " : " TMDb Discover " ,
" tmdb_keyword " : " TMDb Keyword " ,
" tmdb_list " : " TMDb List " ,
" tmdb_list_details " : " TMDb List " ,
" tmdb_movie " : " TMDb Movie " ,
" tmdb_movie_details " : " TMDb Movie " ,
" tmdb_network " : " TMDb Network " ,
" tmdb_now_playing " : " TMDb Now Playing " ,
2021-01-26 21:47:46 +00:00
" tmdb_person " : " TMDb Person " ,
2021-01-20 21:37:59 +00:00
" tmdb_popular " : " TMDb Popular " ,
2021-02-28 02:47:41 +00:00
" tmdb_producer " : " TMDb Producer " ,
2021-03-02 05:28:32 +00:00
" tmdb_producer_details " : " TMDb Producer " ,
2021-01-20 21:37:59 +00:00
" tmdb_show " : " TMDb Show " ,
" tmdb_show_details " : " TMDb Show " ,
" tmdb_top_rated " : " TMDb Top Rated " ,
" tmdb_trending_daily " : " TMDb Trending Daily " ,
" tmdb_trending_weekly " : " TMDb Trending Weekly " ,
2021-02-28 02:47:41 +00:00
" tmdb_writer " : " TMDb Writer " ,
2021-03-02 05:28:32 +00:00
" tmdb_writer_details " : " TMDb Writer " ,
2021-03-09 07:50:44 +00:00
" trakt_collected " : " Trakt Collected " ,
2021-03-27 07:30:07 +00:00
" trakt_collection " : " Trakt Collection " ,
2021-01-20 21:37:59 +00:00
" trakt_list " : " Trakt List " ,
2021-03-08 17:02:40 +00:00
" trakt_list_details " : " Trakt List " ,
2021-03-09 07:50:44 +00:00
" trakt_popular " : " Trakt Popular " ,
" trakt_recommended " : " Trakt Recommended " ,
2021-01-20 21:37:59 +00:00
" trakt_trending " : " Trakt Trending " ,
2021-03-09 07:50:44 +00:00
" trakt_watched " : " Trakt Watched " ,
2021-01-20 21:37:59 +00:00
" trakt_watchlist " : " Trakt Watchlist " ,
" tvdb_list " : " TVDb List " ,
2021-03-08 17:02:40 +00:00
" tvdb_list_details " : " TVDb List " ,
2021-01-20 21:37:59 +00:00
" tvdb_movie " : " TVDb Movie " ,
2021-03-08 17:02:40 +00:00
" tvdb_movie_details " : " TVDb Movie " ,
" tvdb_show " : " TVDb Show " ,
" tvdb_show_details " : " TVDb Show "
2021-01-20 21:37:59 +00:00
}
pretty_ids = {
" anidbid " : " AniDB " ,
" imdbid " : " IMDb " ,
" mal_id " : " MyAnimeList " ,
" themoviedb_id " : " TMDb " ,
" thetvdb_id " : " TVDb " ,
" tvdbid " : " TVDb "
}
2021-02-24 06:44:06 +00:00
def tab_new_lines ( data ) :
return str ( data ) . replace ( " \n " , " \n | \t " ) if " \n " in str ( data ) else str ( data )
2021-01-20 21:37:59 +00:00
def make_ordinal ( n ) :
n = int ( n )
suffix = [ " th " , " st " , " nd " , " rd " , " th " ] [ min ( n % 10 , 4 ) ]
if 11 < = ( n % 100 ) < = 13 :
suffix = " th "
return str ( n ) + suffix
def choose_from_list ( datalist , description , data = None , list_type = " title " , exact = False ) :
if len ( datalist ) > 0 :
if len ( datalist ) == 1 and ( description != " collection " or datalist [ 0 ] . title == data ) :
return datalist [ 0 ]
2021-02-24 06:44:06 +00:00
zero_option = f " Create New Collection: { data } " if description == " collection " else " Do Nothing "
message = f " Multiple { description } s Found \n 0) { zero_option } "
2021-01-20 21:37:59 +00:00
for i , d in enumerate ( datalist , 1 ) :
if list_type == " title " :
if d . title == data :
return d
2021-02-24 06:44:06 +00:00
message + = f " \n { i } ) { d . title } "
2021-01-20 21:37:59 +00:00
else :
2021-02-24 06:44:06 +00:00
message + = f " \n { i } ) [ { d [ 0 ] } ] { d [ 1 ] } "
2021-01-20 21:37:59 +00:00
if exact :
return None
print_multiline ( message , info = True )
while True :
try :
2021-02-24 06:44:06 +00:00
selection = int ( logger_input ( f " Choose { description } number " ) ) - 1
2021-01-20 21:37:59 +00:00
if selection > = 0 : return datalist [ selection ]
elif selection == - 1 : return None
2021-02-24 06:44:06 +00:00
else : logger . info ( f " Invalid { description } number " )
except IndexError : logger . info ( f " Invalid { description } number " )
2021-01-20 21:37:59 +00:00
except TimeoutExpired :
if list_type == " title " :
2021-02-24 06:44:06 +00:00
logger . warning ( f " Input Timeout: using { data } " )
2021-01-20 21:37:59 +00:00
return None
else :
2021-02-24 06:44:06 +00:00
logger . warning ( f " Input Timeout: using { datalist [ 0 ] [ 1 ] } " )
2021-02-28 23:06:04 +00:00
return datalist [ 0 ]
2021-01-20 21:37:59 +00:00
else :
return None
2021-04-11 15:31:59 +00:00
def get_bool ( method_name , method_data ) :
if isinstance ( method_data , bool ) :
return method_data
elif str ( method_data ) . lower ( ) in [ " t " , " true " ] :
return True
elif str ( method_data ) . lower ( ) in [ " f " , " false " ] :
return False
else :
raise Failed ( f " Collection Error: { method_name } attribute: { method_data } invalid must be either true or false " )
2021-05-08 23:49:55 +00:00
def compile_list ( data ) :
if isinstance ( data , list ) :
text = " "
for item in data :
text + = f " { ' , ' if len ( text ) > 0 else ' ' } { item } "
return text
else :
return data
def get_list ( data , lower = False , split = True , int_list = False ) :
2021-05-25 22:22:59 +00:00
if data is None : return None
elif isinstance ( data , list ) : return data
2021-01-20 21:37:59 +00:00
elif isinstance ( data , dict ) : return [ data ]
2021-02-17 06:10:50 +00:00
elif split is False : return [ str ( data ) ]
2021-02-16 04:54:47 +00:00
elif lower is True : return [ d . strip ( ) . lower ( ) for d in str ( data ) . split ( " , " ) ]
2021-05-08 23:49:55 +00:00
elif int_list is True : return [ int ( d . strip ( ) ) for d in str ( data ) . split ( " , " ) ]
2021-02-16 04:54:47 +00:00
else : return [ d . strip ( ) for d in str ( data ) . split ( " , " ) ]
2021-01-20 21:37:59 +00:00
def get_int_list ( data , id_type ) :
values = get_list ( data )
int_values = [ ]
for value in values :
try : int_values . append ( regex_first_int ( value , id_type ) )
except Failed as e : logger . error ( e )
return int_values
2021-03-26 05:43:11 +00:00
def get_year_list ( data , current_year , method ) :
2021-01-20 21:37:59 +00:00
final_years = [ ]
2021-03-26 05:43:11 +00:00
values = get_list ( data )
2021-01-20 21:37:59 +00:00
for value in values :
2021-03-26 05:43:11 +00:00
final_years . append ( check_year ( value , current_year , method ) )
2021-01-20 21:37:59 +00:00
return final_years
2021-02-25 21:59:56 +00:00
def check_year ( year , current_year , method ) :
return check_number ( year , method , minimum = 1800 , maximum = current_year )
def check_number ( value , method , number_type = " int " , minimum = None , maximum = None ) :
if number_type == " int " :
try : num_value = int ( str ( value ) )
except ValueError : raise Failed ( f " Collection Error: { method } : { value } must be an integer " )
elif number_type == " float " :
try : num_value = float ( str ( value ) )
except ValueError : raise Failed ( f " Collection Error: { method } : { value } must be a number " )
else : raise Failed ( f " Number Type: { number_type } invalid " )
if minimum is not None and maximum is not None and ( num_value < minimum or num_value > maximum ) :
raise Failed ( f " Collection Error: { method } : { num_value } must be between { minimum } and { maximum } " )
elif minimum is not None and num_value < minimum :
raise Failed ( f " Collection Error: { method } : { num_value } is less then { minimum } " )
elif maximum is not None and num_value > maximum :
raise Failed ( f " Collection Error: { method } : { num_value } is greater then { maximum } " )
else :
return num_value
2021-07-21 19:25:29 +00:00
def validate_date ( date_text , method , return_as = None ) :
try : date_obg = datetime . strptime ( str ( date_text ) , " % Y- % m- %d " if " - " in str ( date_text ) else " % m/ %d / % Y " )
except ValueError : raise Failed ( f " Collection Error: { method } : { date_text } must match pattern YYYY-MM-DD (e.g. 2020-12-25) or MM/DD/YYYY (e.g. 12/25/2020) " )
return datetime . strftime ( date_obg , return_as ) if return_as else date_obg
2021-02-25 21:59:56 +00:00
2021-01-20 21:37:59 +00:00
def logger_input ( prompt , timeout = 60 ) :
if windows : return windows_input ( prompt , timeout )
elif hasattr ( signal , " SIGALRM " ) : return unix_input ( prompt , timeout )
else : raise SystemError ( " Input Timeout not supported on this system " )
2021-07-14 14:47:20 +00:00
def header ( language = " en-US,en;q=0.5 " ) :
return { " Accept-Language " : language , " User-Agent " : " Mozilla/5.0 x64 " }
2021-01-20 21:37:59 +00:00
def alarm_handler ( signum , frame ) :
raise TimeoutExpired
def unix_input ( prompt , timeout = 60 ) :
2021-02-24 06:44:06 +00:00
prompt = f " | { prompt } : "
2021-01-20 21:37:59 +00:00
signal . signal ( signal . SIGALRM , alarm_handler )
signal . alarm ( timeout )
2021-07-04 04:13:06 +00:00
try : return input ( prompt )
except EOFError : raise Failed ( " Input Failed " )
finally : signal . alarm ( 0 )
2021-01-20 21:37:59 +00:00
def old_windows_input ( prompt , timeout = 60 , timer = time . monotonic ) :
2021-02-24 06:44:06 +00:00
prompt = f " | { prompt } : "
2021-01-20 21:37:59 +00:00
sys . stdout . write ( prompt )
sys . stdout . flush ( )
endtime = timer ( ) + timeout
result = [ ]
while timer ( ) < endtime :
if msvcrt . kbhit ( ) :
result . append ( msvcrt . getwche ( ) )
if result [ - 1 ] == " \n " :
out = " " . join ( result [ : - 1 ] )
2021-02-24 06:44:06 +00:00
logger . debug ( f " { prompt [ 2 : ] } { out } " )
2021-01-20 21:37:59 +00:00
return out
time . sleep ( 0.04 )
raise TimeoutExpired
def windows_input ( prompt , timeout = 5 ) :
2021-02-24 06:44:06 +00:00
sys . stdout . write ( f " | { prompt } : " )
2021-01-20 21:37:59 +00:00
sys . stdout . flush ( )
result = [ ]
start_time = time . time ( )
while True :
if msvcrt . kbhit ( ) :
2021-02-24 06:42:58 +00:00
char = msvcrt . getwche ( )
if ord ( char ) == 13 : # enter_key
2021-01-20 21:37:59 +00:00
out = " " . join ( result )
print ( " " )
2021-02-24 06:44:06 +00:00
logger . debug ( f " { prompt } : { out } " )
2021-01-20 21:37:59 +00:00
return out
2021-02-24 06:42:58 +00:00
elif ord ( char ) > = 32 : #space_char
result . append ( char )
2021-01-20 21:37:59 +00:00
if ( time . time ( ) - start_time ) > timeout :
print ( " " )
raise TimeoutExpired
def print_multiline ( lines , info = False , warning = False , error = False , critical = False ) :
2021-02-21 17:01:10 +00:00
for i , line in enumerate ( str ( lines ) . split ( " \n " ) ) :
2021-01-20 21:37:59 +00:00
if critical : logger . critical ( line )
elif error : logger . error ( line )
elif warning : logger . warning ( line )
elif info : logger . info ( line )
else : logger . debug ( line )
if i == 0 :
logger . handlers [ 1 ] . setFormatter ( logging . Formatter ( " " * 65 + " | %(message)s " ) )
logger . handlers [ 1 ] . setFormatter ( logging . Formatter ( " [ %(asctime)s ] %(filename)-27s %(levelname)-10s | %(message)s " ) )
def print_stacktrace ( ) :
print_multiline ( traceback . format_exc ( ) )
def my_except_hook ( exctype , value , tb ) :
for line in traceback . format_exception ( etype = exctype , value = value , tb = tb ) :
print_multiline ( line , critical = True )
def get_id_from_imdb_url ( imdb_url ) :
match = re . search ( " (tt \\ d+) " , str ( imdb_url ) )
if match : return match . group ( 1 )
2021-02-24 06:44:06 +00:00
else : raise Failed ( f " Regex Error: Failed to parse IMDb ID from IMDb URL: { imdb_url } " )
2021-01-20 21:37:59 +00:00
def regex_first_int ( data , id_type , default = None ) :
match = re . search ( " ( \\ d+) " , str ( data ) )
if match :
return int ( match . group ( 1 ) )
elif default :
2021-02-24 06:44:06 +00:00
logger . warning ( f " Regex Warning: Failed to parse { id_type } from { data } using { default } as default " )
2021-01-20 21:37:59 +00:00
return int ( default )
else :
2021-02-24 06:44:06 +00:00
raise Failed ( f " Regex Error: Failed to parse { id_type } from { data } " )
2021-01-20 21:37:59 +00:00
2021-05-24 03:38:46 +00:00
def centered ( text , sep = " " ) :
2021-01-20 21:37:59 +00:00
if len ( text ) > screen_width - 2 :
2021-06-11 14:26:11 +00:00
return text
2021-01-20 21:37:59 +00:00
space = screen_width - len ( text ) - 2
2021-05-24 03:38:46 +00:00
text = f " { text } "
2021-01-20 21:37:59 +00:00
if space % 2 == 1 :
2021-05-24 03:38:46 +00:00
text + = sep
2021-01-20 21:37:59 +00:00
space - = 1
2021-05-24 03:38:46 +00:00
side = int ( space / 2 ) - 1
final_text = f " { sep * side } { text } { sep * side } "
2021-03-21 23:00:37 +00:00
return final_text
2021-01-20 21:37:59 +00:00
2021-05-24 03:38:46 +00:00
def separator ( text = None , space = True , border = True , debug = False ) :
sep = " " if space else separating_character
2021-05-19 17:12:34 +00:00
for handler in logger . handlers :
apply_formatter ( handler , border = False )
2021-05-24 03:38:46 +00:00
border_text = f " | { separating_character * screen_width } | "
if border and debug :
logger . debug ( border_text )
elif border :
logger . info ( border_text )
2021-01-20 21:37:59 +00:00
if text :
2021-02-28 03:56:49 +00:00
text_list = text . split ( " \n " )
for t in text_list :
2021-05-24 03:38:46 +00:00
logger . info ( f " | { sep } { centered ( t , sep = sep ) } { sep } | " )
if border and debug :
logger . debug ( border_text )
elif border :
logger . info ( border_text )
2021-05-19 17:12:34 +00:00
for handler in logger . handlers :
apply_formatter ( handler )
def apply_formatter ( handler , border = True ) :
text = f " | %(message)- { screen_width - 2 } s | " if border else f " %(message)- { screen_width - 2 } s "
2021-05-19 21:30:20 +00:00
if isinstance ( handler , logging . handlers . RotatingFileHandler ) :
2021-05-19 17:12:34 +00:00
text = f " [%(asctime)s] %(filename)-27s %(levelname)-10s { text } "
handler . setFormatter ( logging . Formatter ( text ) )
2021-01-20 21:37:59 +00:00
2021-05-26 13:25:32 +00:00
def adjust_space ( display_title ) :
display_title = str ( display_title )
space_length = spacing - len ( display_title )
if space_length > 0 :
display_title + = " " * space_length
return display_title
def print_return ( text ) :
print ( adjust_space ( f " | { text } " ) , end = " \r " )
global spacing
spacing = len ( text ) + 2
2021-01-20 21:37:59 +00:00
2021-05-26 13:25:32 +00:00
def print_end ( ) :
print ( adjust_space ( " " ) , end = " \r " )
global spacing
spacing = 0
2021-05-19 21:30:20 +00:00
def validate_filename ( filename ) :
if is_valid_filename ( filename ) :
2021-05-20 20:38:48 +00:00
return filename , None
2021-05-19 21:30:20 +00:00
else :
mapping_name = sanitize_filename ( filename )
2021-05-20 20:38:48 +00:00
return mapping_name , f " Log Folder Name: { filename } is invalid using { mapping_name } "
2021-07-06 15:46:29 +00:00
def is_locked ( filepath ) :
locked = None
file_object = None
if os . path . exists ( filepath ) :
try :
file_object = open ( filepath , ' a ' , 8 )
if file_object :
locked = False
except IOError as message :
locked = True
finally :
if file_object :
file_object . close ( )
return locked
2021-07-22 21:00:45 +00:00
def validate_dict_list ( method_name , data ) :
final_list = [ ]
for dict_data in get_list ( data ) :
if isinstance ( dict_data , dict ) :
final_list . append ( ( dict_data , { dm . lower ( ) : dm for dm in dict_data } ) )
else :
raise Failed ( f " Collection Error: { method_name } attribute is not a dictionary: { dict_data } " )
return final_list
def parse_int ( method , data , default = 10 , minimum = 1 , maximum = None ) :
list_count = regex_first_int ( data , " List Size " , default = default )
if maximum is None and list_count < minimum :
logger . warning ( f " Collection Warning: { method } must an integer >= { minimum } using { default } as default " )
elif maximum is not None and ( list_count < minimum or list_count > maximum ) :
logger . warning ( f " Collection Warning: { method } must an integer between { minimum } and { maximum } using { default } as default " )
else :
return list_count
return default
2021-07-23 18:45:49 +00:00
def parse_from_dict ( parent , method , data , methods , default = None , options = None , translation = None ) :
2021-07-22 21:00:45 +00:00
message = " "
2021-07-23 18:45:49 +00:00
if options is None and translation is not None :
options = [ o for o in translation ]
2021-07-22 21:00:45 +00:00
if method not in methods :
2021-07-23 18:45:49 +00:00
message = f " { parent } { method } attribute not found "
2021-07-22 21:00:45 +00:00
elif data [ methods [ method ] ] is None :
2021-07-23 18:45:49 +00:00
message = f " { parent } { method } attribute is blank "
elif ( translation is not None and str ( data [ methods [ method ] ] ) . lower ( ) not in translation ) or \
( options is not None and translation is None and str ( data [ methods [ method ] ] ) . lower ( ) not in options ) :
message = f " { parent } { method } attribute { data [ methods [ method ] ] } must be in { options } "
2021-07-22 21:00:45 +00:00
else :
2021-07-23 18:45:49 +00:00
return translation [ data [ methods [ method ] ] ] if translation is not None else data [ methods [ method ] ]
2021-07-22 21:00:45 +00:00
if default is None :
2021-07-23 18:45:49 +00:00
raise Failed ( f " Collection Error: { message } " )
2021-07-22 21:00:45 +00:00
else :
2021-07-23 18:45:49 +00:00
logger . warning ( f " Collection Warning: { message } using { default } as default " )
return translation [ default ] if translation is not None else default
2021-07-22 21:00:45 +00:00
def parse_int_from_dict ( parent , method , data , methods , default , minimum = 1 , maximum = None ) :
if method not in methods :
logger . warning ( f " Collection Warning: { parent } { method } attribute not found using { default } as default " )
elif not data [ methods [ method ] ] :
logger . warning ( f " Collection Warning: { parent } { methods [ method ] } attribute is blank using { default } as default " )
elif maximum is None and ( not isinstance ( data [ methods [ method ] ] , int ) or data [ methods [ method ] ] < minimum ) :
logger . warning ( f " Collection Warning: { parent } { methods [ method ] } attribute { data [ methods [ method ] ] } must an integer >= { minimum } using { default } as default " )
elif maximum is not None and ( not isinstance ( data [ methods [ method ] ] , int ) or data [ methods [ method ] ] < minimum or data [ methods [ method ] ] > maximum ) :
logger . warning ( f " Collection Warning: { parent } { methods [ method ] } attribute { data [ methods [ method ] ] } must an integer between { minimum } and { maximum } using { default } as default " )
else :
return data [ methods [ method ] ]
return default
2021-07-23 18:45:49 +00:00
def parse_list ( method , data , methods ) :
if method in methods and data [ methods [ method ] ] :
return [ i for i in data [ methods [ method ] ] if i ] if isinstance ( data [ methods [ method ] ] , list ) else [ str ( data [ methods [ method ] ] ) ]
return [ ]