BLOG

Acephal

En partenariat avec Alveol !

Une plateforme pour créer et afficher des formulaires

Acephal est une application web qui permet aux utilisateurs de créer des formulaires avec une interface glisser-déposer, de générer des formulaires HTML et de faire des appels API. Elle est construite sur les technologies NextJs, Ruby on Rails et KeyCloak.

Technologie

– NextJs pour le front-end

– Ruby on Rails pour le back-end

– KeyCloak pour la gestion et l’authentification des utilisateurs

Objectif

L’objectif principal d’Acephal est de fournir une interface conviviale pour créer des formulaires et les afficher avec du code HTML généré automatiquement et des appels d’API. Cela signifie que les utilisateurs peuvent facilement concevoir des formulaires sans code et les afficher avec un minimum d’effort.

Fonctionnalités

Certaines des fonctionnalités clés d’Acephal incluent :

– Interface sans code pour la création de formulaire

– Création facile de formulaires avec la fonction glisser-déposer

– Tableau pour afficher les réponses des utilisateurs

– Exportation des réponses des utilisateurs sous forme de fichiers CSV ou XLS

– Champ de fichier avec la possibilité de modifier la taille du fichier

– Tous les formulaires enregistrés pour un accès et une gestion faciles

Avec Acephal, les utilisateurs peuvent facilement créer des formulaires pour une variété de fins, y compris les sondages, les formulaires de feedback et bien plus encore.

Malheureusement Acephal n’est plus en ligne depuis un certain temps.

Une analyse basique: formation à THP

Ouverture de fichier CSV

Répertoire GitHub:

  • GitHub GHub : https://github.com/alegarn/S4J2-THP2-Pandas-Basics

Language:

  • Python Python

Librairies:

Type de projet:

  • Petits projets
  • Data Analyse

La librairie Pandas

Pandas est une librairie Python très utilisée en Data Analyse car elle comporte les outils nécessaires à une analyse de donnée (effectuer un chargement, un nettoyage, une transformation et une analyse).

Toute donnée est transformée en « Dataframe », bloc de base avec la librairie Pandas. Les Dataframes sont des tableaux pouvant accueillir des millions de lignes et de colonnes… si vous avez assez de mémoire RAM.

Une analyse basique

Le but de cet exercice est de manipuler Pandas directement en effectuant une analyse. Ici peu de nettoyage, et des commandes de base.

Et voici un intitulé comportant les questions.

Répondre aux questions posées par ton manager

Ton manager t’a donné une liste de questions pour te rendre la tâche plus concrète. Il t’a demandé de répondre à ces questions dans un premier temps en laissant apparentes tes requêtes sur le Notebook.

  • Combien y-a-t-il de musées en France métropolitaine ?
  • Dans quelle(s) ville(s) y-a-t-il de plus de musées ?
  • Quel est le nombre moyen de musées par ville ?
  • Quel est le nombre médian de musées par ville ?
  • Comment sont répartis les musées par type (en pourcentage) ?
  • Combien y-a-t-il de musées dont le nom commence par « Château » ?
  • Pour combien de musées dispose-t-on de l’adresse du site web ?
  • Quel département français possède le plus de musées sur son territoire ?
  • Quel département français possède le moins de musées sur son territoire ?
  • Combien de musées ont « Napoléon » dans leur nom ?

Charger et nettoyer les données

Toute analyse commence par importer les librairies, chargement, le nettoyage de donnée, cette fois ci très succint: enlever les colonnes en trop.

import seaborn as sns
import numpy as np
import pandas as pd

import csv
df = pd.read_csv(r'liste-des-musees-de-france-2021-point-virgules.csv', sep=";")

columns_to_drop = ['osm_id', 'country_code', 'country', 'lat','lon', 'wikidata', 'fax', 'description', 'date_added' ]
data_dropcol = df.drop(columns_to_drop,axis=1)

print(data_dropcol.head(10))

Manipuler les Dataframes

  • Combien y-a-t-il de musées en France métropolitaine ?
index = data_dropcol.index
number_of_rows = len(index)
total_museum = number_of_rows

museum_number_template = """Il y a au total {} musées en France."""

museum_number_message = museum_number_template.format(total_museum)
print(museum_number_message)
Il y a au total 3784 musées en France.
  • Dans quelle(s) ville(s) y-a-t-il de plus de musées ?
data_cities_all_col = pd.DataFrame(data_dropcol, columns= ['name','city'])

city_museum_number_template = """Il y a au total {} musées en France."""


museum_number_each_city = data_cities_all_col.pivot_table( columns=['city'], aggfunc='size').sort_values
number = data_cities_all_col['city'].str.split(' ').str[0:3].value_counts()

number_top_10_text = """Le top 10 'villes avec le plus de musées en France: 
{} """

number_top_10_message = number_top_10_text.format(number[0:11])


print (number_top_10_message)
Le top 10 'villes avec le plus de musées en France: 
[Paris]         175
[Lyon]           33
[Marseille]      28
[Grenoble]       22
[Bordeaux]       20
[Toulouse]       20
[Nice]           16
[Strasbourg]     16
[Rouen]          15
[Lille]          15
[Reims]          13
Name: city, dtype: int64 
  • Quel est le nombre moyen de musées par ville ?
per_city_number_museum_mean = """Il y a en moyenne {} musées par ville."""
museum_number_each_city_mean = data_cities_all_col.pivot_table( columns=['city'], aggfunc='size').mean(axis=0)
museum_mean_message = per_city_number_museum_mean.format(museum_number_each_city_mean)

print(museum_mean_message)
Il y a en moyenne 1.647930283224401 musées par ville.
  • Quel est le nombre médian de musées par ville ?
per_city_number_museum_median = """Nombre médian de musées par ville: {}"""
museum_number_each_city_median = data_cities_all_col.pivot_table( columns=['city'], aggfunc='size').median(axis=0)
museum_median_message = per_city_number_museum_median.format(museum_number_each_city_median)

print(museum_median_message)
Nombre médian de musées par ville: 1.0
  • Comment sont répartis les musées par type (en pourcentage) ?
data_tags = pd.DataFrame(data_dropcol, columns = ['name','tags'])

museum_number_each_tag = data_tags.pivot_table(columns=['tags'], aggfunc='size')

museum_number_each_tag_percent = pd.DataFrame(museum_number_each_tag, columns=['size'])

museum_number_each_tag_percent['percent'] = (museum_number_each_tag_percent['size'] / museum_number_each_tag_percent['size'].sum()) * 100

sample = museum_number_each_tag_percent.loc[museum_number_each_tag_percent["percent"]>5]

museum_tag_message_string = """Voici la répartition (> 5%) des types de musée: 
{}"""
museum_tag_message = museum_tag_message_string.format(sample)

print(museum_tag_message)
Voici la répartition (> 5%) des types de musée: 
                                               size    percent
tags                                                          
osm:museum                                     2175  57.509254
osm:museum;type:ecomusee                        387  10.232681
osm:museum;type:musee technique et industriel   194   5.129561
  • Combien y-a-t-il de musées dont le nom commence par « Château » ?
name_castle_sentence = """Il y a un nombre de 'Château' musée de {} ."""
sample
name_castle = pd.DataFrame(data_dropcol, columns= ['name']).add_prefix('name_')
name_castle['name'] = data_dropcol['name'].str.replace('�', "â")
new = name_castle['name'].str.split(' ').str[0].value_counts()

print(name_castle_sentence.format(new['Château']))
Il y a un nombre de 'Château' musée de 48 .
  • Pour combien de musées dispose-t-on de l’adresse du site web ?
with_website_template = """Il y a {} musées possédant un site internet."""

frame_internet = data_dropcol.pivot_table(index="website", values="name", aggfunc=np.count_nonzero)


with_website = with_website_template.format(frame_internet.sum(axis=0)['name'])

print(with_website)
Il y a 1636 musées possédant un site internet.
  • Quel département français possède le plus de musées sur son territoire ?
city_zipcode_text = """Le département français ayant le plus de musées est le {} avec {} musées."""

city_zipcode = pd.DataFrame(data_dropcol, columns= ['name', 'postal_code']) 
city_zipcode['department'] = city_zipcode['postal_code'].astype(str).str[:2]
museum_each_dep = city_zipcode.pivot_table(columns=['department'], aggfunc='size').add_prefix('zip_')

city_zipcode_message_zip = city_zipcode_text.format(museum_each_dep.loc[museum_each_dep == museum_each_dep.max()].to_string()[15:18], museum_each_dep.loc[museum_each_dep == museum_each_dep.max()][0])
                                          
print(city_zipcode_message_zip)
Le département français ayant le plus de musées est le 75  avec 180 musées.
  • Quel département français possède le moins de musées sur son territoire ?
city_zipcode_text_lower = """Le département français ayant le moins de musées est le {} avec {} musées."""
dep_zip_less = museum_each_dep.loc[museum_each_dep == museum_each_dep.min()].to_string()
dep_zip_total_min = museum_each_dep.loc[museum_each_dep == museum_each_dep.min()][0]

city_zipcode_full_message = city_zipcode_text_lower.format(dep_zip_less[15:18], dep_zip_total_min)


print(city_zipcode_full_message)
Le département français ayant le moins de musées est le 98  avec 8 musées. 

Déduction: Les outre-mer.

  • Combien de musées ont « Napoléon » dans leur nom ?
number_museum_emperor_template = """Il y a {} musées 'Napoléon'."""

search_name = pd.DataFrame(data_dropcol, columns= ['name']) 
search_name['name'] = data_dropcol['name'].str.replace('�', "é")
contain_values = search_name[search_name['name'].str.contains('Napoléon')].count()

number_museum_emperor_text = number_museum_emperor_template.format(contain_values.to_string())

print(number_museum_emperor_text)
Il y a name    4 musées 'Napoléon'.

Voilà, c’était une des premières utilisations de Pandas! (la 1ère…)

Le Bonus

Du SQL sur les Panama Papers: https://github.com/alegarn/S4J4-THP2-Basic-SQL

SQL_Panama_Papers: du SQL / Python et SQLite3

L’exploration de la base de donnée (retourner chaque table, leur relations et toutes les colonnes).

import sqlite3

def sqlite_table_schema(conn, name):
    """Return a string representing the table's CREATE"""
    cursor = conn.execute("SELECT sql FROM sqlite_master WHERE name=?;", [name])
    sql = cursor.fetchone()[0]
    cursor.close()
    return sql

# the database connection
with sqlite3.connect('databases/panamapapers.sqlite3') as conn:
    print("Opened database successfully")

# all tables
    cursor = conn.execute("SELECT name FROM sqlite_master WHERE type='table';")
    table_names = []
    for row in cursor:
        table_names.append(row)
       
    for row in table_names:

        transform = ''.join(row)
        
        exe = "SELECT * FROM {};"
        cursor = conn.execute(exe.format(transform))
        names = list(map(lambda x: x[0], cursor.description))
        print("\nNom de la table: " + transform + "\n")
        print("A complete table scheme : ")
        print(sqlite_table_schema(conn, transform), "\n")
        print(" Et voici chaque colonnes:")

        for columns in names:
            print(" - " + str(columns))

    print("Operation done successfully")
Opened database successfully

Nom de la table: source

A complete table scheme : 
CREATE TABLE source (
  id INTEGER PRIMARY KEY,
  source TEXT
) 

 Et voici chaque colonnes:
 - id
 - source

Nom de la table: status

A complete table scheme : 
CREATE TABLE status (
  status TEXT,
  id INTEGER PRIMARY KEY
) 

 Et voici chaque colonnes:
 - status
 - id

Nom de la table: country

A complete table scheme : 
CREATE TABLE country (
  code TEXT,
  country TEXT,
  id INTEGER PRIMARY KEY
) 

 Et voici chaque colonnes:
 - code
 - country
 - id

Nom de la table: address

A complete table scheme : 
CREATE TABLE address  (
  address TEXT,
  countries TEXT,
  country_codes TEXT,
  id_address BIGINT PRIMARY KEY,
  source_id INTEGER
  ,FOREIGN KEY (source_id) REFERENCES source(id)
) 

 Et voici chaque colonnes:
 - address
 - countries
 - country_codes
 - id_address
 - source_id

Nom de la table: intermediary

A complete table scheme : 
CREATE TABLE intermediary (
  id INTEGER  
	PRIMARY KEY
  ,name TEXT,
  source_id INTEGER,
  note TEXT,
  /* url TEXT, */
  id_address BIGINT,
  status_id INTEGER
  , url TEXT,FOREIGN KEY (id_address) REFERENCES address(id_address)
  ,FOREIGN KEY (source_id) REFERENCES source(id)
  ,FOREIGN KEY (status_id) REFERENCES status(id)
) 

 Et voici chaque colonnes:
 - id
 - name
 - source_id
 - note
 - id_address
 - status_id
 - url

Nom de la table: entity

A complete table scheme : 
CREATE TABLE entity (
  id INTEGER PRIMARY KEY,
  name TEXT,
  jurisdiction TEXT,
  jurisdiction_description TEXT,
  incorporation_date DATE,
  status TEXT,
  service_provider TEXT,
  source TEXT,
  note TEXT,
  id_address BIGINT,
  end_date DATE
  , url TEXT, lifetime INTEGER,FOREIGN KEY (id_address) REFERENCES address(id_address)
) 

 Et voici chaque colonnes:
 - id
 - name
 - jurisdiction
 - jurisdiction_description
 - incorporation_date
 - status
 - service_provider
 - source
 - note
 - id_address
 - end_date
 - url
 - lifetime

Nom de la table: officer

A complete table scheme : 
CREATE TABLE officer (
  id INTEGER  
	PRIMARY KEY
  ,name TEXT,
  source_id INTEGER,
  note TEXT,
  country_id INTEGER
  ,FOREIGN KEY (country_id) REFERENCES country(id)
  ,FOREIGN KEY (source_id) REFERENCES source(id)
) 

 Et voici chaque colonnes:
 - id
 - name
 - source_id
 - note
 - country_id

Nom de la table: assoc_officer_entity

A complete table scheme : 
CREATE TABLE assoc_officer_entity (
  officer INTEGER,
  assoc_type TEXT,
  entity INTEGER,
  source_id INTEGER,
  start_date DATE,
  end_date DATE
  ,FOREIGN KEY (officer) REFERENCES officer(id)
  ,FOREIGN KEY (entity) REFERENCES entity(id)
  ,FOREIGN KEY (source_id) REFERENCES source(id)
) 

 Et voici chaque colonnes:
 - officer
 - assoc_type
 - entity
 - source_id
 - start_date
 - end_date

Nom de la table: assoc_inter_entity

A complete table scheme : 
CREATE TABLE assoc_inter_entity (
  inter INTEGER ,
  entity INTEGER,
  source_id INTEGER,
  FOREIGN KEY (inter) REFERENCES intermediary(id),
  FOREIGN KEY (entity) REFERENCES entity(id),
  FOREIGN KEY (source_id) REFERENCES source(id)
) 

 Et voici chaque colonnes:
 - inter
 - entity
 - source_id

Nom de la table: assoc_officers

A complete table scheme : 
CREATE TABLE assoc_officers (
  officer1 INTEGER ,
  assoc_type TEXT,
  officer2 INTEGER ,
  start_date DATE,
  end_date DATE,
FOREIGN KEY (officer1) REFERENCES officer(id),
FOREIGN KEY (officer2) REFERENCES officer(id)
) 

 Et voici chaque colonnes:
 - officer1
 - assoc_type
 - officer2
 - start_date
 - end_date

Nom de la table: assoc_intermediaries

A complete table scheme : 
CREATE TABLE assoc_intermediaries (
  interm1 INTEGER ,
  assoc_type TEXT,
  interm2 INTEGER ,
  start_date DATE,
  end_date DATE,
FOREIGN KEY (interm1) REFERENCES intermediary(id),
FOREIGN KEY (interm2) REFERENCES intermediary(id)
) 

 Et voici chaque colonnes:
 - interm1
 - assoc_type
 - interm2
 - start_date
 - end_date

Nom de la table: assoc_entities

A complete table scheme : 
CREATE TABLE assoc_entities (
  entity1 INTEGER ,
  assoc_type TEXT,
  entity2 INTEGER ,
  start_date DATE,
  end_date DATE,
FOREIGN KEY (entity1) REFERENCES entity(id),
FOREIGN KEY (entity2) REFERENCES entity(id)
) 

 Et voici chaque colonnes:
 - entity1
 - assoc_type
 - entity2
 - start_date
 - end_date

Nom de la table: assoc_inter_offi

A complete table scheme : 
CREATE TABLE assoc_inter_offi (
  inter INTEGER ,
  assoc_type TEXT,
  officer INTEGER,
  start_date DATE,
  end_date DATE,
  FOREIGN KEY (inter) REFERENCES intermediary(id),
  FOREIGN KEY (officer) REFERENCES officer(id)
) 

 Et voici chaque colonnes:
 - inter
 - assoc_type
 - officer
 - start_date
 - end_date

Nom de la table: assoc_officer_interm

A complete table scheme : 
CREATE TABLE assoc_officer_interm (
  officer INTEGER ,
  assoc_type TEXT,
  interm INTEGER,
  start_date DATE,
  end_date DATE,
  FOREIGN KEY (officer) REFERENCES officer(id),
  FOREIGN KEY (interm) REFERENCES intermediary(id)
) 

 Et voici chaque colonnes:
 - officer
 - assoc_type
 - interm
 - start_date
 - end_date
Operation done successfully

Pour les Panama Papers, je vous donne les questions:

  • Combien la base de données contient-elle de sociétés offshores différentes dont la source est « Panama Papers » ?
  • Quel intermédiaire a créé le plus de sociétés offshores ? A-t-on son adresse et son pays ?
  • Combien la base contient-elle de bénéficiaires avec un nom unique ? Quel est le bénéficiaire dont le nom revient le plus souvent ?
  • Donner la liste des juridictions avec le nombre d’entreprises offshores enregistrées sur chaque territoire, triée par ordre décroissant.
  • Regrouper les sociétés offshores par statut, et trier la liste par ordre décroissant.
  • Trouver la liste des bénéficiaires dont le nom contient « BNP » et ajouter, pour chaque bénéficiaire, le nom des sociétés offshores.
  • Trouver la liste des sociétés dont la juridiction est « France », « Monaco » ou « Réunion ».
  • Trouver la liste des sociétés dont le pays de l’adresse et le pays de la juridiction sont différents.
  • Trouver la liste des bénéficiaires qui ont des sociétés au même nom et enregistrée à la même date, trier la liste par odre décroissant.
  • Donner la liste des intermédiaires qui ont aussi été bénéficiaires, en ajoutant leur nom de bénéficiaire et leur adresse.

Beaucoup de similarités entre le premier et le deuxième exercice, seulement les 2 dernières seront exposées:

  • Donner le top 10 des bénéficiaires qui ont le plus d’identités différentes (similar name and address) et le nombre d’identités correspondant.
# the database connection
with sqlite3.connect('database_sqlite3/panamapapers.sqlite3') as conn:
    print("Opened database successfully")
    sentence = "Top {} officer with similar name and address (multiple identities): \n officer: {} , {} identities."
# all tables
    i = 1
    cursor = conn.execute("SELECT O.name, count(O.id) FROM officer as O, assoc_officers as AO WHERE O.id = AO.officer1 AND AO.assoc_type = 'similar name and address as' GROUP BY O.name ORDER BY COUNT(O.id) DESC LIMIT 10 ;")
    for row in cursor:
        print(sentence.format(i, row[0], row[1]))
        i = i + 1
Opened database successfully
Top 1 officer with similar name and address (multiple identities): 
 officer: NORTH ATLANTIC SERVICES LIMITED , 811 identities.
Top 2 officer with similar name and address (multiple identities): 
 officer: BROCK NOMINEES LIMITED , 575 identities.
Top 3 officer with similar name and address (multiple identities): 
 officer: TENBY NOMINEES LIMITED , 529 identities.
Top 4 officer with similar name and address (multiple identities): 
 officer: MOHUL NOMINEES LIMITED , 513 identities.
Top 5 officer with similar name and address (multiple identities): 
 officer: SCIVIAS TRUST MANAGEMENT LTD , 369 identities.
Top 6 officer with similar name and address (multiple identities): 
 officer: FORMIA LIMITED , 363 identities.
Top 7 officer with similar name and address (multiple identities): 
 officer: RICHMOND NOMINEES LIMITED , 355 identities.
Top 8 officer with similar name and address (multiple identities): 
 officer: ELCAN NOMINEES LIMITED , 312 identities.
Top 9 officer with similar name and address (multiple identities): 
 officer: DORCHESTER INTERNATIONAL INC. , 311 identities.
Top 10 officer with similar name and address (multiple identities): 
 officer: CAVERSHAM NOMINEES LIMITED , 300 identities.
  • Donner le top 10 des bénéficiaires qui ont le plus de parts toujours valides dans des entreprises offshores (dont la date de fin n’est pas encore passée).

with sqlite3.connect('database_sqlite3/panamapapers.sqlite3') as conn:
    print("Opened database successfully")
    sentence = "Number {} officer {}, Total offshore societies owned, still active : {}."
    print("Officers with entities still 'Valid'")

    cursor = conn.execute("SELECT O.name, count(O.id) FROM entity as E, officer as O, assoc_officer_entity as AOE WHERE O.id = AOE.officer and AOE.entity = E.id and E.status = 'Active' GROUP BY O.id ORDER BY COUNT (O.id) DESC LIMIT 10.")
    i = 1
    for row in cursor:
        print(sentence.format(i, row[0], row[1]))
        i = i + 1
Opened database successfully
Officers with entities still 'Valid'
Number 1 officer MOSSFON SUBSCRIBERS LTD., Total offshore societies owned, still active : 2111.
Number 2 officer BOS NOMINEES (JERSEY) LIMITED, Total offshore societies owned, still active : 294.
Number 3 officer BOS SECRETARIES (JERSEY) LIMITED, Total offshore societies owned, still active : 284.
Number 4 officer MOSTALINA INVESTMENTS S.A, Total offshore societies owned, still active : 113.
Number 5 officer BROCK NOMINEES LIMITED, Total offshore societies owned, still active : 96.
Number 6 officer Dorchester International Inc, Total offshore societies owned, still active : 95.
Number 7 officer Cannon Nominees Limited, Total offshore societies owned, still active : 93.
Number 8 officer INTERCON LIMITED, Total offshore societies owned, still active : 87.
Number 9 officer MAYTREE OVERSEAS S.A., Total offshore societies owned, still active : 87.
Number 10 officer TENBY NOMINEES LIMITED, Total offshore societies owned, still active : 82.

Réponses: https://github.com/alegarn/S4J4-THP2-Basic-SQL

La base de Dash

Pourquoi ce projet?

Dans tous les outils Python existant pour faire un dashboard interactif sans certaines connaissances en HTML/CSS ou Javascript, se trouve une librairie du nom de Dash.

Et combiné avec Plotly (qui s’occupe des graphiques), réaliser des dashboards est relativement simple.

Tableau Tableau est très puissant, toutefois plutôt gourmant et coûteux. Pour réaliser des dashboards, il n’est toutefois pas indispensable.

Répertoire GitHub:

  • GHub https://github.com/alegarn/basic_dash

Language:

  • Python Python

Type de projet:

  • Petit projet
  • Data Visualisation

Libraries:

Aperçu

0 – Ouvrir les données et faire une Dataframe

import pandas as pd

# 1: simple chart
import plotly.express as px
# 1 + 2: the pie chart
import plotly.graph_objects as go

# to create the final dashboard
from dash import Dash, dcc, html, Input, Output
from dash.dependencies import Input, Output

with open('data/combined_final_last_10_years.csv', 'r') as file:
    data_last = pd.read_csv(file, sep=',')

# save the csv datas, and put it in the last graph
total_data = data_last

#a slice of data, new dataframe
data_last = data_last[['continent', 'country', 'year', 'income_per_person', 'gini_index']]
df = pd.DataFrame(data_last)

# to display 10 lines
df.head(10)

1 – Réaliser un graphique en bar simple

fig1 = px.bar(gini_con_mean_2006, x='continent', y='gini_index')

fig1.show()

Ce premier graphique construit avec Plotly, pas de CSS, et seulement un croisement de 2 colonnes.

Ici le continent le plus inégalitaire en 2006, possédant un indice de Gini de prêt de 50 est le continent Américain, ce qui n’est pas le cas de l’Europe, son indice étant proche de 32.

2 – Construire un graphique camembert plus compliqué

fig2 = go.Figure(data=[go.Pie(labels = labels,
                             values = values)])
colors = ['gold', 'mediumturquoise', 'darkorange', 'lightgreen']

fig2.update_traces(
  hoverinfo='label', 
  textinfo='value', 
  textfont_size=20,
  marker=dict(
    colors=colors, 
    line=dict(color='#000000', 
    width=2)))

fig2.show()

Un autre test de Plotly. Par contre, il y a un rajout de CSS (des couleurs, titre stylisé).

Le camembert permet de comparer facilement des données, « parties » d’un même total. L’Océanie, Nouvelle Zélande et Australie, est loin devant avec un revenue moyen de 35 950$ par an.

3 – Commencer le dashboard

Une fois les 2 premiers graphiques affichés, commence la construction du Layout (la page du Dashboard) contenant les éléments (graphiques et explications).

# new Dash app
app = Dash(__name__)

# CSS 
colors = {
    'background': '#111111',
    'text': '#7FDBFF'
}


# HTML page/layout

app.layout = html.Div([
    # Dashboard title
    html.Div([
        html.H1(
            children='Countries Plotly Visualization',
            style={'textAlign': 'center','color': '#2E10E6'})
        ]),

    # the first figure div, a simple one
    html.Div([
        html.H1(
            children='Bar chart: continent Gini index average in 2006'),
        html.Div(
            dcc.Graph(
                id='gini_index_per_continent',
                figure=fig1)
        )]),

    # the second figure div, simple with CSS
    html.Div([
        html.H1(
            children='Pie chart: The 2006 average income per person and continent',
            style={'textAlign': 'center','color': colors['text']}),
        html.Div(
            dcc.Graph(
                id='income_per_continent',
                figure=fig2)
        )]),

    # the third figure div, but this one can be changed
    html.Div([
        html.H1(
            children='Scatter chart: Yearly internationals income per person compare to the Gini index',
            style={'textAlign': 'center','color': colors['text']}),
        # https://dash.plotly.com/basic-callbacks
        html.Div([
            dcc.Graph(
            id='gini_with_income_per_continent'),
            dcc.Slider(
                total_data['year'].min(),
                total_data['year'].max(),
                step=None,
                value=total_data['year'].min(),
                marks={str(year): str(year) for year in total_data['year'].unique()},
                id='year-slider'
            )
        ])
    ])
])

Le code semble plutôt simple, on se perd dans les parenthèses. Cependant lorsqu’on sait faire un peu de HTML / CSS, écrire les balises est plus simple.

4 – Le graphique « dispersé » (Scatter)

Pour ce dernier graphique, c’est le moment d’en mettre plein la vue!

Voici un graphique type « dispersé », contenant 2 axes, des couleurs et des bulles de taille différentes!

Niveau visualisation, il permet de découper certains « groupes » (la plupart des pays Européens ont un index de Gini faible, contrairement à l’Amérique), en plus de permettre des rapprochements variés (on remarque les pays d’Afrique les plus riches sont aussi les plus inégalitaires, ce qui n’est pas le cas de l’Asie).


# that's the same for every graph, unique parameter to change 
@app.callback(
    # graph show
    Output('gini_with_income_per_continent', 'figure'),
    # object to update a graph
    Input('year-slider', 'value'))

# a unique function, even with multiple graphs
def update_figure(selected_year):

    # updated data
    filtered_df = total_data[total_data.year == selected_year]

    # graphic result
    fig3 = px.scatter(
       filtered_df, 
       x="income_per_person",
       y="gini_index",
       size="demox_eiu", 
       color="continent",
       hover_name="country",
       log_x=True, 
       size_max=60
     )

    # change parameters
    fig3.update_layout(transition_duration=300)
    
    return fig3

# to run the server
if __name__ == '__main__':
    app.run_server(debug=True, use_reloader = False) # use_reloader for Jupyter Notebook

Ci-dessus, on peut voir une introduction aux callbacks, le back-end qui permet quelques fonctionnalités rendant notre dashboard interactif (le slider avec les années partant de 2006 à 2016, s’ajoute à cela des données au survol d’une bulle).

Lancer le serveur, cela prend 2 lignes de codes. Avec un Jupyter-Notebook une petite subtilité, « use_reloader = False » pour ne pas faire se relancer le code tout le temps, puis s’interroger « pas de serveur??? WT.??? »

Une conclusion

Test concluant, la prise en main (à l’aide de tutoriaux) se fait sans trop de difficultés. De plus le rendu est satisfaisant. Très bonne librairie gratuite et open-source!

Sing those words! (0)

Apprendre une langue peut s’avérer compliqué. Pouvoir écrire, lire, parler, choisir le vocabulaire à apprendre… beaucoup de choses à mettre en place. Mais il existe des outils dans le but de faciliter ce processus (flashcards, de la mise en relation avec des tuteurs, mnémotechnique …).

Le vocabulaire n’est pas toujours facile à apprendre. Toutefois, il se trouve que certaines personnes s’aident des paroles de chansons pour s’en souvenir. Avec ce programme, le but est d’optimiser la recherche de paroles à apprendre (qui se trouvent dans la langue voulue), pour choisir une ou plusieurs chansons, et les écouter le plus possible.

Répertoire GitHub:

Language de programmation:

  • Ruby Ruby

Techniques utilisées:

  • Scrapping (la gem: Nokogiri)
  • Production de fichiers textes
  • Calculs et CSV (gem: csv)
  • Programmation Orientée Objet
  • Manipulation de données (arrays, tables, formats, et autres)

Aperçu

Le programme est un mvp(p), qui valide la faisabilité de notre concept! L’UX est quasi inexistante, seulement 50 chansons disponibles, toutes venant du groupe a1, entres autres…

Pour commencer, il faut un menu:

Menu: le choix d’actions à effectuer

Plusieurs choix:

  • 1- Prendre chaque paroles de chanson qu’il nous faut (première version: seulement des paroles du groupe a1), le scrapping d’un site web.
  • 2- Pouvoir choisir son propre vocabulaire en l’écrivant sur le terminal, avec aussi le choix de scraper un dictionnaire de fréquence.
  • 3 – Calculer tous les « scores » (toutes les chansons)
  • 4 – Comparer ces ensembles de paroles, choisir finalement ce qu’on veut
  • 5 – Exécuter 1, 3 et 4.
  • 6 – Sortir du programme

1 – Trouver les paroles

Méthode: Le scrapping

Outils: ‘gem’ Nokogiri

Contenu:

  • Prendre les adresses de plusieurs pages
  • VPN (pour changer d’adresse IP)
  • Manipulation et création de dossiers / textes

2 – Choisir son vocabulaire

Méthode: Scrapping / Formulaire

Outils: gem ‘Nokogiri‘ / gem ‘CSV

Contenu:

  • Un scrapper basique (page unique)
  • Transformation en CSV

3 – Scores des paroles

Méthode: Dossiers / Parsing

Outils: gem ‘CSV

Contenu:

  • Manipulation de textes / CSV
  • Analyse de texte (parsing)
  • String en array
Les mots sont comptés dans chaque chanson.

4 – Sélectionner des musiques

Méthode: Dossiers / Parsing / Calculs

Outils: gem ‘CSV

Contenu:

  • Manipulation de textes / CSV
  • Analyse de texte (parsing)
  • Comparaison
  • Trie
Le comparateur de paroles

Conclusion

Ce projet valide le concept, rappelle des bases de programmation (sorting, poo, arrays) et de data (tables, csv), mais trouve ses limites avec toutes les limites (1 langue, a1 comme seul groupe) ainsi qu’un design limité (console).

How to quickly connect Notion to Twitter and LinkedIn with Zapier

The article on your blog is finished? Need to communicate on multiple channels? You can write some quick stuff and copy and paste, on Twitter and LinkedIn… the first, second, third time… Time lost…

Is there any solution to do that in once? Yes, actually you could automate that by yourself (nice dev project). If you need rapid solution, Zapier could do the trick.

On zapier.com it’s possible to connect thousands (that’s a lot) web/desktop app’s, then choose a repetitive task and automate it (that’s a “Zap”). With a little need) for automation (annoying stuff, repetitive, short stuffs), it’s free (only 100 tasks/actions a month with 5 “zaps”).

I – Writing your message – Database on Notion

1) Have a Notion account

2) You need to create a new page “database” type (a normal page will not work in Zapier)

https://s3.us-west-2.amazonaws.com/secure.notion-static.com/2ace2f33-7bf5-45e2-a00d-5a2137b890bf/page.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20220622%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20220622T021938Z&X-Amz-Expires=86400&X-Amz-Signature=fb3e3808b863bfbe42d500e0467d9a22ba78fa4f0438bca4298f5e8c5608cb7f&X-Amz-SignedHeaders=host&response-content-disposition=filename%20%3D%22page.png%22&x-id=GetObject
Where you want to write: a new article group (”Share” as you can see)

Where you want to write: a new article group (”Share” as you can see)

https://s3.us-west-2.amazonaws.com/secure.notion-static.com/b024995b-3075-4b38-af29-65f69db48318/new_page.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20220622%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20220622T022308Z&X-Amz-Expires=86400&X-Amz-Signature=ca4d13ed6284da3e03dd972bc675ee2eea00f7c1050fea23c141d1ec5498eb78&X-Amz-SignedHeaders=host&response-content-disposition=filename%20%3D%22new_page.png%22&x-id=GetObject
Create a new page where you want

Create a new page where you want

https://s3.us-west-2.amazonaws.com/secure.notion-static.com/f46247ab-4eea-4cc1-9d41-f08a4ba16e1a/gallery.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20220622%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20220622T022309Z&X-Amz-Expires=86400&X-Amz-Signature=3f2612c7730d374842a50103cb80837df2dffb794703e0eb67ef5713e1bc1955&X-Amz-SignedHeaders=host&response-content-disposition=filename%20%3D%22gallery.png%22&x-id=GetObject
Database options: The option “Gallery” is choosed here (image + few text)

Database options: The option “Gallery” is choosed here (image + few text)

3) You have a new page of Database’s type! Let’s do your first quick message!

In your Gallery page, click on “New page”

In your Gallery page, click on “New page”

Once opened, put a Title, a quick text (Property 1 section), add your link inside.

One picture in icon section can be more appealing.

Once completed (the icon should be enough)

Once completed (the icon should be enough)

4) You can save your work. Notion’s part end’s here! 👍

II – Let’s automate!

0) Create a Zapier account

1) Do your first Zap! Step 1: Trigger

Choose your app, add a Trigger

Choose your app, add a Trigger

In your Zapier account, find “+ Create Zap”, choose it. You can add Notion to be the first step. One option possible, your message is being send at the “save” step of a new Database Item (for example: new Gallery item).

Next add your Notion account. You have to manage the access from your Notion’s Database items.

Great, before you added a new page who has a Database Item! So choose only an item you are sure that you want to share (Sending every little bit of new script in your Notion… that’s costly, hundreds of $ for Zapier with a really productive workday)

Page to select (at each “save” one “send”)

Page to select (at each “save” one “send”)

Trigger on a Database item

Upper: Trigger on a Database item

Now, let’s test. Zapier is really looking to the good Database? Look at it, if “Property 1” got the god text, you did it!

2) Do your first Zap! Step 2: let’s take Twitter.

Write “Twitter” in the field, choose it. Click on “Create Tweet” in Action Event

Choose « Create Tweet » when in Action Event

Up: Choose « Create Tweet » when in Action Event

Time to choose your account, i let you do that 😉

So it’s Action time! Export your Notion page to a Tweet.

New message should begins your new tweet. Write that short… super short text, plus your Notion Title, with “Property 1”’s text.

If you have “No Data” near “1. Title”:

  • you didn’t wrote anything
  • a problem with the database occured (that’s the wrong one? a wifi problem?…)

Put Data from Page in Notion

Icon time, add it when you see the “Image, Video or GIF” section.

Problem, there is no Icon…

Up: Problem, there is no Icon…

You are close to finish!

Time for “Test Action”

Check your Twitter posts!

Choose “Test & Review”, you can now check Twitter, “Posts” section. There is? Nice!

3) LinkedIn: cf “section 2)” steps are the same 😉

4) Time to work on your articles!

Easy sharing 👌

Conclusion

That’s it for today, so you have the power to automate (using Zapier)!

Play with it during first 14 days, free paid plan 😉 So it’s possible creating “Multiple steps” zaps, or modifications with the zaps!

Data analyse : Ligue 1

Après 2 mois de formation Data à The Hacking Project, pour valider cette formation, un projet final est requis. Le travail en équipe est requis, je l’ai donc réalisé avec 4 autres « moussaillons » (« apprenants » de THP).

La visualisation des données de la Ligue 1 de football a obtenu tous les critères nécessaires d’un projet possédant les critères nécessaires afin d’être validé.

Derrière ce dashboard interactif il y a une idée: visualiser de manière ludique des données qui ne sont pas ou peu accessibles chez des fans de football ne regardant pas des statistiques tous les jours.

Les outils techniques utilisés

Languages de programmation:

  • Ruby Ruby: scrapper des données (gem: Nokogiri)
  • Python Python: nettoyer les données

Outils utilisés:

  • Jup_notebk Jupyter Notebook: nettoyer la donnée
  • Tableau Tableau software: visualiser des données
  • Excel Excel: nettoyage, transformation et stock de nos données
  • Google Slides Google Slides: logiciel de présentation

Caractéristiques du projet:

Cahier des charges

Un cahier des charges est mis en place, répondant aux besoins d’un « célèbre » client dans le monde du football (demande fictive).

Voici le cahier des charges
Un sommaire, 7 parties.

Organisation

Outils contenant les projets: Google Dri Google drive

Communication vocale:Discord Discord

Outils d’organisation des tâches: Trello Trello

Extraction des données

Quand la donnée est disponible au format csv, téléchargement simple:

  • https://thesportsdb.com/

Parfois les données ne sont pas disponibles en csv, on code alors un scrapper avec Ruby Ruby (gem: Nokogiri):

  • https://www.ligue1.fr/
  • https://thesportsdb.com/

GHub Github: scrapper

Transformation de la donnée

Après l’étape de récupération des données, l’étape de transformation. Dans ce projet, il a suffit de peu de transformation, le scrapper faisant une bonne partie du travail.

Stack transformation:

  • Excel Excel: créer visuellement les colonnes avec des formules
  • Python Python + Jup_notebk Jupyter Notebook: Mise en forme de la donnée, peu de statistiques.
Modification des données avec Jupyter et Python

Le but du projet: présenter des données de façon visuelle. Finalement il y a peu de statistiques, peu de transformation des données (des dataframes, quelques jonctions, remplacement de caractères…).

Chargement dans une « base de données »

Le fichier final contenant peu de lignes (681) et quelques 66 colonnes, le choix se porte donc sur un fichier Excel (.xlsx) comme « base de données ». Il est facile à manipuler et à parcourir, dans ce cas pas besoin d’utliser un Notebook Jupyter.

Le fichier Excel final

Comme certaines données étaient manquantes (thesportsdb.com), c’est à la main qu’il a fallu écrire quelques lignes de données (du site ligue1.fr). Dans ce cas-ci, travailler dans un tableau Excel est plus pratique.

Visualisation

Choisi pour son caractère pratique et puissant :

  • Tableau Tableau software.

Les filtres et des formules (MAKEPOINT / MAKELINE…) ont été utilisées pour créer la visualisation finale, la rendant interactive.

Tableau 1: Première partie

Ci-dessus, la visualisation contenant les clubs jouant en Ligue 1, leur classement général, avec les statistiques cumulées par journée. La journée de Ligue 1 correspond à une semaine de rencontres.

Tableau 2: deuxième partie

Ensuite, sont visualisées des statistiques tout au long du championnat, le classement général, le nombre de buts marqués et encaissés par match, la possession par journée terminant ce premier tableau.

Tableau 3: troisième partie

On peut accéder à la dernière partie de la visualisation en cliquant sur le lien « Accéder aux statistiques par journée » (Tableau 1 et Tableau 3). Cette partie présente certaines données sélectionnées par match, pour chaque équipe (précision par équipe des tirs, ou les tirs convertis en buts).

Tester ce projet c’est par ici!

Présentation des données

Lorsque le processus ETL est finis, et après visualisation, une présentation Google Slides afin de présenter le produit au client. L’environnement de travail utilisant Google, Google Slides fut choisi.

Le plan de la présentation du projet

Pour conclure, ce projet mêle un peu de code, un peu de visualisation et un peu de compétences comme la présentation orale ou l’organisation.

Une bonne équipe ça fait beaucoup, merci à vous!

ScoreIT

Jumbotron

Vers le projet: https://scoreit-thp.herokuapp.com/

Le projet:

3 mois de bootcamp The Hacking Project, 10 heures de code par jour… bienvenue à THP. Le projet final, ce dernier projet pour valider notre formation doit se faire en équipe et mêler le framework Ruby on Rails, HTML, CSS, Bootstrap pour la rapidité, un peu de Javascript.

ScoreIT: Certifiez la solidité de votre startup en vous faisant noter.

https://scoreit-thp.herokuapp.com/

Les startup se font noter sur ScoreIT dans le but d’obtenir plus facilement des financements de potentiels investisseurs. Un score global est donné en remplissant un questionnaire par catégorie (Finance, Stratégie, …). Après notation, il est possible de récupérer un certificat (format pdf) résumant score globale et scores par catégorie.

Notation finale (le dashboard fait de Js), peut être convertie en pdf.

Un espace administrateur est prévu, contenant toutes données concernant les utilisateurs.

Une vue du dashboard admin (ScoreIT)

Informations

  • Temps: 2 semaines
  • Équipe: 5 personnes
  • : https://github.com/guillaume-rygn/ScoreIT
  • Frameworks: Ruby on Rails
  • Base de donnée: PostGreSQL PostGreSQL

Caractéristiques du site

La spécificité:

Système de notation / calculs maisons, temps pour chaque notation (combiné avec Stripe pour ne pas attendre de se faire re-noter).

Liste des languages utilisés:

  • html HTML
  • CSS CSS
  • Javascript Javascript
  • Ruby Ruby

Base de donnée:

  • PostGreSQL PosteGreSQL

Framework:

  • Ruby on Rails

Librairies:

  • Bootstra Bootstrap: Adaptation mobile / grand écran
  • Ruby gem: Devise Devise: Authentification / session administrateur
  • Omniauth: Se connecter avec Github ou LinkedIn
  • wicked_pdf: Transformer du html en pdf

API:

  • SendGrid SendGrid: Mailer
  • Stripe Stripe: Paiement

Outils de travail en équipe:

  • Trello Trello: Organisation des tâches (lien)
  • Git Git: Gestion des versions
  • GitHub: Partage de code
  • Discord Discord: Discussions vocales