Skip to content

Commit

Permalink
Merge pull request #4 from orensbruli/feature/heatmap
Browse files Browse the repository at this point in the history
heatmap
  • Loading branch information
orensbruli authored Jan 16, 2024
2 parents 722b9fc + 7cd005c commit 50ca859
Show file tree
Hide file tree
Showing 7 changed files with 341 additions and 2 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/github_action_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ jobs:
with:
name: compiled-pdf
path: output.pdf

- name: Upload binaries to release
uses: svenstaro/upload-release-action@v2
if: ${{ contains(github.ref, 'tags') }}
Expand All @@ -54,3 +55,12 @@ jobs:
tag: ${{ github.ref }}
overwrite: true
body: "Latest version of my CV."

# TODO: Attach pdf to readme.md using sed and the latest release
# -name: Attach pdf to readme.md
# run: |
# echo "![CV](
#
# )"
# >> README.md

7 changes: 6 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ RUN apt-get update && apt-get install -y \
fonts-roboto-slab

RUN apt-get update && apt-get install -y \
ghostscript
ghostscript \
python3 \
python3-pip

# bind mount requirements.txt and install python dependencies
RUN --mount=type=bind,target=/latex_content pip3 install -r /latex_content/requirements.txt

# Set working directory
WORKDIR /latex_content
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ pdf:
cp data.md build/pdf/
wget --output-document=build/pdf/img.jpg $$(yq e '.image' data.md | grep https)

python3 heatmap.py

mv heatmap.eps build/pdf/

cd build/pdf/; \
pandoc data.md --pdf-engine xelatex --template sidebar.template.tex -o sidebar.tex ; \
pandoc data.md --pdf-engine xelatex --template $(TEX_TEMPLATE) -o $(TEX) ; \
Expand Down
160 changes: 159 additions & 1 deletion data.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,5 +165,163 @@ color_scheme:
color: "SlateGrey"
- name: "body"
color: "LightGrey"

skills_details:
- name: Shell script
levels:
- grade: 5
period: 2008-12 to current
- grade: 4
period: 2008-12 to 2009-12
- grade: 3
period: 2008-12 to 2009-12
- grade: 2
period: 2006-07 to 2008-12
- grade: 1
period: 2006-07 to 2007-12
- name: Python
levels:
- grade: 4
period: 2022-01-08 to 2024-01-14
- grade: 3
period: 2021-08-01 to 2022-01-07
- grade: 4
period: 2021-09 to 2022-07
- grade: 3
period: 2021-01-01 to 2021-08
- grade: 4
period: 2020-02 to 2020-11
- grade: 3
period: 2019-12 to 2020-01
- grade: 4
period: 2018-12-21 to 2019-11
- grade: 3
period: 2016-03-01 to 2018-12-20
- grade: 2
period: 2015-07-29 to 2016-02-28
- name: C++
levels:
- grade: 3
period: 2021-08 to current
- grade: 5
period: 2020-12 to 2021-08
- grade: 4
period: 2020-02 to 2020-11
- grade: 3
period: 2019-12 to 2020-01
- grade: 4
period: 2018-07 to 2019-11
- grade: 3
period: 2016-03 to 2018-06
- grade: 4
period: 2015-07 to 2016-02
- grade: 3
period: 2011-12 to 2015-07
- grade: 4
period: 2010-03 to 2011-12
- grade: 3 # Hesperia
period: 2008-02 to 2010-02
- grade: 1 # Previous to Hesperia
period: 2006-01 to 2010-02
- name: Docker
levels:
- grade: 5 # Open Robotics
period: 2022-08 to current
- grade: 1
period: 2022-07 to 2022-07
- grade: 3 # Robotica UEX
period: 2021-09 to 2022-07
- grade: 1
period: 2021-08 to 2021-08
- grade: 4
period: 2020-02 to 2021-08
- grade: 1
period: 2020-01 to 2020-01
- grade: 4
period: 2019-12 to 2020-01
- grade: 1
period: 2019-11 to 2019-11
- grade: 3
period: 2018-07 to 2019-11
- grade: 1
period: 2018-06 to 2018-06
- grade: 1
period: 2018-01 to 2018-06
- name: Qt
levels:
- grade: 3
period: 2021-09 to current
- grade: 3
period: 2021-08-01 to 2021-08-31
- grade: 5
period: 2020-12 to 2021-08
- grade: 4
period: 2020-12-01 to 2020-12-10
- grade: 4
period: 2020-02-11 to 2020-11-30
- grade: 1
period: 2019-11-17 to 2020-02-10
- grade: 2
period: 2018-12-21 to 2019-11-16
- grade: 1
period: 2016-03-01 to 2018-12-20
- grade: 1
period: 2015-07-29 to 2016-02-28
- name: Unit Testing
levels:
- grade: 3
period: 2022-01-08 to 2024-01-14
- grade: 5
period: 2021-08-01 to 2022-01-07
- grade: 4
period: 2021-09 to 2022-07
- grade: 3
period: 2021-01-01 to 2021-08
- grade: 3
period: 2020-02 to 2020-11
- grade: 1
period: 2019-05 to 2020-01
- name: Github Actions
levels:
- grade: 5
period: 2022-08 to current
- grade: 3
period: 2021-09 to 2022-07
- grade: 4
period: 2020-12 to 2021-08
- grade: 1
period: 2018-12 to 2020-11
- name: Gitlab CI/CD
levels:
- grade: 4
period: 2022-12 to current
- grade: 2
period: 2021-09 to 2022-11
- name: Ansible
levels:
- grade: 2
period: 2022-01-08 to 2024-01-14
- grade: 1
period: 2021-09-01 to 2022-07-31
# - name: Argo CD
# levels:
# - grade: 1
# period: 2022-01-08 to 2024-01-14
# - name: NGINX
# levels:
# - grade: 2
# period: 2018-07-03 to 2018-12-20
# - name: SQLAlchemy
# levels:
# - grade: 3
# period: 2015-07-29 to 2016-02-28
# - name: PHP
# levels:
# - grade: 3
# period: 2006-07 to 2008-12
# - name: MySql
# levels:
# - grade: 3
# period: 2008-01 to 2008-12
# - grade: 2
# period: 2006-07 to 2007-12
---
151 changes: 151 additions & 0 deletions heatmap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontManager
import numpy as np
import pandas as pd
import seaborn as sns
import yaml



def print_available_fonts():
global yaml_data
font_manager = FontManager()
fuentes_disponibles = [f.name for f in font_manager.ttflist]
# Imprime las fuentes disponibles
for fuente in sorted(set(fuentes_disponibles)):
print(fuente)


# Load the YAML data from a string
def load_yaml_data(yaml_data_string):
yaml_data = yaml.load(yaml_data_string, Loader=yaml.FullLoader)
return yaml_data


def load_yaml_data_from_file(yaml_file):
with open(yaml_file) as file:
docs = yaml.load_all(file, yaml.FullLoader)
for yaml_data in docs:
if yaml_data is not None and 'skills_details' in yaml_data:
break
if 'skills_details' not in yaml_data:
raise Exception('Invalid YAML file. Missing skills section')
return yaml_data


def configure_labels(ax):
# Obtener las etiquetas actuales del eje x
ax = plt.gca()
xlabels = [label.get_text() for label in ax.get_xticklabels()]
# filtrar las etiquetas para mostrar solo los años
xlabels = [label if label.endswith('01') else '' for label in xlabels]
# Establecer las etiquetas filtradas en el eje x
ax.set_xticks([xlabels.index(label) for label in xlabels])
ax.set_xticklabels(xlabels)
ax.set_yticklabels(ax.get_yticklabels(), rotation=0)
# Rotar las etiquetas para mejor visualización
plt.xticks(rotation=90)
# Ajustes de visualización
plt.xlabel('Date', fontdict={'fontsize': 16, 'fontfamily': 'Roboto Slab', 'fontweight': 'bold'})
plt.ylabel('Skill', fontdict={'fontsize': 16, 'fontfamily': 'Roboto Slab', 'fontweight': 'bold'})
return ax


def group_periods(df):
# Group by skill and period and calculate the average level
# Use M for monthly, Q for quarterly, Y for yearly averages
df_grouped = df.groupby([df['skill'], df['period'].dt.to_period('Q')])['level'].mean()
df_grouped = df_grouped.reset_index()
# Convertimos el periodo a formato de fecha para el gráfico
df_grouped['period'] = df_grouped['period'].dt.to_timestamp()
return df_grouped


def format_periods(df):
# Some periods can contain words like "current" or "actually" and must be replaced with the current date
df['period'] = df['period'].apply(lambda x: x.replace('current', pd.Timestamp.now().strftime('%Y-%m-%d')))
# convert the period to a list of months between the start and end date
df['period'] = df['period'].apply(
lambda x: pd.date_range(start=x.split(' to ')[0], end=x.split(' to ')[1], freq='W').tolist())
# create a new dataframe with the period expanded
df = df.explode('period')
# Convert the period to a datetime object
df['period'] = pd.to_datetime(df['period'])
return df


def yaml_to_dataframe(yaml_data):
# convert to a structured like this
# {
# 'year': year,
# 'week': week,
# 'skill': name,
# 'level': grade
# }
data = []
for skill in yaml_data['skills_details']:
for level in skill['levels']:
data.append({
'period': level['period'],
'skill': skill['name'],
'level': level['grade']
})
# Create a dataframe from the data keeping the order of the columns entries
df = pd.DataFrame(data, columns=['skill', 'period', 'level'])
return df


def sum_of_skills(df):
# Sum the level of each skill for each period
df_grouped = df.groupby([df['period'].dt.to_period('Q')])['level'].sum()
df_grouped = df_grouped.reset_index()
# Convert the period to a datetime object
df_grouped['period'] = df_grouped['period'].dt.to_timestamp()
return df_grouped


def main():
yaml_data = load_yaml_data_from_file('data.md')
df = yaml_to_dataframe(yaml_data)
original_skill_order = df['skill'].unique()
df = format_periods(df)
df_grouped = group_periods(df)
# Preparamos los datos para el mapa de calor mantiendo el orden las filas y columnas
heatmap_data = pd.pivot_table(df_grouped, values='level', index='skill', columns='period', aggfunc=np.mean)
heatmap_data.columns = heatmap_data.columns.to_series().dt.strftime('%Y-%m')
heatmap_data = heatmap_data.fillna(0)
# sort by the sum of the skills
# heatmap_data = heatmap_data.reindex(heatmap_data.sum().sort_values(ascending=False).index)
heatmap_data = heatmap_data.reindex(original_skill_order)
#
# Ajustamos el tamaño del gráfico
fig, ax = plt.subplots(figsize=(20, 4))
# cmap = sns.light_palette("darkgreen", as_cmap=True)
# Crear el mapa de calor
custom_colors = sns.color_palette("YlGn", as_cmap=True)
# add blue at the beginning to show the lowest values
custom_colors = sns.color_palette(["#ECECEC"] + list(sns.color_palette("YlGn", 10).as_hex()), as_cmap=True)

sns.heatmap(heatmap_data, annot=False, cmap=custom_colors, linewidths=1, square=True, ax=ax)
ax = configure_labels(ax)
ax.set_title('Level of skill', loc='center', pad=24,
fontdict={'fontsize': 16, 'fontweight': 'bold', 'fontfamily': 'Roboto Slab'})

ax.yaxis.tick_right()

# Encuentra la barra de colores actual en el gráfico, si existe
cbar = ax.collections[0].colorbar

# Si existe la barra de colores, ajusta su posición
if cbar:
cbar.remove() # Primero, removemos la barra de colores existente
cbar = fig.colorbar(ax.collections[0], ax=ax, location='left', shrink=0.65, pad=0.04, fraction=0.01)

plt.subplots_adjust(left=0.02, right=0.92, top=0.999, bottom=0.001)

#save the figure
fig.savefig('heatmap.eps', format='eps')


if __name__ == '__main__':
main()
5 changes: 5 additions & 0 deletions latex/main.template.tex
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

% The paracol package lets you typeset columns of text in parallel
\usepackage{paracol}
\usepackage{adjustbox}


% Change the font if you want to, depending on whether
Expand Down Expand Up @@ -134,9 +135,13 @@
$endfor$
$endif$



\switchcolumn
\input{sidebar}
\end{paracol}

\centering\includegraphics[width=1.06\linewidth]{heatmap.eps}


\end{document}
6 changes: 6 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
matplotlib==3.8.2
matplotlib-inline==0.1.3
numpy==1.26.2
pandas==2.0.0
pyyaml_env_tag==0.1
seaborn==0.13.1

0 comments on commit 50ca859

Please sign in to comment.