anilist-api.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. import pprint
  2. import numpy as np
  3. import pandas as pd
  4. from graphql_client import GraphQLClient
  5. pp = pprint.PrettyPrinter(indent=2)
  6. def myprint(data, show = True):
  7. if show:
  8. pp.pprint(data)
  9. class AnilistApi:
  10. # Le client graphQL pour effectuer les requetes
  11. graphql = GraphQLClient('https://graphql.anilist.co')
  12. # La derniere query graphql
  13. query = None
  14. # Le derniere utilisateur
  15. user = None
  16. def findUser(self, username, returnSelf = True):
  17. """Cherche un utilisateur passé en paramètre pour les futures appels à l'API"""
  18. request = '''
  19. query ($name: String) {
  20. User(name: $name) {
  21. id
  22. mediaListOptions {
  23. scoreFormat
  24. }
  25. }
  26. }
  27. '''
  28. variables = {
  29. 'name': username
  30. }
  31. # Création query
  32. self.query = self.graphql.query(request, variables)
  33. # Execution query
  34. result = self.query.execute().get()
  35. if(self.graphql.isError(result)):
  36. result = None
  37. self.user = result
  38. # Retour
  39. if returnSelf:
  40. return self
  41. return result != None
  42. def getUserAnimeList(self):
  43. """Retourne la liste d'anime complétés de l'utilisateur (ou None en cas de problème)"""
  44. if(self.user == None):
  45. return None
  46. request = '''
  47. query ($id: Int) {
  48. MediaListCollection (userId: $id, type: ANIME, status: COMPLETED) {
  49. lists {
  50. entries {
  51. score
  52. media {
  53. id
  54. title {
  55. userPreferred
  56. }
  57. averageScore
  58. format
  59. episodes
  60. duration
  61. popularity
  62. source
  63. tags {
  64. name
  65. }
  66. genres
  67. }
  68. }
  69. }
  70. }
  71. }
  72. '''
  73. variables = {
  74. 'id': self.user['User']['id']
  75. }
  76. # Création query
  77. self.query = self.graphql.query(request, variables)
  78. # Execution query
  79. result = self.query.execute().get()
  80. if(self.graphql.isError(result)):
  81. return None
  82. return UserAnimeList(result['MediaListCollection']['lists'][0]['entries'], self.user['User']['mediaListOptions']['scoreFormat'])
  83. def fetchAnimeList(self, query, page = 0, element = 50):
  84. if query.object != 'Media':
  85. raise ValueError('bad query type: ' + query.object)
  86. # Requete graphql
  87. request = '''
  88. query ($page: Int, $element: Int, ${def}) {
  89. Page(page: $page, perPage: $element) {
  90. media(${param}) {
  91. id
  92. title {
  93. userPreferred
  94. }
  95. averageScore
  96. format
  97. episodes
  98. duration
  99. popularity
  100. source
  101. tags {
  102. name
  103. }
  104. genres
  105. }
  106. }
  107. }
  108. '''
  109. # Recupération info query et application
  110. definition, parameter, variable = query.get()
  111. request = request.replace('${def}', definition)
  112. request = request.replace('${param}', parameter)
  113. variable['page'] = page
  114. variable['element'] = element
  115. # Création query
  116. self.query = self.graphql.query(request, variable)
  117. # Execution query
  118. result = self.query.execute().get()
  119. if(self.graphql.isError(result)):
  120. return None
  121. # Préparation pour creation objet AnimeList
  122. data = []
  123. for i in range(0, len(result['Page']['media'])):
  124. data.append({'media': result['Page']['media'][i]})
  125. return UserAnimeList(data)
  126. def iterateAnimeListFetch(self, query, nb, element):
  127. for i in range (0, nb):
  128. yield self.fetchAnimeList(query, i, element)
  129. class AnilistQuery:
  130. __declaration = ''
  131. __parameter = ''
  132. __variable = {}
  133. # Constructeur
  134. def __init__(self, obj = 'Media'):
  135. self.object = obj
  136. self.reset()
  137. def scoreGreaterThan(self, value):
  138. if(self.__variable.get('sgt') == None):
  139. self.__declaration += ',$sgt: Int'
  140. self.__parameter += ',averageScore_greater: $sgt'
  141. self.__variable['sgt'] = round(value)
  142. return self
  143. def popularityGreaterThan(self, value):
  144. if(self.__variable.get('pgt') == None):
  145. self.__declaration += ',$pgt: Int'
  146. self.__parameter += ',popularity_greater: $pgt'
  147. self.__variable['pgt'] = round(value)
  148. return self
  149. def episodeBetween(self, minValue, maxValue):
  150. if(self.__variable.get('egt') == None):
  151. self.__declaration += ',$egt: Int'
  152. self.__parameter += ',episodes_greater: $egt'
  153. self.__variable['egt'] = round(minValue)
  154. if(self.__variable.get('elt') == None):
  155. self.__declaration += ',$elt: Int'
  156. self.__parameter += ',episodes_lesser: $elt'
  157. self.__variable['elt'] = round(maxValue)
  158. return self
  159. def durationBetween(self, minValue, maxValue):
  160. if(self.__variable.get('dgt') == None):
  161. self.__declaration += ',$dgt: Int'
  162. self.__parameter += ',duration_greater: $dgt'
  163. self.__variable['dgt'] = round(minValue)
  164. if(self.__variable.get('dlt') == None):
  165. self.__declaration += ',$dlt: Int'
  166. self.__parameter += ',duration_lesser: $dlt'
  167. self.__variable['dlt'] = round(maxValue)
  168. return self
  169. def formatIn(self, value):
  170. if(self.__variable.get('fin') == None):
  171. self.__declaration += ',$fin: [MediaFormat]'
  172. self.__parameter += ',format_in: $fin'
  173. self.__variable['fin'] = value
  174. return self
  175. def sourceIn(self, value):
  176. if(self.__variable.get('sin') == None):
  177. self.__declaration += ',$sin: [MediaSource]'
  178. self.__parameter += ',source_in: $sin'
  179. self.__variable['sin'] = value
  180. return self
  181. def tagIn(self, value):
  182. if(self.__variable.get('tin') == None):
  183. self.__declaration += ',$tin: [String]'
  184. self.__parameter += ',tag_in: $tin'
  185. self.__variable['tin'] = value
  186. return self
  187. def genreIn(self, value):
  188. if(self.__variable.get('gin') == None):
  189. self.__declaration += ',$gin: [String]'
  190. self.__parameter += ',genre_in: $gin'
  191. self.__variable['gin'] = value
  192. return self
  193. def get(self):
  194. return self.__declaration.strip(','), self.__parameter.strip(','), self.__variable
  195. def reset(self):
  196. self.__declaration = ''
  197. self.__parameter = ''
  198. self.__variable = {}
  199. def __str__(self):
  200. return 'Declaration: ' + self.__declaration.strip(',') + '\nParameter: ' + self.__parameter.strip(',') + '\nVariable: ' + pp.pformat(self.__variable)
  201. class UserAnimeList:
  202. data = []
  203. # Constructeur, met en forme les données
  204. def __init__(self, entries, userScoreType = None):
  205. self.data = []
  206. for entry in entries:
  207. media = entry['media']
  208. d = {
  209. 'id': media['id'],
  210. 'title': media['title']['userPreferred'],
  211. 'score': media['averageScore'],
  212. 'popularity': media['popularity'],
  213. 'format': media['format'],
  214. 'episode': media['episodes'],
  215. 'duration': media['duration'],
  216. 'source': media['source']
  217. }
  218. # Ajout score de l'utilisateur si present
  219. if userScoreType != None and entry.get('score') != None:
  220. d['userScore'] = self.__formatScore(entry['score'], userScoreType)
  221. # Ajout tag (3 max)
  222. end = 3 if len(media['tags']) > 3 else len(media['tags'])
  223. for i in range(0, 3):
  224. if(i < end):
  225. d['tag' + str(i + 1)] = media['tags'][i]['name']
  226. else:
  227. d['tag' + str(i + 1)] = 'None'
  228. # Ajout genre (5 max)
  229. end = 5 if len(media['genres']) > 5 else len(media['genres'])
  230. for i in range(0, 5):
  231. if(i < end):
  232. d['genre' + str(i + 1)] = media['genres'][i]
  233. else:
  234. d['genre' + str(i + 1)] = 'None'
  235. # Ajoute la ligne aux données
  236. self.data.append(d)
  237. def toDataFrame(self, dummies = False):
  238. df = pd.DataFrame(self.data)
  239. if dummies and len(self.data) > 0:
  240. df = self.__multipleColDummies(df, 'tag', 3)
  241. df = self.__multipleColDummies(df, 'genre', 5)
  242. df = pd.get_dummies(df, columns=['format', 'source'])
  243. return df
  244. def __formatScore(self, score, scoreType):
  245. if scoreType == 'POINT_10_DECIMAL' or scoreType == 'POINT_10':
  246. return 10 * score
  247. elif scoreType == 'POINT_5':
  248. return 20 * score
  249. elif scoreType == 'POINT_3':
  250. return (score / 3) * 100
  251. else:
  252. return score
  253. def __multipleColDummies(self, df, colname, num):
  254. # Recherche de toutes les valeurs possibles
  255. values = []
  256. for i in range(1, num + 1):
  257. d = df[colname + str(i)]
  258. for item in d.iteritems():
  259. if not(item[1] in values and item[1] != 'None'):
  260. values.append(item[1])
  261. # Creation colonne dans la dataframe
  262. for val in values:
  263. df[colname + '_' + val.replace(' ', '_')] = 0
  264. # Remplissage des colonnes
  265. newDf = []
  266. for row in df.iterrows():
  267. row = pd.Series(row[1])
  268. for i in range(1, num + 1):
  269. if row[colname + str(i)] != None:
  270. name = row[colname + str(i)].replace(' ', '_')
  271. row[colname + '_' + name] = 1
  272. newDf.append(row)
  273. df = pd.DataFrame(newDf)
  274. # Suppr ancienne colonne
  275. for i in range(1, num + 1):
  276. df = df.drop(colname + str(i), axis = 1)
  277. return df