# -*- coding: utf-8 -*-
"""DataFormatting.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/github/SimoneCallegarin/TIS_project/blob/main/DataframeFormatting/DataFormatting.ipynb

## <font color=#5DADE2>Dataframe Formatting</font>
In this section some functions are defined to format the dataframes, in order to improve their readability and to facilitate their comprehension

#### <font color=#84B1E0>Other Functions</font>

Returns True when shading is from lighter to darker color and False when it is in the other direction, the *numOfShades* determines the number of colors we are chosing to shade the dataframe attributes:
"""

def attColorScale(numOfShades, up, attCount):
    if attCount == numOfShades:
        return False
    if attCount == 0:
        return True
    return up

"""If the color shading is normal (up == True) it proceed to shade from lighter to darker colors, otherwise it follows a backward shade:"""

def selectAttColor(color, shadingIntensity, up):
    if up:
      return color - int(shadingIntensity,16)
    else:
      return color + int(shadingIntensity,16)

"""Add or reduce the attribute counter according to the shade order we are following:"""

def setCounter(attCount, up):
    if up:
      return attCount + 1
    else:
      return attCount - 1

"""Select an hexadecimal value for the starting color of the dataframe shade (the lighter one):"""

def startingColorSel(color,loneAttribute):
  if color == 'blue':
    if loneAttribute:
      return 'E2EDF8'
    else:
      return 'EBF3FA'
  if color == 'gray':
    if loneAttribute:
      return'BFBFBF'
    else:
      return 'CCCCCC'

"""Select the intensity of the shades between the attributes of the dataframe:"""

def shadingIntensitySel(color,loneAttribute):
  if color == 'blue':
    if loneAttribute:
      return '3A230E'
    else:
      return 'A0602'
  if color == 'gray':
    if loneAttribute:
      return '191919'
    else:
      return 'D0D0D'

"""### <font color=#85C1E9>Text formatting</font>

Paints <font color=red>FALSE</font> values in <font color=red>red</font> and <font color=green>TRUE</font> values in <font color=green>green</font> text:
"""

def color_true_false(value):
    if type(value) != type(''):
      if value == 1:
        color = 'green'
      else:
        color = 'red' 
      return 'color: {}'.format(color)

"""Paints values that are equal to 1 with <font color=green>green</font> text:"""

def color_val(value):
  if type(value) != type(''):
    if value == 1:
      color = 'green'
      return 'color: {}'.format(color)

"""Paints value that are equal to 1.0 with <font color=#FDFEFE>white</font> text:"""

def color_white(value):
  if type(value) != type(''):
    if value == 1.0:
      color = '#FDFEFE'
      return 'color: {}'.format(color)

"""### <font color=#85C1E9>Row formatting</font>

Shades automatically the rows of the table with a <font color=red>c<font color=orange>o<font color=yellow>l<font color=green>o<font color=blue>r<font color=blue> s<font color=green>c<font color=yellow>a<font color=orange>l<font color=red>e</font></font></font></font></font></font></font></font></font></font> for each *attribute_name* group that was defined during the preprocessing phase.

Works differently based on the fact that we want to focus on a single attribute (the sensitive one) or on the entire dataframe.

Parameters meanings:

-   `tab` $\rightarrow$ the dataframe to be formatted (pandas df)
-   `startingColor` $\rightarrow$ the lighter color to start the shade process (hexadecimal color)
-   `NumOfShades` $\rightarrow$ the number of shades to apply (integer)
-   `shadingIntensity` $\rightarrow$ the intensity between each shade (hexadecimal color)
-   `loneAttribute` $\rightarrow$ indicates if the analysis is required only on a sensitive attribute (boolean)
"""

def highlight(tab, startingColor, numOfShades, shadingIntensity, loneAttribute):

    df =  pd.DataFrame('', index=tab.index, columns=tab.columns)
    color = int(startingColor,16)
    attCount = 0
    up = True

    if loneAttribute:
        for att in tab['attribute_name']:
            bgcolor = 'background-color: 	#' + str(format(color, 'X'))
            df.iloc[attCount, :] = bgcolor
            up = attColorScale(numOfShades-1, up, attCount)
            color = selectAttColor(color, shadingIntensity, up)
            attCount = setCounter(attCount, up)

    else:
        for att in tab['attribute_name'].unique():
            mask = tab['attribute_name'] == att
            bgcolor = 'background-color: 	#' + str(format(color, 'X'))
            df.loc[mask, :] = bgcolor
            up = attColorScale(numOfShades-1, up, attCount)
            color = selectAttColor(color, shadingIntensity, up)
            attCount = setCounter(attCount, up)

    return df

"""Apply a <font color=green>green</font> background to cells containing a *1.0* value:"""

def highlight_100(value):
  if type(value) != type(''):
    if value == 1.0:
      return 'background-color:	 #16A085'

"""---
Apply a background gradient to each column of the table ***tab*** belonging to the ***sel_col*** list (using `axis=0` along the rows or using `axis=None` along both axes), and format their values to two decimal also applying percentage:
"""

def table_gradient(tab,tab_style,sel_col,loneAttribute):
  if sel_col:
    tab_style.format({col: "{:.2%}" for col in tab.columns if col in sel_col})
    if loneAttribute:
      tab_style.background_gradient(axis=None, subset = sel_col)
    else:
      tab_style.background_gradient(axis=0, subset = sel_col)

"""### <font color=#85C1E9>Table formatting</font>

Formats text and background for each row of the dataframe:


1.   Set <font color=black>black</font> text as default
2.   Set <font color=navy>navy</font> text to values belonging to the *attribute_value* column
3.   Set a sequence of <font color=lightblue>light blue</font> shades as background for each defined group 
4.   Add a gradient to the columns belonging to *sel_col* list
5.   Set a <font color=green>green</font> background to the cells containing 100.00%
6.   Set <font color=#FDFEFE>white</font> text to the cells containing 100.00%
"""

def format_blue_table(tab, sel_col, numOfShades, loneAttribute):
    tab_style = tab.style
    startingColor = startingColorSel('blue',loneAttribute)
    shadingIntensity = shadingIntensitySel('blue',loneAttribute)
    tab_style.set_properties(**{'color': 'black'})
    tab_style.set_properties(**{'color': 'navy'}, subset = ['attribute_value'])
    tab_style.apply(highlight, startingColor = startingColor, numOfShades = numOfShades, shadingIntensity = shadingIntensity, loneAttribute = loneAttribute, axis = None)
    table_gradient(tab,tab_style,sel_col,loneAttribute)
    tab_style.applymap(highlight_100, subset = sel_col)
    tab_style.applymap(color_white, subset = sel_col)
    return tab_style

"""Formats text and background for each row of the dataframe:


1.   Set <font color=black>black</font> text for the values belonging to the *attribute_name* column
2.   Set <font color=navy>navy</font> text to values belonging to the *attribute_value* column
3.   Set a sequence of <font color=gray>gray</font> shades as background for each defined group
4.   Set red and green colored text of the cells containing respectively False and True
5.   Apply a <font color=red>gradient</font> to the columns belonging to the *sel_col* list
6.   Set <font color=green>green</font> colored text of the cells containing '1'

"""

def format_gray_table(tab,sel_col, numOfShades, loneAttribute):
    tab_style = tab.style
    startingColor = startingColorSel('gray',loneAttribute)
    shadingIntensity = shadingIntensitySel('gray',loneAttribute)
    tab_style.set_properties(**{'color': 'black'}, subset = ['attribute_name'])
    if 'attribute_value' in tab.columns:
      tab_style.set_properties(**{'color': 'navy'}, subset = ['attribute_value'])   
    tab_style.apply(highlight, startingColor = startingColor, numOfShades = numOfShades, shadingIntensity = shadingIntensity, loneAttribute = loneAttribute, axis = None)
    tab_style.applymap(color_true_false)
    if loneAttribute:
      tab_style.text_gradient(axis=1, cmap='gist_heat', subset = sel_col, low=0, high=0.5)
    else:
      tab_style.text_gradient(axis=0, cmap='gist_heat', subset = sel_col, low=0, high=0.5)
    tab_style.applymap(color_val)
    return tab_style