text: """
  Create a csv with the last port calls of each boat (600~) named PortCalls-today.csv
  You  need to have chromedriver.exe in the same folder as the script
  The version of chromedriver depends on the version of chrome you are using
  The script is using data.csv which is the list of every boat in activity
  The script uses progressbar to show in the terminal the progression
  It runs during ~30mins
  """
  
  from datetime import date, datetime
  from selenium import webdriver
  from selenium.webdriver.support.ui import WebDriverWait as Dwait
  from selenium.webdriver.support import expected_conditions as EC
  from selenium.webdriver.common.by import By
  from selenium.common.exceptions import TimeoutException, WebDriverException
  import csv
  from progress.bar import Bar
  import pandas as pd
  import numpy as np
  
  """TOUTES LES OPTIONS UTILES POUR CHROMEDRIVER"""
  options = webdriver.ChromeOptions()
  options.add_experimental_option('excludeSwitches', ['enable-logging'])
  # ChromeDriver is just AWFUL because every version or two it breaks unless you pass cryptic arguments
  # AGRESSIVE: options.setPageLoadStrategy(PageLoadStrategy.NONE)
  # https://www.skptricks.com/2018/08/timed-out-receiving-message-from-renderer-selenium.html
  options.add_argument("start-maximized")  # https://stackoverflow.com/a/26283818/1689770
  options.add_argument("enable-automation")  # https://stackoverflow.com/a/43840128/1689770
  # options.add_argument("--headless")  # only if you are ACTUALLY running headless
  options.add_argument("--no-sandbox")  # https://stackoverflow.com/a/50725918/1689770
  options.add_argument("--disable-infobars")  # https://stackoverflow.com/a/43840128/1689770
  options.add_argument("--disable-dev-shm-usage")  # https://stackoverflow.com/a/50725918/1689770
  options.add_argument("--disable-browser-side-navigation")  # https://stackoverflow.com/a/49123152/1689770
  options.add_argument("--disable-gpu")  # https://stackoverflow.com/questions/51959986/how-to-solve-selenium-chromedriver-timed-out-receiving-message-from-renderer-exc
  
  
  def getData(url, driver):
      """
      Cette fonction prend en entrée une url de data ainsi que le driver pour
      acquérir les données des derniers port calls et les renvoyer sous la forme
      d'une liste.
      Exemple de liste réduite renvoyé :
      ['Ras Laffan, Qatar', 'Arrival (UTC)', 'Mar 22, 11:12', 'Departure (UTC)', 'Mar 23, 16:45', 'In Port', '1d 5h']
      """
      driver.get(url)
      try:
          # Liens vers les derniers ports visités
          Dwait(driver, 4).until(EC.presence_of_element_located((By.CLASS_NAME, "flx._rLk.t5UW5")))
      except TimeoutException:
          # Il n'y a pas les liens disponibles
          print(f"Timeout no port calls, url: {url}")
          try:
              # Premier cas : Aucun appel de ports détectés
              Dwait(driver, 2).until(EC.presence_of_element_located((By.ID, "port-calls")))
          except Exception as e:
              # 2nd cas : il n'y a pas la section des derniers ports (bateaux peu utiles ?)
              print(f"error with {url} : {e}")
              list_pc = []
      finally:
          table = driver.find_elements_by_class_name('vfix-top')[3]
          list_pc = table.text.split('\n')
      return list_pc
  
  
  def processData(list_pc, imo):
      """
      Cette fonction traite les données de getData pour écrire une seule string
      prête à être copié dans le csv et qui contient toutes les lignes d'un bateau
      """
      str_pc = ''
      for i in range(len(list_pc)):
          if list_pc[i] == 'Arrival (UTC)':
              tab = list_pc[i-1].split(',')  # [Port, Country] (good) or [Port, Region, Country] (bad)
              if len(tab) == 3:
                  tab = ['"' + tab[0] + ',' + tab[1].strip() + '"', tab[2]]  # [Port+(Region), Country]
              str_pc = str_pc + imo + ',' + tab[0] + ',' + tab[1] + ',"' + list_pc[i+1] + '","' + list_pc[i+3] + '","' + list_pc[i+5] + '"\n'
      return str_pc
  
  
  def getAndWritePortCalls(name):
      """
      Cette fonction définit le driver (et ouvre la fenêtre chrome)
      et parcourt les lignes de data pour lancer l'acquisition sur une page
      Elle copie ensuite les données traitées dans le csv final
      """
  
      driver = webdriver.Chrome(options=options)
  
      # Permet d'avoir la taille pour utiliser la barre de chargement
      with open('../Data/data.csv') as f:
          boats_count = len(f.readlines())
  
      with open('../Data/data.csv', newline='') as csv_ship_infos:
          with open(f'../Data/Portcalls/{name}', 'a') as csv_pc:
              # Ecriture de l'en-tête
              csv_pc.write('IMO,Port,Country,Arrival,Departure,In Port\n')
              # Barre de chargement dans le terminal
              bar = Bar('Processing', max=boats_count)
              ship_infos = csv.reader(csv_ship_infos)
              # On itère sur tous les bateaux connus
              for row in ship_infos:
                  if row[3] != 'Lien':
                      url = 'https://www.' + row[3]
                      imo = row[1]
                      list_data = getData(url, driver)
                      str_data = processData(list_data, imo)
                      csv_pc.write(str_data)
                      bar.next()
              bar.finish()
  
  
  def convertPcDate(x):
      # On convertit les dates pour trier par mois puis jour
      if isinstance(x, str) and x != '-':
          try:
              # May 4, 21:16 -> 05/04, 21:16
              date = datetime.strptime(x, "%b %d, %H:%M").strftime("%m/%d, %H:%M")
          except ValueError:
              try:
                  # La date est déjà au bon format
                  date = datetime.strptime(x, "%m/%d, %H:%M").strftime("%m/%d, %H:%M")
              # Si rien ne marche, on renvoie '-' qui sera converti en nan
              except Exception:
                  date = '-'
          except Exception:
              date = '-'
      else:
          date = '-'
      return date
  
  
  def appendAndSelect(name):
      """
      Ajoute le fichier du jour au fichier commun en supprimant les doublons :
      1 . Les lignes qui sont toutes pareilles : Arrival, Departure, In Port
      2 . Les lignes ayant le même Arrival : on garde celle qui a le plus d'infos,
          notamment ayant les infos a, d, ip car certaines lignes correspondent
          à un bateau qui n'a pas encore quitté le port
      3 . Les lignes ayant le même Departure : on garde aussi celle qui a le plus d'infos,
          certaines dates de départs sont supprimées quand elles sont trop anciennes
          (elles sont ici remplacées par les dates de départs)
  
      pc = port calls
      """
      # On ajoute le nouveau fichier portcalls au fichier commun
      csv_pc = pd.read_csv(f'../Data/Portcalls/{name}', index_col=['IMO'])
      csv_all_pc = pd.read_csv('../Data/PortCalls/PortCalls.csv', index_col=['IMO'])
      csv_pc = csv_pc.append(csv_all_pc)
      size_before_add = csv_all_pc.shape[0]
  
      # On convertit les dates pour pouvoir trier le fichier
      csv_pc['Departure'] = csv_pc['Departure'].apply(convertPcDate)
      csv_pc['Arrival'] = csv_pc['Arrival'].apply(convertPcDate)
  
      # Gestion des nan + remplissage des dates d'arrivées manquantes
      csv_pc = csv_pc.replace('-', np.nan)
      csv_pc['Arrival'].fillna(csv_pc['Departure'], inplace=True)
  
      # Etape 1
      csv_pc.drop_duplicates(inplace=True)
      # Etape 2
      csv_pc.sort_values(by=['IMO', 'Arrival'], na_position='first', inplace=True)
      csv_pc.drop_duplicates(subset=['Port', 'Country', 'Arrival'], keep='last', inplace=True)
      # Etape 3
      csv_pc.drop_duplicates(subset=['Port', 'Country', 'Departure'], keep='first', inplace=True)
      size_after_add = csv_pc.shape[0]
  
      # Ecriture dans le csv + communication dans le terminal
      csv_pc.to_csv('../Data/PortCalls/PortCalls.csv')
      print(f'{size_after_add - size_before_add} appels de ports ajoutés')
  
  
  if __name__ == '__main__':
      today = date.today()
      name_file = f'PortCalls-{today}.csv'
      getAndWritePortCalls(name_file)
      appendAndSelect(name_file)
  