text: import tensorflow as tf
  import tensorflow_hub as hub
  
  import xml.etree.ElementTree as Xet
  import pandas as pd
  import requests
  import re
  import copy
  
  
  def get_hot_data(base_url="https://www.boardgamegeek.com/xmlapi2"):
      hot = requests.get(f"{base_url}/hot?type=boardgame").content.decode()
    
      # Parsing the XML file
      xmlparse = Xet.fromstring(hot)
  
      hot_array = []
      for item in xmlparse:
          dict_element = {
              "id": item.get("id"),
              "rank": item.get("rank")
          }
          for option in item:
              dict_element[option.tag] = option.get("value")
          hot_array.append(dict_element)
      return hot_array
  
  
  def get_comments(id_array, base_url="https://www.boardgamegeek.com/xmlapi2", verbose=1):
      max_comments_per_page = 1
      array_ids = [h['id'] for h in id_array]
      ids = ','.join(array_ids)
      page_size = 100
      page_number = 0
      comments_array = []
  
      while max_comments_per_page > 0 and len(array_ids) > 0:
          page_number += 1
          if verbose>0 and page_number%verbose==0:
              print(f"page number = {page_number}", end=" ")
          comments = requests.get(f"{base_url}/thing?id={ids}&comments=1&pagesize={page_size}&page={page_number}").content.decode()
  
          # Parsing the XML file
          xmlparse = Xet.fromstring(comments)
          comments_per_item = []
          for item in xmlparse:        
              for option in item:
                  if option.tag == 'comments':
                      comments_per_page = 0
                      for comment in option:
                          comments_per_page += 1
                          dict_element = {
                              "id": item.get("id"),
                              "username": comment.get("username"),
                              "rating": comment.get("rating"),
                              "value": comment.get("value")
                          }
                          comments_array.append(dict_element)
                      if comments_per_page==0:
                          array_ids.remove(item.get("id"))
                          ids = ','.join(array_ids)
                      comments_per_item.append(comments_per_page)
          max_comments_per_page = max(comments_per_item)
          if verbose>0 and page_number%verbose==0:
              print(f"(max: {max(comments_per_item)}, len: {len(comments_per_item)})")
      comments_df = pd.DataFrame(comments_array)
      print(f"comments_df shape: {comments_df.shape}")
      return comments_df
  
  
  # REMOVE SHORT COMMENTS
  def remove_short_comments(df: pd.DataFrame, min_len=25, method='unrated'):
      if method not in ['unrated', 'rated', 'both']:
          raise AttributeError(f"method {method} not allowed")
      df_local = df.copy()    
      df_local['comment_len'] = [len(re.findall("[A-Za-z]", v)) for v in df_local['value'].values]
      if method == 'rated':
          df_local = df_local.query('(comment_len>@min_len and rating != "N/A") or rating == "N/A"')
      elif method == 'unrated':
          df_local = df_local.query('(comment_len>@min_len and rating == "N/A") or rating != "N/A"')
      else:
          df_local = df_local.query('comment_len>@min_len')
      print(f"removed {len(df)-len(df_local)} for 'remove_short_comments' - min len: {min_len} - method: {method}")
      return df_local.reset_index(drop=True)
  
  
  def build_model(hub_layer=None, pre_trained_model_name="https://tfhub.dev/google/tf2-preview/nnlm-en-dim50/1", model_type='classifier', lstm=False, verbose=1):    
      if hub_layer is None:
          hub_layer = hub.KerasLayer(pre_trained_model_name, input_shape=[], dtype=tf.string, trainable=True)
          
      if model_type not in ('classifier', 'regressor'):
          raise AttributeError("model type can only be 'classifier' or 'regressor'")
  
      if verbose:
          print("example of layers processing:")
          print(hub_layer(train_examples[:2]))
          print("")
  
      if model_type == 'classifier':
          model = tf.keras.Sequential()
          model.add(copy.copy(hub_layer))
          if lstm:
              raise NotImplementedError("lstm = True is not implemented")
          else:
              model.add(tf.keras.layers.Dense(16, activation='relu'))
          model.add(tf.keras.layers.Dense(1))
          
          if verbose:
              print("model summary:")
              print(model.summary())
          
          model.compile(
              optimizer='adam',
              loss=tf.losses.BinaryCrossentropy(from_logits=True),
              metrics=[tf.metrics.BinaryAccuracy(threshold=0.0, name='accuracy')]
          )
      else:
          model = tf.keras.Sequential()
          model.add(copy.copy(hub_layer))
          if lstm:
              raise NotImplementedError("lstm = True is not implemented")
          else:
              model.add(tf.keras.layers.Dense(16, activation='relu', kernel_initializer='normal'))
          model.add(tf.keras.layers.Dense(1, kernel_initializer='normal'))
          
          if verbose:
              print("model summary:")
              print(model.summary())
              
          model.compile(
              loss='mean_squared_error', 
              optimizer='adam',
              metrics=["mean_squared_error"]
          )
      
      return model
  
  
  def print_my_examples(inputs, results, actual_values=None, limit=None):
      if actual_values is None:
          result_for_printing = [f'input:\n"{inputs[i]}"\nscore: {results[i][0]:.6f}' for i in range(len(inputs))]
      else:
          result_for_printing = [f'input:\n"{inputs[i]}"\nscore: {results[i][0]:.6f}\nactual: {actual_values[i]}' for i in range(len(inputs))]
      if limit:
          result_for_printing = result_for_printing[:limit]
      print(*result_for_printing, sep='\n\n')