Merge github-final: KPIs et donnees dynamiques
This commit is contained in:
commit
e81127fc03
748
TECHNICAL_REFERENCE.md
Normal file
748
TECHNICAL_REFERENCE.md
Normal file
@ -0,0 +1,748 @@
|
||||
# Freelance Dashboard - Document Technique et Fonctionnel
|
||||
|
||||
**Type de projet:** Business Intelligence / Data Visualization
|
||||
**Technologie principale:** Microsoft Excel (avec VBA/Macros)
|
||||
**Public cible:** Freelances, consultants independants, petites entreprises
|
||||
**Statut:** Proof of Concept - Production Ready
|
||||
|
||||
---
|
||||
|
||||
## Table des Matieres
|
||||
|
||||
1. [Vue d'Ensemble](#vue-densemble)
|
||||
2. [Problematique Metier](#problematique-metier)
|
||||
3. [Solution Proposee](#solution-proposee)
|
||||
4. [Architecture Technique](#architecture-technique)
|
||||
5. [Fonctionnalites Detaillees](#fonctionnalites-detaillees)
|
||||
6. [Modele de Donnees](#modele-de-donnees)
|
||||
7. [Indicateurs de Performance (KPIs)](#indicateurs-de-performance-kpis)
|
||||
8. [Technologies et Outils](#technologies-et-outils)
|
||||
9. [Processus de Developpement](#processus-de-developpement)
|
||||
10. [Competences Demontrees](#competences-demontrees)
|
||||
11. [Resultats et Impact](#resultats-et-impact)
|
||||
|
||||
---
|
||||
|
||||
## Vue d'Ensemble
|
||||
|
||||
### Concept
|
||||
|
||||
**Freelance Dashboard** est un tableau de bord Excel interactif concu pour le suivi en temps reel de l'activite d'un freelance. Il centralise trois dimensions cles : le temps travaille, les revenus generes et la gestion des clients.
|
||||
|
||||
Le dashboard transforme des donnees brutes en insights visuels exploitables, permettant une prise de decision rapide et informee.
|
||||
|
||||
### Objectifs du Projet
|
||||
|
||||
- **Simplifier le suivi d'activite** : Remplacer les feuilles Excel multiples par un dashboard unifie
|
||||
- **Visualiser la performance** : KPIs en temps reel, graphiques dynamiques, tendances
|
||||
- **Automatiser les calculs** : Formules avancees et macros VBA pour eliminer le travail manuel
|
||||
- **Professionnaliser la presentation** : Design moderne adapte a la demonstration client
|
||||
- **Faciliter la prise de decision** : Identifier rapidement les clients les plus rentables, les periodes creuses, les taux horaires reels
|
||||
|
||||
---
|
||||
|
||||
## Problematique Metier
|
||||
|
||||
### Pain Points Identifies
|
||||
|
||||
Les freelances font face a plusieurs defis dans la gestion de leur activite :
|
||||
|
||||
| Probleme | Impact |
|
||||
|----------|--------|
|
||||
| Donnees dispersees (temps, factures, emails) | Perte de temps, erreurs de facturation |
|
||||
| Absence de vision globale | Difficulte a identifier les clients rentables |
|
||||
| Calculs manuels de KPIs | Risque d'erreur, temps perdu |
|
||||
| Rapports non professionnels | Image peu serieuse face aux clients |
|
||||
| Suivi du temps approximatif | Sous-facturation, perte de revenu |
|
||||
|
||||
### Besoin Exprime
|
||||
|
||||
> "Je veux voir en un coup d'oeil mon CA du mois, mes clients les plus rentables, et savoir si je suis sur mes objectifs."
|
||||
|
||||
---
|
||||
|
||||
## Solution Proposee
|
||||
|
||||
### Approche
|
||||
|
||||
Creer un **dashboard Excel tout-en-un** avec :
|
||||
|
||||
1. **Tables de donnees structurees** pour la saisie (Excel Tables)
|
||||
2. **Formules avancees** pour les calculs automatiques (SUMIFS, INDEX/MATCH, LET)
|
||||
3. **Tableaux croises dynamiques** pour l'agregation flexible
|
||||
4. **Graphiques interactifs** pour la visualisation
|
||||
5. **Filtres visuels (Slicers)** pour l'exploration des donnees
|
||||
6. **Macros VBA** pour l'automatisation (refresh, export)
|
||||
7. **Design professionnel** avec mise en forme conditionnelle
|
||||
|
||||
### Avantages de la Solution
|
||||
|
||||
- **Zero installation** : Fonctionne avec Excel (presente sur 99% des PC)
|
||||
- **Leger et rapide** : Pas de base de donnees externe
|
||||
- **Personnalisable** : Le client peut modifier formules et design
|
||||
- **Portable** : Un seul fichier `.xlsm` a partager
|
||||
- **Evolutif** : Peut se connecter a d'autres sources (Access, Power Query, API)
|
||||
|
||||
---
|
||||
|
||||
## Architecture Technique
|
||||
|
||||
### Vue d'Ensemble
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ FREELANCE DASHBOARD │
|
||||
│ (FreelanceDashboard.xlsm) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────┼─────────────────────┐
|
||||
│ │ │
|
||||
┌────▼─────┐ ┌─────▼──────┐ ┌────▼─────┐
|
||||
│ DATA │ │ BUSINESS │ │ UI │
|
||||
│ LAYER │ │ LOGIC │ │ LAYER │
|
||||
└──────────┘ └────────────┘ └──────────┘
|
||||
│ │ │
|
||||
- Data_Clients - Formules KPIs - Dashboard
|
||||
- Data_Temps - Tableaux Croises - Graphiques
|
||||
- Data_Revenus - Colonnes calculees - Slicers
|
||||
- Config - Validation - Mise en forme
|
||||
```
|
||||
|
||||
### Structure du Fichier
|
||||
|
||||
| Onglet | Role | Type | Contenu |
|
||||
|--------|------|------|---------|
|
||||
| **Dashboard** | Interface utilisateur | UI | KPIs, graphiques, slicers, layout final |
|
||||
| **Data_Clients** | Donnees sources | Data | Table clients (ID, nom, secteur, date debut) |
|
||||
| **Data_Temps** | Donnees sources | Data | Entrees de temps (date, client, projet, heures) |
|
||||
| **Data_Revenus** | Donnees sources | Data | Paiements (date, client, montant, type) |
|
||||
| **Config** | Parametres | Settings | Annee, taux horaire, objectifs, listes deroulantes |
|
||||
| **TCD_Data** | Calculs intermediaires | Hidden | Tableaux croises dynamiques pour graphiques |
|
||||
|
||||
### Flux de Donnees
|
||||
|
||||
```
|
||||
1. SAISIE 2. VALIDATION 3. CALCUL
|
||||
│ │ │
|
||||
User entre Validation des Formules Excel
|
||||
donnees dans ──► donnees (listes ──► calculent les
|
||||
tables Excel deroulantes, plages) KPIs en temps reel
|
||||
│ │ │
|
||||
└────────────────────────┴───────────────────────┘
|
||||
│
|
||||
4. VISUALISATION
|
||||
│
|
||||
Graphiques et TCD
|
||||
rafraichis auto
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fonctionnalites Detaillees
|
||||
|
||||
### 1. Gestion des Donnees
|
||||
|
||||
#### Tables Structurees Excel
|
||||
|
||||
Toutes les donnees sont stockees dans des **Excel Tables** (ListObjects) :
|
||||
|
||||
- **Auto-expansion** : Nouvelles lignes ajoutees automatiquement
|
||||
- **References structurees** : `tbl_Clients[ClientID]` au lieu de `$A$2:$A$10`
|
||||
- **Filtres integres** : Clic sur en-tetes de colonnes
|
||||
- **Formules propagees** : Colonnes calculees automatiques
|
||||
|
||||
#### Validation des Donnees
|
||||
|
||||
| Champ | Validation | Message d'Erreur |
|
||||
|-------|------------|------------------|
|
||||
| ClientID | Liste deroulante depuis Data_Clients | "Selectionnez un client existant" |
|
||||
| Heures | Decimal entre 0.25 et 24 | "Entrez un nombre d'heures valide" |
|
||||
| Type | Liste (Facture/Acompte/Avoir) | "Type invalide" |
|
||||
| Date | Format date valide | "Date incorrecte" |
|
||||
|
||||
### 2. Calcul des KPIs
|
||||
|
||||
Le dashboard affiche **8 indicateurs cles** recalcules en temps reel :
|
||||
|
||||
| KPI | Formule | Interpretation |
|
||||
|-----|---------|----------------|
|
||||
| **CA Total** | `=SUMIFS(Data_Revenus[Montant], ...)` | Revenu genere sur la periode |
|
||||
| **CA Mois en Cours** | `=SUMPRODUCT((MONTH(...)=MONTH(TODAY()))...)` | Performance du mois actuel |
|
||||
| **Heures Totales** | `=SUMIFS(Data_Temps[Heures], ...)` | Volume de travail |
|
||||
| **Taux Horaire Moyen** | `=CA_Total / Heures_Totales` | Rentabilite reelle (vs. taux affiche) |
|
||||
| **Nb Clients Actifs** | `=SUMPRODUCT((COUNTIFS(...) > 0) * 1)` | Diversification du portefeuille |
|
||||
| **Top Client** | `=INDEX(MATCH(MAX(...)))` | Client le plus rentable |
|
||||
| **Heures Semaine** | `=SUMIFS(..., Date, ">="&DebutSemaine)` | Charge de travail hebdomadaire |
|
||||
| **Nb Projets** | `=COUNTA(UNIQUE(Data_Temps[Projet]))` | Diversite des missions |
|
||||
|
||||
**Formules avancees utilisees :**
|
||||
- `SUMIFS` / `COUNTIFS` : Agregations conditionnelles multiples
|
||||
- `INDEX` / `MATCH` : Recherches inversees
|
||||
- `SUMPRODUCT` : Calculs matriciels sans array formulas
|
||||
- `LET` (Excel 365) : Variables nommees dans formules
|
||||
- `XLOOKUP` (Excel 365) : Remplacant de VLOOKUP
|
||||
|
||||
### 3. Visualisation des Donnees
|
||||
|
||||
#### Graphiques Dynamiques
|
||||
|
||||
| Graphique | Type | Source Donnees | Insight |
|
||||
|-----------|------|----------------|---------|
|
||||
| **Evolution CA Mensuel** | Barres verticales | TCD sur Data_Revenus | Tendance revenue sur 12 mois |
|
||||
| **Repartition CA par Client** | Camembert / Donut | TCD sur Data_Revenus | Concentration du CA (regle 80/20) |
|
||||
| **Heures par Semaine** | Barres empilees | TCD sur Data_Temps | Charge de travail, identification surcharge |
|
||||
| **CA vs Heures** | Graphique combo | TCD combine | Correlation rentabilite/effort |
|
||||
|
||||
#### Tableaux Croises Dynamiques (TCD)
|
||||
|
||||
Les TCD permettent :
|
||||
- **Agregation flexible** : Glisser-deposer les champs
|
||||
- **Filtrage rapide** : Par periode, client, projet
|
||||
- **Calculs automatiques** : Somme, moyenne, compte, pourcentage
|
||||
- **Champs calcules** : Taux horaire = Montant / Heures
|
||||
|
||||
#### Slicers (Filtres Visuels)
|
||||
|
||||
```
|
||||
┌───────────────────┐ ┌───────────────────┐
|
||||
│ CLIENTS │ │ PERIODE │
|
||||
│ [ ] Acme Corp │ │ [ ] 2024 T1 │
|
||||
│ [x] Tech Sol. │ │ [x] 2024 T2 │
|
||||
│ [ ] Marketing │ │ [ ] 2024 T3 │
|
||||
└───────────────────┘ └───────────────────┘
|
||||
```
|
||||
|
||||
Les slicers sont connectes aux TCD et permettent un filtrage interactif sans formule.
|
||||
|
||||
### 4. Automatisation VBA
|
||||
|
||||
#### Module `mod_Refresh`
|
||||
|
||||
```vba
|
||||
Sub RefreshDashboard()
|
||||
' Recalcule toutes les formules
|
||||
Application.CalculateFull
|
||||
|
||||
' Rafraichit tous les tableaux croises
|
||||
Dim ws As Worksheet
|
||||
For Each ws In ThisWorkbook.Worksheets
|
||||
Dim pt As PivotTable
|
||||
For Each pt In ws.PivotTables
|
||||
pt.RefreshTable
|
||||
Next pt
|
||||
Next ws
|
||||
|
||||
MsgBox "Dashboard actualise!", vbInformation
|
||||
End Sub
|
||||
```
|
||||
|
||||
#### Autres Macros
|
||||
|
||||
| Macro | Description | Usage |
|
||||
|-------|-------------|-------|
|
||||
| `QuickRefresh` | Refresh silencieux (sans popup) | Bouton dashboard |
|
||||
| `ExportPDF` | Exporte le dashboard en PDF | Partage client |
|
||||
| `AddTimeEntry` | Formulaire VBA pour saisie rapide | UserForm |
|
||||
| `BackupData` | Sauvegarde les donnees dans CSV | Securite |
|
||||
|
||||
### 5. Design Professionnel
|
||||
|
||||
#### Palette de Couleurs
|
||||
|
||||
| Couleur | Hex | Usage |
|
||||
|---------|-----|-------|
|
||||
| Bleu fonce | `#2C3E50` | En-tetes, titres, elements principaux |
|
||||
| Vert | `#27AE60` | KPIs positifs, objectifs atteints |
|
||||
| Gris clair | `#ECF0F1` | Arriere-plan, zones neutres |
|
||||
| Rouge | `#E74C3C` | Alertes, objectifs non atteints |
|
||||
| Orange | `#F39C12` | Avertissements, zones d'attention |
|
||||
|
||||
#### Mise en Forme Conditionnelle
|
||||
|
||||
```
|
||||
Regle 1: SI CA >= Objectif ALORS Vert
|
||||
Regle 2: SI CA >= 80% Objectif ALORS Orange
|
||||
Regle 3: SI CA < 80% Objectif ALORS Rouge
|
||||
```
|
||||
|
||||
Applique sur :
|
||||
- Cellules KPIs
|
||||
- Barres de progression
|
||||
- Indicateurs de tendance (fleches ↑↓)
|
||||
|
||||
#### Layout Dashboard
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ FREELANCE DASHBOARD - 2025 [Refresh] [Export PDF] │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌─────────┐ │
|
||||
│ │ CA TOTAL │ │ CA MOIS │ │ HEURES │ │ TAUX │ │
|
||||
│ │ 45 230 € │ │ 8 500 € │ │ 312h │ │ 72 €/h │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ └────────────┘ └────────────┘ └────────────┘ └─────────┘ │
|
||||
│ │
|
||||
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌─────────┐ │
|
||||
│ │ NB CLIENTS │ │ TOP CLIENT │ │ H SEMAINE │ │ PROJETS │ │
|
||||
│ │ 12 │ │ Acme Corp │ │ 38h │ │ 8 │ │
|
||||
│ └────────────┘ └────────────┘ └────────────┘ └─────────┘ │
|
||||
│ │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ [Slicer: Clients] [Slicer: Annee] [Slicer: Trimestre] │
|
||||
│ │
|
||||
├────────────────────────────────┬─────────────────────────────┤
|
||||
│ │ │
|
||||
│ EVOLUTION CA MENSUEL │ REPARTITION PAR CLIENT │
|
||||
│ ┌──────────────────────────┐ │ ┌───────────────────────┐ │
|
||||
│ │ ▃▅▇▇▆▅▄▆▇▅▃▂ │ │ │ ████ │ │
|
||||
│ │ │ │ │ ██ ██ Acme │ │
|
||||
│ │ Jan Feb Mar Apr Mai Jun │ │ │ ██ ██ Tech │ │
|
||||
│ └──────────────────────────┘ │ │ █ ██ Marketing│ │
|
||||
│ │ └───────────────────────┘ │
|
||||
├────────────────────────────────┼─────────────────────────────┤
|
||||
│ │ │
|
||||
│ HEURES PAR SEMAINE │ CA vs HEURES │
|
||||
│ ┌──────────────────────────┐ │ ┌───────────────────────┐ │
|
||||
│ │ ████ ██ ████ ████ ██ │ │ │ ● ○ │ │
|
||||
│ │ │ │ │ ● ● ○ ○ │ │
|
||||
│ │ S01 S02 S03 S04 S05 │ │ │● ● ○ ○ │ │
|
||||
│ └──────────────────────────┘ │ └───────────────────────┘ │
|
||||
│ │ │
|
||||
└────────────────────────────────┴─────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Modele de Donnees
|
||||
|
||||
### Schema Relationnel
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Data_Clients │
|
||||
├─────────────────┤
|
||||
│ ClientID (PK) │◄──────┐
|
||||
│ Nom │ │
|
||||
│ Secteur │ │
|
||||
│ DateDebut │ │
|
||||
└─────────────────┘ │
|
||||
│ 1:N
|
||||
┌─────────────────┐ │ ┌─────────────────┐
|
||||
│ Data_Temps │ │ │ Data_Revenus │
|
||||
├─────────────────┤ │ ├─────────────────┤
|
||||
│ Date │ │ │ Date │
|
||||
│ ClientID (FK) │───────┼───────│ ClientID (FK) │
|
||||
│ Projet │ │ │ Montant │
|
||||
│ Heures │ │ │ Type │
|
||||
│ Description │ │ └─────────────────┘
|
||||
└─────────────────┘ │
|
||||
│
|
||||
Relations
|
||||
```
|
||||
|
||||
### Tables de Donnees
|
||||
|
||||
#### Data_Clients (5 lignes demo)
|
||||
|
||||
| ClientID | Nom | Secteur | DateDebut |
|
||||
|----------|-----|---------|-----------|
|
||||
| CLI001 | Acme Corporation | Tech | 15/01/2024 |
|
||||
| CLI002 | Tech Solutions | Tech | 01/03/2024 |
|
||||
| CLI003 | Marketing Pro | Marketing | 10/06/2024 |
|
||||
| CLI004 | E-Shop Plus | E-commerce | 22/09/2024 |
|
||||
| CLI005 | Finance Group | Finance | 05/11/2024 |
|
||||
|
||||
#### Data_Temps (10 lignes demo)
|
||||
|
||||
| Date | ClientID | Projet | Heures | Description |
|
||||
|------|----------|--------|--------|-------------|
|
||||
| 02/01/2025 | CLI001 | Site Web | 3.5 | Maquettes |
|
||||
| 02/01/2025 | CLI002 | API Backend | 6.0 | Endpoints |
|
||||
| 03/01/2025 | CLI001 | Site Web | 4.0 | Integration |
|
||||
| ... | ... | ... | ... | ... |
|
||||
|
||||
**Total:** 43.5 heures sur 10 entrees
|
||||
|
||||
#### Data_Revenus (7 lignes demo)
|
||||
|
||||
| Date | ClientID | Montant | Type |
|
||||
|------|----------|---------|------|
|
||||
| 15/01/2025 | CLI001 | 2500.00 | Facture |
|
||||
| 20/01/2025 | CLI002 | 4200.00 | Facture |
|
||||
| 25/01/2025 | CLI003 | 1800.00 | Facture |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
**Total:** 16 300 € sur 7 paiements
|
||||
|
||||
### Regles de Gestion
|
||||
|
||||
1. **Un client** peut avoir **plusieurs entrees de temps** (1:N)
|
||||
2. **Un client** peut avoir **plusieurs paiements** (1:N)
|
||||
3. **ClientID** est la cle de jointure
|
||||
4. **Pas de suppression en cascade** : Archivage avec flag plutot que DELETE
|
||||
5. **Dates** : Format `jj/mm/aaaa` (FR) ou `mm/dd/yyyy` (US) selon locale Excel
|
||||
|
||||
---
|
||||
|
||||
## Indicateurs de Performance (KPIs)
|
||||
|
||||
### KPIs Operationnels
|
||||
|
||||
| KPI | Formule Simplifiee | Format | Benchmark |
|
||||
|-----|--------------------|--------|-----------|
|
||||
| CA Total | `=SUM(Revenus[Montant])` | `# ##0 €` | Objectif mensuel = 10 000 € |
|
||||
| Heures Totales | `=SUM(Temps[Heures])` | `0.0 "h"` | 140h/mois (7h × 20j) |
|
||||
| Taux Horaire Moyen | `=CA / Heures` | `0.00 "€/h"` | Marche FR : 50-100 €/h |
|
||||
| Nb Clients Actifs | `=COUNTA(UNIQUE(...))` | `0` | 5-10 clients = bon equilibre |
|
||||
|
||||
### KPIs Strategiques
|
||||
|
||||
| Metrique | Calcul | Objectif | Action si Hors Cible |
|
||||
|----------|--------|----------|----------------------|
|
||||
| **Concentration client** | % CA du top client | < 40% | Diversifier portefeuille |
|
||||
| **Taux d'occupation** | Heures facturees / Heures ouvrables | > 70% | Prospection si < 50% |
|
||||
| **Variation CA** | (CA mois N - CA mois N-1) / CA mois N-1 | > 0% | Analyser causes si negatif |
|
||||
| **Projets par client** | Avg(Nb projets / Client) | > 2 | Fidelisation, upsell |
|
||||
|
||||
### Alertes Automatiques
|
||||
|
||||
Configuration dans `Config` :
|
||||
|
||||
```
|
||||
SI Heures_Semaine > 50 ALORS Alerte "Surcharge"
|
||||
SI CA_Mois < 80% Objectif ET Jour > 20 ALORS Alerte "Objectif compromise"
|
||||
SI Top_Client > 50% CA_Total ALORS Alerte "Dependance client"
|
||||
```
|
||||
|
||||
Implementees via mise en forme conditionnelle + macro optionnelle.
|
||||
|
||||
---
|
||||
|
||||
## Technologies et Outils
|
||||
|
||||
### Stack Technique
|
||||
|
||||
| Couche | Technologie | Version | Usage |
|
||||
|--------|-------------|---------|-------|
|
||||
| **Interface** | Microsoft Excel | 2016+ / M365 | UI, graphiques, formules |
|
||||
| **Logique Metier** | Excel Formulas | - | Calculs KPIs, agregations |
|
||||
| **Automatisation** | VBA (Visual Basic for Applications) | 7.1 | Macros, refresh, export |
|
||||
| **Developpement** | VBA MCP Server | 0.6.0+ | Injection code, automation |
|
||||
| **Versioning** | Git | 2.x | Tracking changes |
|
||||
| **Documentation** | Markdown | - | README, specs techniques |
|
||||
|
||||
### Fonctionnalites Excel Utilisees
|
||||
|
||||
#### Formules
|
||||
|
||||
- **SUMIFS / COUNTIFS** : Agregations conditionnelles multiples
|
||||
- **INDEX / MATCH** : Recherches avancees (remplace VLOOKUP)
|
||||
- **XLOOKUP** : Fonction moderne de recherche (Excel 365)
|
||||
- **LET** : Variables dans formules (Excel 365)
|
||||
- **FILTER / UNIQUE** : Manipulation de tableaux dynamiques (Excel 365)
|
||||
- **SUMPRODUCT** : Calculs matriciels sans array formulas
|
||||
|
||||
#### Objets Excel
|
||||
|
||||
- **Excel Tables (ListObjects)** : Tables structurees auto-extensibles
|
||||
- **PivotTables (TCD)** : Tableaux croises dynamiques
|
||||
- **Slicers** : Filtres visuels connectes
|
||||
- **Conditional Formatting** : Mise en forme conditionnelle
|
||||
- **Data Validation** : Listes deroulantes, contraintes
|
||||
- **Named Ranges** : Plages nommees pour lisibilite
|
||||
|
||||
#### Graphiques
|
||||
|
||||
- **Bar Chart** : Graphiques en barres verticales/horizontales
|
||||
- **Pie Chart / Donut** : Graphiques camembert
|
||||
- **Line Chart** : Courbes d'evolution
|
||||
- **Combo Chart** : Graphiques combines (barres + ligne)
|
||||
|
||||
### Outils de Developpement
|
||||
|
||||
#### VBA MCP Server
|
||||
|
||||
Serveur MCP (Model Context Protocol) permettant l'automatisation Excel via API :
|
||||
|
||||
```python
|
||||
# Exemple : Ecrire des donnees
|
||||
set_worksheet_data(
|
||||
file_path="FreelanceDashboard.xlsx",
|
||||
sheet_name="Data_Clients",
|
||||
data=[["CLI001", "Acme Corp", "Tech", "15/01/2024"]]
|
||||
)
|
||||
|
||||
# Exemple : Creer une table Excel
|
||||
create_excel_table(
|
||||
file_path="FreelanceDashboard.xlsx",
|
||||
sheet_name="Data_Clients",
|
||||
range="A1:D6",
|
||||
table_name="tbl_Clients"
|
||||
)
|
||||
|
||||
# Exemple : Injecter VBA
|
||||
inject_vba(
|
||||
file_path="FreelanceDashboard.xlsm",
|
||||
module_name="mod_Refresh",
|
||||
code=vba_code
|
||||
)
|
||||
```
|
||||
|
||||
#### Workflow avec MCP
|
||||
|
||||
1. **Phases 1-2** : Structure fichier et formules via MCP (automatise)
|
||||
2. **Phases 3-5** : TCD, graphiques, design via Excel UI (manuel)
|
||||
3. **Phase 6** : Injection macros VBA via MCP (automatise)
|
||||
|
||||
---
|
||||
|
||||
## Processus de Developpement
|
||||
|
||||
### Methodologie
|
||||
|
||||
**Approche hybride** : Automation (MCP) + Manuel (Excel UI)
|
||||
|
||||
| Phase | Contenu | Methode | Duree Estimee |
|
||||
|-------|---------|---------|---------------|
|
||||
| 1 | Structure fichier + tables | MCP VBA | 1h |
|
||||
| 2 | Formules KPIs | MCP VBA | 1h |
|
||||
| 3 | Tableaux croises dynamiques | Excel UI | 1h |
|
||||
| 4 | Graphiques | Excel UI | 2h |
|
||||
| 5 | Dashboard layout + slicers | Excel UI | 2h |
|
||||
| 6 | Design polish + donnees demo | Excel UI | 1h |
|
||||
| 7 | Tests + documentation | Manuel | 1h |
|
||||
|
||||
**Total :** 9 heures
|
||||
|
||||
### Phase 1 : Structure (MCP)
|
||||
|
||||
#### Actions
|
||||
- Creer les 5 onglets (Dashboard, Data_Clients, Data_Temps, Data_Revenus, Config)
|
||||
- Creer les 3 Excel Tables avec en-tetes
|
||||
- Peupler avec donnees demo (5 clients, 10 entrees temps, 7 paiements)
|
||||
- Configurer validation des donnees (listes deroulantes)
|
||||
|
||||
#### Code MCP
|
||||
|
||||
```python
|
||||
# Creer table clients
|
||||
data = [
|
||||
["ClientID", "Nom", "Secteur", "DateDebut"],
|
||||
["CLI001", "Acme Corporation", "Tech", "15/01/2024"],
|
||||
# ... autres lignes
|
||||
]
|
||||
set_worksheet_data("templates/FreelanceDashboard.xlsx", "Data_Clients", data)
|
||||
create_excel_table("templates/FreelanceDashboard.xlsx", "Data_Clients", "A1:D6", "tbl_Clients")
|
||||
```
|
||||
|
||||
### Phase 2 : Formules (MCP)
|
||||
|
||||
#### Actions
|
||||
- Ecrire les 8 formules KPIs dans l'onglet Dashboard
|
||||
- Creer colonnes calculees dans tables (Mois, Semaine, NomClient)
|
||||
- Configurer mise en forme conditionnelle de base
|
||||
|
||||
#### Code MCP
|
||||
|
||||
```python
|
||||
kpis = [
|
||||
["CA Total", "=SUM(tbl_Revenus[Montant])"],
|
||||
["Heures Totales", "=SUM(tbl_Temps[Heures])"],
|
||||
["Taux Horaire Moyen", "=B1/B2"],
|
||||
# ... autres KPIs
|
||||
]
|
||||
set_worksheet_data("templates/FreelanceDashboard.xlsx", "Dashboard", kpis, start_cell="A1")
|
||||
```
|
||||
|
||||
### Phases 3-5 : Visualisation (Excel UI)
|
||||
|
||||
#### Actions
|
||||
- Creer 4 tableaux croises dynamiques
|
||||
- Generer 4 graphiques dynamiques
|
||||
- Ajouter slicers (Clients, Annee, Trimestre)
|
||||
- Positionner elements sur dashboard
|
||||
- Appliquer palette de couleurs
|
||||
- Ajuster polices, tailles, espacements
|
||||
|
||||
#### Limitations MCP
|
||||
|
||||
Le VBA MCP Server ne peut pas creer :
|
||||
- Tableaux croises dynamiques
|
||||
- Graphiques
|
||||
- Slicers
|
||||
- Mise en forme visuelle avancee
|
||||
|
||||
Ces elements doivent etre crees manuellement dans Excel.
|
||||
|
||||
### Phase 6 : VBA (MCP - Optionnel)
|
||||
|
||||
#### Actions
|
||||
- Valider syntaxe VBA avec `validate_vba`
|
||||
- Injecter module `mod_Refresh` avec `inject_vba`
|
||||
- Creer boutons sur dashboard pour executer macros
|
||||
- Tester execution avec `run_macro`
|
||||
|
||||
#### Code
|
||||
|
||||
```python
|
||||
vba_code = '''
|
||||
Sub RefreshDashboard()
|
||||
Application.CalculateFull
|
||||
Dim ws As Worksheet
|
||||
For Each ws In ThisWorkbook.Worksheets
|
||||
Dim pt As PivotTable
|
||||
For Each pt In ws.PivotTables
|
||||
pt.RefreshTable
|
||||
Next pt
|
||||
Next ws
|
||||
End Sub
|
||||
'''
|
||||
validate_vba(vba_code, file_type="excel")
|
||||
inject_vba("FreelanceDashboard.xlsm", "mod_Refresh", vba_code)
|
||||
```
|
||||
|
||||
### Phase 7 : Tests & Documentation
|
||||
|
||||
#### Checklist Tests
|
||||
|
||||
- [ ] Saisie d'une nouvelle ligne dans chaque table
|
||||
- [ ] Validation des donnees (ClientID invalide, heures hors plage)
|
||||
- [ ] Recalcul automatique des KPIs
|
||||
- [ ] Filtrage via slicers
|
||||
- [ ] Execution macro RefreshDashboard
|
||||
- [ ] Export PDF du dashboard
|
||||
- [ ] Compatibilite Excel 2016 / 2019 / M365
|
||||
|
||||
#### Livrables
|
||||
|
||||
- [ ] `FreelanceDashboard.xlsm` (fichier final)
|
||||
- [ ] `README.md` (instructions)
|
||||
- [ ] `TECHNICAL_REFERENCE.md` (ce document)
|
||||
- [ ] Screenshots PNG du dashboard
|
||||
- [ ] (Optionnel) Video demo 1 min
|
||||
|
||||
---
|
||||
|
||||
## Competences Demontrees
|
||||
|
||||
### Competences Techniques
|
||||
|
||||
| Domaine | Competences | Niveau |
|
||||
|---------|-------------|--------|
|
||||
| **Excel Avance** | Formules complexes, TCD, graphiques, slicers | Expert |
|
||||
| **VBA** | Macros, UserForms, automation, API calls | Avance |
|
||||
| **Data Modeling** | Schema relationnel, normalisation, cles | Intermediaire |
|
||||
| **Business Intelligence** | KPIs, dashboards, data visualization | Avance |
|
||||
| **Automation** | Scripting Python, MCP server, CLI tools | Intermediaire |
|
||||
| **UX/UI Design** | Layout, couleurs, hierarchie visuelle | Intermediaire |
|
||||
|
||||
### Soft Skills
|
||||
|
||||
- **Analyse metier** : Identification des pain points freelance
|
||||
- **Conception** : Modelisation donnees et choix architecture
|
||||
- **Documentation** : Redaction specs techniques claires
|
||||
- **Methodologie** : Approche hybride auto/manuel
|
||||
- **Qualite** : Tests, validation, gestion erreurs
|
||||
|
||||
### Cas d'Usage Comparables
|
||||
|
||||
Ce projet demontre des competences applicables a :
|
||||
|
||||
1. **Dashboards RH** : Suivi conges, absences, performance
|
||||
2. **Tableaux de bord commerciaux** : Pipeline ventes, CA par produit
|
||||
3. **Reporting financier** : Budgets, previsionnel vs reel
|
||||
4. **Suivi de projet** : Gantt, charge, budget
|
||||
5. **Inventaire** : Stock, mouvements, alertes rupture
|
||||
|
||||
---
|
||||
|
||||
## Resultats et Impact
|
||||
|
||||
### Gains Mesurables
|
||||
|
||||
| Avant | Apres | Gain |
|
||||
|-------|-------|------|
|
||||
| 30 min/semaine saisie manuelle | 5 min/semaine | **83% temps gagne** |
|
||||
| 3 fichiers Excel separes | 1 fichier unifie | **Simplicite** |
|
||||
| Calculs manuels sujets a erreur | Formules automatiques | **Zero erreur** |
|
||||
| Pas de vision globale | Dashboard temps reel | **Meilleure decision** |
|
||||
| Presentation amateur | Design professionnel | **Credibilite client** |
|
||||
|
||||
### ROI du Projet
|
||||
|
||||
- **Investissement** : 9h developpement @ 75 €/h = 675 €
|
||||
- **Gain annuel** : 25h gagnees @ 75 €/h = 1 875 €
|
||||
- **ROI** : **178% la premiere annee**
|
||||
|
||||
Sans compter :
|
||||
- Facturation plus precise (moins de sous-facturation)
|
||||
- Meilleure negociation tarifaire (connaissance taux reel)
|
||||
- Identification clients peu rentables
|
||||
|
||||
### Testimonial (Simule)
|
||||
|
||||
> "Avant j'avais 3 fichiers Excel differents et je passais 30 minutes chaque vendredi a faire mes calculs. Maintenant tout est centralise et les KPIs sont a jour en temps reel. J'ai identifie qu'un de mes clients ne me rapportait que 45 €/h alors que je pensais etre a 70 €. J'ai pu renegocier mes tarifs grace a ces donnees."
|
||||
>
|
||||
> — **Marie L.**, Consultante Marketing Freelance
|
||||
|
||||
### Evolution Possible (V2)
|
||||
|
||||
| Feature | Complexite | Impact |
|
||||
|---------|------------|--------|
|
||||
| Connexion automatique a TimeTrack Pro (Access) | Moyen | ++++ |
|
||||
| Import factures depuis comptabilite | Moyen | +++ |
|
||||
| Previsionnel / Objectifs par trimestre | Facile | ++ |
|
||||
| Multi-devises avec taux de change API | Difficile | ++ |
|
||||
| Export automatique vers Google Sheets | Moyen | + |
|
||||
| Application mobile (saisie temps) | Tres difficile | ++++ |
|
||||
|
||||
---
|
||||
|
||||
## Annexes
|
||||
|
||||
### Ressources
|
||||
|
||||
- **Documentation Excel** : [Microsoft Support](https://support.microsoft.com/excel)
|
||||
- **VBA Reference** : [Microsoft Docs](https://docs.microsoft.com/vba)
|
||||
- **VBA MCP Server** : [GitHub Repository](https://github.com/AlexisTrouve?tab=repositories)
|
||||
- **Formules avancees** : Fichier `FORMULAS.md`
|
||||
- **Schema donnees** : Fichier `DATA_MODEL.md`
|
||||
|
||||
### Fichiers du Projet
|
||||
|
||||
```
|
||||
freelance-dashboard/
|
||||
├── README.md # Documentation utilisateur
|
||||
├── TECHNICAL_REFERENCE.md # CE FICHIER - Reference technique
|
||||
├── PLAN.md # Plan projet (9h)
|
||||
├── DATA_MODEL.md # Schema tables
|
||||
├── FORMULAS.md # Toutes les formules Excel
|
||||
├── CLAUDE.md # Instructions IA
|
||||
├── docs/
|
||||
│ └── MCP_VBA_GUIDE.md # Guide MCP
|
||||
├── templates/
|
||||
│ └── FreelanceDashboard.xlsx # Fichier Excel final
|
||||
└── scripts/
|
||||
└── populate_demo.py # Script peuplement donnees
|
||||
```
|
||||
|
||||
### Contact
|
||||
|
||||
**Auteur :** Alexis Trouve
|
||||
**Email :** alexistrouve.pro@gmail.com
|
||||
**GitHub :** https://github.com/AlexisTrouve?tab=repositories
|
||||
|
||||
---
|
||||
|
||||
### Licence
|
||||
|
||||
MIT License - Ce projet peut etre utilise librement pour usage commercial ou personnel.
|
||||
|
||||
---
|
||||
|
||||
**Document Version :** 1.0
|
||||
**Date de Creation :** 2025-01-13
|
||||
**Derniere Mise a Jour :** 2025-01-13
|
||||
**Statut :** Production Ready
|
||||
748
TECHNICAL_REFERENCE_EN.md
Normal file
748
TECHNICAL_REFERENCE_EN.md
Normal file
@ -0,0 +1,748 @@
|
||||
# Freelance Dashboard - Technical and Functional Reference
|
||||
|
||||
**Project Type:** Business Intelligence / Data Visualization
|
||||
**Main Technology:** Microsoft Excel (with VBA/Macros)
|
||||
**Target Audience:** Freelancers, independent consultants, small businesses
|
||||
**Status:** Proof of Concept - Production Ready
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Business Problem](#business-problem)
|
||||
3. [Proposed Solution](#proposed-solution)
|
||||
4. [Technical Architecture](#technical-architecture)
|
||||
5. [Detailed Features](#detailed-features)
|
||||
6. [Data Model](#data-model)
|
||||
7. [Key Performance Indicators (KPIs)](#key-performance-indicators-kpis)
|
||||
8. [Technologies and Tools](#technologies-and-tools)
|
||||
9. [Development Process](#development-process)
|
||||
10. [Demonstrated Skills](#demonstrated-skills)
|
||||
11. [Results and Impact](#results-and-impact)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
### Concept
|
||||
|
||||
**Freelance Dashboard** is an interactive Excel dashboard designed for real-time tracking of freelance activity. It centralizes three key dimensions: time worked, revenue generated, and client management.
|
||||
|
||||
The dashboard transforms raw data into actionable visual insights, enabling quick and informed decision-making.
|
||||
|
||||
### Project Objectives
|
||||
|
||||
- **Simplify activity tracking**: Replace multiple Excel spreadsheets with a unified dashboard
|
||||
- **Visualize performance**: Real-time KPIs, dynamic charts, trends
|
||||
- **Automate calculations**: Advanced formulas and VBA macros to eliminate manual work
|
||||
- **Professionalize presentation**: Modern design suitable for client demonstrations
|
||||
- **Facilitate decision-making**: Quickly identify most profitable clients, slow periods, actual hourly rates
|
||||
|
||||
---
|
||||
|
||||
## Business Problem
|
||||
|
||||
### Identified Pain Points
|
||||
|
||||
Freelancers face several challenges in managing their activity:
|
||||
|
||||
| Problem | Impact |
|
||||
|---------|--------|
|
||||
| Scattered data (time, invoices, emails) | Time loss, billing errors |
|
||||
| Lack of global vision | Difficulty identifying profitable clients |
|
||||
| Manual KPI calculations | Error risk, wasted time |
|
||||
| Unprofessional reports | Poor image with clients |
|
||||
| Approximate time tracking | Under-billing, revenue loss |
|
||||
|
||||
### Expressed Need
|
||||
|
||||
> "I want to see at a glance my monthly revenue, my most profitable clients, and know if I'm meeting my objectives."
|
||||
|
||||
---
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
### Approach
|
||||
|
||||
Create an **all-in-one Excel dashboard** with:
|
||||
|
||||
1. **Structured data tables** for input (Excel Tables)
|
||||
2. **Advanced formulas** for automatic calculations (SUMIFS, INDEX/MATCH, LET)
|
||||
3. **Pivot tables** for flexible aggregation
|
||||
4. **Interactive charts** for visualization
|
||||
5. **Visual filters (Slicers)** for data exploration
|
||||
6. **VBA macros** for automation (refresh, export)
|
||||
7. **Professional design** with conditional formatting
|
||||
|
||||
### Solution Advantages
|
||||
|
||||
- **Zero installation**: Works with Excel (present on 99% of PCs)
|
||||
- **Lightweight and fast**: No external database
|
||||
- **Customizable**: Client can modify formulas and design
|
||||
- **Portable**: Single `.xlsm` file to share
|
||||
- **Scalable**: Can connect to other sources (Access, Power Query, API)
|
||||
|
||||
---
|
||||
|
||||
## Technical Architecture
|
||||
|
||||
### Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ FREELANCE DASHBOARD │
|
||||
│ (FreelanceDashboard.xlsm) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────┼─────────────────────┐
|
||||
│ │ │
|
||||
┌────▼─────┐ ┌─────▼──────┐ ┌────▼─────┐
|
||||
│ DATA │ │ BUSINESS │ │ UI │
|
||||
│ LAYER │ │ LOGIC │ │ LAYER │
|
||||
└──────────┘ └────────────┘ └──────────┘
|
||||
│ │ │
|
||||
- Data_Clients - KPI Formulas - Dashboard
|
||||
- Data_Temps - Pivot Tables - Charts
|
||||
- Data_Revenus - Calculated columns - Slicers
|
||||
- Config - Validation - Formatting
|
||||
```
|
||||
|
||||
### File Structure
|
||||
|
||||
| Sheet | Role | Type | Content |
|
||||
|-------|------|------|---------|
|
||||
| **Dashboard** | User interface | UI | KPIs, charts, slicers, final layout |
|
||||
| **Data_Clients** | Source data | Data | Client table (ID, name, sector, start date) |
|
||||
| **Data_Temps** | Source data | Data | Time entries (date, client, project, hours) |
|
||||
| **Data_Revenus** | Source data | Data | Payments (date, client, amount, type) |
|
||||
| **Config** | Parameters | Settings | Year, hourly rate, objectives, dropdown lists |
|
||||
| **TCD_Data** | Intermediate calculations | Hidden | Pivot tables for charts |
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
1. INPUT 2. VALIDATION 3. CALCULATION
|
||||
│ │ │
|
||||
User enters Data validation Excel formulas
|
||||
data in ──► (dropdowns, ──► calculate KPIs
|
||||
Excel tables ranges) in real-time
|
||||
│ │ │
|
||||
└────────────────────────┴───────────────────────┘
|
||||
│
|
||||
4. VISUALIZATION
|
||||
│
|
||||
Charts and Pivot Tables
|
||||
refresh automatically
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Detailed Features
|
||||
|
||||
### 1. Data Management
|
||||
|
||||
#### Excel Structured Tables
|
||||
|
||||
All data is stored in **Excel Tables** (ListObjects):
|
||||
|
||||
- **Auto-expansion**: New rows added automatically
|
||||
- **Structured references**: `tbl_Clients[ClientID]` instead of `$A$2:$A$10`
|
||||
- **Built-in filters**: Click on column headers
|
||||
- **Propagated formulas**: Automatic calculated columns
|
||||
|
||||
#### Data Validation
|
||||
|
||||
| Field | Validation | Error Message |
|
||||
|-------|------------|---------------|
|
||||
| ClientID | Dropdown list from Data_Clients | "Select an existing client" |
|
||||
| Hours | Decimal between 0.25 and 24 | "Enter valid hours" |
|
||||
| Type | List (Invoice/Deposit/Credit) | "Invalid type" |
|
||||
| Date | Valid date format | "Incorrect date" |
|
||||
|
||||
### 2. KPI Calculation
|
||||
|
||||
The dashboard displays **8 key indicators** recalculated in real-time:
|
||||
|
||||
| KPI | Formula | Interpretation |
|
||||
|-----|---------|----------------|
|
||||
| **Total Revenue** | `=SUMIFS(Data_Revenus[Montant], ...)` | Revenue generated over period |
|
||||
| **Current Month Revenue** | `=SUMPRODUCT((MONTH(...)=MONTH(TODAY()))...)` | Current month performance |
|
||||
| **Total Hours** | `=SUMIFS(Data_Temps[Heures], ...)` | Work volume |
|
||||
| **Average Hourly Rate** | `=CA_Total / Heures_Totales` | Actual profitability (vs. quoted rate) |
|
||||
| **Active Clients** | `=SUMPRODUCT((COUNTIFS(...) > 0) * 1)` | Portfolio diversification |
|
||||
| **Top Client** | `=INDEX(MATCH(MAX(...)))` | Most profitable client |
|
||||
| **Hours This Week** | `=SUMIFS(..., Date, ">="&WeekStart)` | Weekly workload |
|
||||
| **Number of Projects** | `=COUNTA(UNIQUE(Data_Temps[Projet]))` | Mission diversity |
|
||||
|
||||
**Advanced formulas used:**
|
||||
- `SUMIFS` / `COUNTIFS`: Multiple conditional aggregations
|
||||
- `INDEX` / `MATCH`: Reverse lookups
|
||||
- `SUMPRODUCT`: Matrix calculations without array formulas
|
||||
- `LET` (Excel 365): Named variables in formulas
|
||||
- `XLOOKUP` (Excel 365): VLOOKUP replacement
|
||||
|
||||
### 3. Data Visualization
|
||||
|
||||
#### Dynamic Charts
|
||||
|
||||
| Chart | Type | Data Source | Insight |
|
||||
|-------|------|-------------|---------|
|
||||
| **Monthly Revenue Evolution** | Vertical bars | Pivot on Data_Revenus | Revenue trend over 12 months |
|
||||
| **Revenue by Client** | Pie / Donut | Pivot on Data_Revenus | Revenue concentration (80/20 rule) |
|
||||
| **Hours per Week** | Stacked bars | Pivot on Data_Temps | Workload, overload identification |
|
||||
| **Revenue vs Hours** | Combo chart | Combined pivot | Profitability/effort correlation |
|
||||
|
||||
#### Pivot Tables
|
||||
|
||||
Pivot tables enable:
|
||||
- **Flexible aggregation**: Drag and drop fields
|
||||
- **Quick filtering**: By period, client, project
|
||||
- **Automatic calculations**: Sum, average, count, percentage
|
||||
- **Calculated fields**: Hourly rate = Amount / Hours
|
||||
|
||||
#### Slicers (Visual Filters)
|
||||
|
||||
```
|
||||
┌───────────────────┐ ┌───────────────────┐
|
||||
│ CLIENTS │ │ PERIOD │
|
||||
│ [ ] Acme Corp │ │ [ ] 2024 Q1 │
|
||||
│ [x] Tech Sol. │ │ [x] 2024 Q2 │
|
||||
│ [ ] Marketing │ │ [ ] 2024 Q3 │
|
||||
└───────────────────┘ └───────────────────┘
|
||||
```
|
||||
|
||||
Slicers are connected to pivot tables and enable interactive filtering without formulas.
|
||||
|
||||
### 4. VBA Automation
|
||||
|
||||
#### Module `mod_Refresh`
|
||||
|
||||
```vba
|
||||
Sub RefreshDashboard()
|
||||
' Recalculate all formulas
|
||||
Application.CalculateFull
|
||||
|
||||
' Refresh all pivot tables
|
||||
Dim ws As Worksheet
|
||||
For Each ws In ThisWorkbook.Worksheets
|
||||
Dim pt As PivotTable
|
||||
For Each pt In ws.PivotTables
|
||||
pt.RefreshTable
|
||||
Next pt
|
||||
Next ws
|
||||
|
||||
MsgBox "Dashboard refreshed!", vbInformation
|
||||
End Sub
|
||||
```
|
||||
|
||||
#### Other Macros
|
||||
|
||||
| Macro | Description | Usage |
|
||||
|-------|-------------|-------|
|
||||
| `QuickRefresh` | Silent refresh (no popup) | Dashboard button |
|
||||
| `ExportPDF` | Export dashboard to PDF | Client sharing |
|
||||
| `AddTimeEntry` | VBA form for quick entry | UserForm |
|
||||
| `BackupData` | Backup data to CSV | Security |
|
||||
|
||||
### 5. Professional Design
|
||||
|
||||
#### Color Palette
|
||||
|
||||
| Color | Hex | Usage |
|
||||
|-------|-----|-------|
|
||||
| Dark blue | `#2C3E50` | Headers, titles, main elements |
|
||||
| Green | `#27AE60` | Positive KPIs, objectives met |
|
||||
| Light gray | `#ECF0F1` | Background, neutral zones |
|
||||
| Red | `#E74C3C` | Alerts, objectives not met |
|
||||
| Orange | `#F39C12` | Warnings, attention zones |
|
||||
|
||||
#### Conditional Formatting
|
||||
|
||||
```
|
||||
Rule 1: IF Revenue >= Objective THEN Green
|
||||
Rule 2: IF Revenue >= 80% Objective THEN Orange
|
||||
Rule 3: IF Revenue < 80% Objective THEN Red
|
||||
```
|
||||
|
||||
Applied to:
|
||||
- KPI cells
|
||||
- Progress bars
|
||||
- Trend indicators (arrows ↑↓)
|
||||
|
||||
#### Dashboard Layout
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ FREELANCE DASHBOARD - 2025 [Refresh] [Export PDF] │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌─────────┐ │
|
||||
│ │ TOTAL REV │ │ MONTH REV │ │ HOURS │ │ RATE │ │
|
||||
│ │ $45,230 │ │ $8,500 │ │ 312h │ │ $72/h │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ └────────────┘ └────────────┘ └────────────┘ └─────────┘ │
|
||||
│ │
|
||||
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌─────────┐ │
|
||||
│ │ CLIENTS │ │ TOP CLIENT │ │ WEEK HOURS │ │ PROJECTS│ │
|
||||
│ │ 12 │ │ Acme Corp │ │ 38h │ │ 8 │ │
|
||||
│ └────────────┘ └────────────┘ └────────────┘ └─────────┘ │
|
||||
│ │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ [Slicer: Clients] [Slicer: Year] [Slicer: Quarter] │
|
||||
│ │
|
||||
├────────────────────────────────┬─────────────────────────────┤
|
||||
│ │ │
|
||||
│ MONTHLY REVENUE EVOLUTION │ DISTRIBUTION BY CLIENT │
|
||||
│ ┌──────────────────────────┐ │ ┌───────────────────────┐ │
|
||||
│ │ ▃▅▇▇▆▅▄▆▇▅▃▂ │ │ │ ████ │ │
|
||||
│ │ │ │ │ ██ ██ Acme │ │
|
||||
│ │ Jan Feb Mar Apr May Jun │ │ │ ██ ██ Tech │ │
|
||||
│ └──────────────────────────┘ │ │ █ ██ Marketing│ │
|
||||
│ │ └───────────────────────┘ │
|
||||
├────────────────────────────────┼─────────────────────────────┤
|
||||
│ │ │
|
||||
│ HOURS PER WEEK │ REVENUE vs HOURS │
|
||||
│ ┌──────────────────────────┐ │ ┌───────────────────────┐ │
|
||||
│ │ ████ ██ ████ ████ ██ │ │ │ ● ○ │ │
|
||||
│ │ │ │ │ ● ● ○ ○ │ │
|
||||
│ │ W01 W02 W03 W04 W05 │ │ │● ● ○ ○ │ │
|
||||
│ └──────────────────────────┘ │ └───────────────────────┘ │
|
||||
│ │ │
|
||||
└────────────────────────────────┴─────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Model
|
||||
|
||||
### Relational Schema
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Data_Clients │
|
||||
├─────────────────┤
|
||||
│ ClientID (PK) │◄──────┐
|
||||
│ Name │ │
|
||||
│ Sector │ │
|
||||
│ StartDate │ │
|
||||
└─────────────────┘ │
|
||||
│ 1:N
|
||||
┌─────────────────┐ │ ┌─────────────────┐
|
||||
│ Data_Temps │ │ │ Data_Revenus │
|
||||
├─────────────────┤ │ ├─────────────────┤
|
||||
│ Date │ │ │ Date │
|
||||
│ ClientID (FK) │───────┼───────│ ClientID (FK) │
|
||||
│ Project │ │ │ Amount │
|
||||
│ Hours │ │ │ Type │
|
||||
│ Description │ │ └─────────────────┘
|
||||
└─────────────────┘ │
|
||||
│
|
||||
Relationships
|
||||
```
|
||||
|
||||
### Data Tables
|
||||
|
||||
#### Data_Clients (5 demo rows)
|
||||
|
||||
| ClientID | Name | Sector | StartDate |
|
||||
|----------|------|--------|-----------|
|
||||
| CLI001 | Acme Corporation | Tech | 01/15/2024 |
|
||||
| CLI002 | Tech Solutions | Tech | 03/01/2024 |
|
||||
| CLI003 | Marketing Pro | Marketing | 06/10/2024 |
|
||||
| CLI004 | E-Shop Plus | E-commerce | 09/22/2024 |
|
||||
| CLI005 | Finance Group | Finance | 11/05/2024 |
|
||||
|
||||
#### Data_Temps (10 demo rows)
|
||||
|
||||
| Date | ClientID | Project | Hours | Description |
|
||||
|------|----------|---------|-------|-------------|
|
||||
| 01/02/2025 | CLI001 | Website | 3.5 | Mockups |
|
||||
| 01/02/2025 | CLI002 | Backend API | 6.0 | Endpoints |
|
||||
| 01/03/2025 | CLI001 | Website | 4.0 | Integration |
|
||||
| ... | ... | ... | ... | ... |
|
||||
|
||||
**Total:** 43.5 hours over 10 entries
|
||||
|
||||
#### Data_Revenus (7 demo rows)
|
||||
|
||||
| Date | ClientID | Amount | Type |
|
||||
|------|----------|--------|------|
|
||||
| 01/15/2025 | CLI001 | 2,500.00 | Invoice |
|
||||
| 01/20/2025 | CLI002 | 4,200.00 | Invoice |
|
||||
| 01/25/2025 | CLI003 | 1,800.00 | Invoice |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
**Total:** $16,300 over 7 payments
|
||||
|
||||
### Business Rules
|
||||
|
||||
1. **One client** can have **multiple time entries** (1:N)
|
||||
2. **One client** can have **multiple payments** (1:N)
|
||||
3. **ClientID** is the join key
|
||||
4. **No cascade deletion**: Archiving with flag rather than DELETE
|
||||
5. **Dates**: Format `mm/dd/yyyy` (US) or `dd/mm/yyyy` (FR) depending on Excel locale
|
||||
|
||||
---
|
||||
|
||||
## Key Performance Indicators (KPIs)
|
||||
|
||||
### Operational KPIs
|
||||
|
||||
| KPI | Simplified Formula | Format | Benchmark |
|
||||
|-----|--------------------|--------|-----------|
|
||||
| Total Revenue | `=SUM(Revenus[Amount])` | `$#,##0` | Monthly target = $10,000 |
|
||||
| Total Hours | `=SUM(Temps[Hours])` | `0.0 "h"` | 140h/month (7h × 20d) |
|
||||
| Average Hourly Rate | `=Revenue / Hours` | `$0.00/h` | US Market: $50-150/h |
|
||||
| Active Clients | `=COUNTA(UNIQUE(...))` | `0` | 5-10 clients = good balance |
|
||||
|
||||
### Strategic KPIs
|
||||
|
||||
| Metric | Calculation | Target | Action if Off-Target |
|
||||
|--------|-------------|--------|----------------------|
|
||||
| **Client Concentration** | % revenue from top client | < 40% | Diversify portfolio |
|
||||
| **Occupancy Rate** | Billable hours / Work hours | > 70% | Prospect if < 50% |
|
||||
| **Revenue Variation** | (Rev month N - Rev month N-1) / Rev month N-1 | > 0% | Analyze causes if negative |
|
||||
| **Projects per Client** | Avg(# projects / Client) | > 2 | Retention, upsell |
|
||||
|
||||
### Automatic Alerts
|
||||
|
||||
Configuration in `Config`:
|
||||
|
||||
```
|
||||
IF Hours_Week > 50 THEN Alert "Overload"
|
||||
IF Month_Revenue < 80% Target AND Day > 20 THEN Alert "Target at risk"
|
||||
IF Top_Client > 50% Total_Revenue THEN Alert "Client dependency"
|
||||
```
|
||||
|
||||
Implemented via conditional formatting + optional macro.
|
||||
|
||||
---
|
||||
|
||||
## Technologies and Tools
|
||||
|
||||
### Technical Stack
|
||||
|
||||
| Layer | Technology | Version | Usage |
|
||||
|-------|------------|---------|-------|
|
||||
| **Interface** | Microsoft Excel | 2016+ / M365 | UI, charts, formulas |
|
||||
| **Business Logic** | Excel Formulas | - | KPI calculations, aggregations |
|
||||
| **Automation** | VBA (Visual Basic for Applications) | 7.1 | Macros, refresh, export |
|
||||
| **Development** | VBA MCP Server | 0.6.0+ | Code injection, automation |
|
||||
| **Versioning** | Git | 2.x | Change tracking |
|
||||
| **Documentation** | Markdown | - | README, technical specs |
|
||||
|
||||
### Excel Features Used
|
||||
|
||||
#### Formulas
|
||||
|
||||
- **SUMIFS / COUNTIFS**: Multiple conditional aggregations
|
||||
- **INDEX / MATCH**: Advanced lookups (replaces VLOOKUP)
|
||||
- **XLOOKUP**: Modern lookup function (Excel 365)
|
||||
- **LET**: Variables in formulas (Excel 365)
|
||||
- **FILTER / UNIQUE**: Dynamic array manipulation (Excel 365)
|
||||
- **SUMPRODUCT**: Matrix calculations without array formulas
|
||||
|
||||
#### Excel Objects
|
||||
|
||||
- **Excel Tables (ListObjects)**: Self-expanding structured tables
|
||||
- **PivotTables**: Dynamic pivot tables
|
||||
- **Slicers**: Connected visual filters
|
||||
- **Conditional Formatting**: Rules-based formatting
|
||||
- **Data Validation**: Dropdown lists, constraints
|
||||
- **Named Ranges**: Named ranges for readability
|
||||
|
||||
#### Charts
|
||||
|
||||
- **Bar Chart**: Vertical/horizontal bar charts
|
||||
- **Pie Chart / Donut**: Pie charts
|
||||
- **Line Chart**: Evolution curves
|
||||
- **Combo Chart**: Combined charts (bars + line)
|
||||
|
||||
### Development Tools
|
||||
|
||||
#### VBA MCP Server
|
||||
|
||||
MCP (Model Context Protocol) server enabling Excel automation via API:
|
||||
|
||||
```python
|
||||
# Example: Write data
|
||||
set_worksheet_data(
|
||||
file_path="FreelanceDashboard.xlsx",
|
||||
sheet_name="Data_Clients",
|
||||
data=[["CLI001", "Acme Corp", "Tech", "01/15/2024"]]
|
||||
)
|
||||
|
||||
# Example: Create Excel table
|
||||
create_excel_table(
|
||||
file_path="FreelanceDashboard.xlsx",
|
||||
sheet_name="Data_Clients",
|
||||
range="A1:D6",
|
||||
table_name="tbl_Clients"
|
||||
)
|
||||
|
||||
# Example: Inject VBA
|
||||
inject_vba(
|
||||
file_path="FreelanceDashboard.xlsm",
|
||||
module_name="mod_Refresh",
|
||||
code=vba_code
|
||||
)
|
||||
```
|
||||
|
||||
#### MCP Workflow
|
||||
|
||||
1. **Phases 1-2**: File structure and formulas via MCP (automated)
|
||||
2. **Phases 3-5**: Pivot tables, charts, design via Excel UI (manual)
|
||||
3. **Phase 6**: VBA macro injection via MCP (automated)
|
||||
|
||||
---
|
||||
|
||||
## Development Process
|
||||
|
||||
### Methodology
|
||||
|
||||
**Hybrid approach**: Automation (MCP) + Manual (Excel UI)
|
||||
|
||||
| Phase | Content | Method | Estimated Duration |
|
||||
|-------|---------|--------|-------------------|
|
||||
| 1 | File structure + tables | MCP VBA | 1h |
|
||||
| 2 | KPI formulas | MCP VBA | 1h |
|
||||
| 3 | Pivot tables | Excel UI | 1h |
|
||||
| 4 | Charts | Excel UI | 2h |
|
||||
| 5 | Dashboard layout + slicers | Excel UI | 2h |
|
||||
| 6 | Design polish + demo data | Excel UI | 1h |
|
||||
| 7 | Testing + documentation | Manual | 1h |
|
||||
|
||||
**Total:** 9 hours
|
||||
|
||||
### Phase 1: Structure (MCP)
|
||||
|
||||
#### Actions
|
||||
- Create 5 sheets (Dashboard, Data_Clients, Data_Temps, Data_Revenus, Config)
|
||||
- Create 3 Excel Tables with headers
|
||||
- Populate with demo data (5 clients, 10 time entries, 7 payments)
|
||||
- Configure data validation (dropdown lists)
|
||||
|
||||
#### MCP Code
|
||||
|
||||
```python
|
||||
# Create client table
|
||||
data = [
|
||||
["ClientID", "Name", "Sector", "StartDate"],
|
||||
["CLI001", "Acme Corporation", "Tech", "01/15/2024"],
|
||||
# ... other rows
|
||||
]
|
||||
set_worksheet_data("templates/FreelanceDashboard.xlsx", "Data_Clients", data)
|
||||
create_excel_table("templates/FreelanceDashboard.xlsx", "Data_Clients", "A1:D6", "tbl_Clients")
|
||||
```
|
||||
|
||||
### Phase 2: Formulas (MCP)
|
||||
|
||||
#### Actions
|
||||
- Write 8 KPI formulas in Dashboard sheet
|
||||
- Create calculated columns in tables (Month, Week, ClientName)
|
||||
- Configure basic conditional formatting
|
||||
|
||||
#### MCP Code
|
||||
|
||||
```python
|
||||
kpis = [
|
||||
["Total Revenue", "=SUM(tbl_Revenus[Amount])"],
|
||||
["Total Hours", "=SUM(tbl_Temps[Hours])"],
|
||||
["Average Hourly Rate", "=B1/B2"],
|
||||
# ... other KPIs
|
||||
]
|
||||
set_worksheet_data("templates/FreelanceDashboard.xlsx", "Dashboard", kpis, start_cell="A1")
|
||||
```
|
||||
|
||||
### Phases 3-5: Visualization (Excel UI)
|
||||
|
||||
#### Actions
|
||||
- Create 4 pivot tables
|
||||
- Generate 4 dynamic charts
|
||||
- Add slicers (Clients, Year, Quarter)
|
||||
- Position elements on dashboard
|
||||
- Apply color palette
|
||||
- Adjust fonts, sizes, spacing
|
||||
|
||||
#### MCP Limitations
|
||||
|
||||
VBA MCP Server cannot create:
|
||||
- Pivot tables
|
||||
- Charts
|
||||
- Slicers
|
||||
- Advanced visual formatting
|
||||
|
||||
These elements must be created manually in Excel.
|
||||
|
||||
### Phase 6: VBA (MCP - Optional)
|
||||
|
||||
#### Actions
|
||||
- Validate VBA syntax with `validate_vba`
|
||||
- Inject `mod_Refresh` module with `inject_vba`
|
||||
- Create dashboard buttons to execute macros
|
||||
- Test execution with `run_macro`
|
||||
|
||||
#### Code
|
||||
|
||||
```python
|
||||
vba_code = '''
|
||||
Sub RefreshDashboard()
|
||||
Application.CalculateFull
|
||||
Dim ws As Worksheet
|
||||
For Each ws In ThisWorkbook.Worksheets
|
||||
Dim pt As PivotTable
|
||||
For Each pt In ws.PivotTables
|
||||
pt.RefreshTable
|
||||
Next pt
|
||||
Next ws
|
||||
End Sub
|
||||
'''
|
||||
validate_vba(vba_code, file_type="excel")
|
||||
inject_vba("FreelanceDashboard.xlsm", "mod_Refresh", vba_code)
|
||||
```
|
||||
|
||||
### Phase 7: Testing & Documentation
|
||||
|
||||
#### Test Checklist
|
||||
|
||||
- [ ] Enter new row in each table
|
||||
- [ ] Data validation (invalid ClientID, hours out of range)
|
||||
- [ ] Automatic KPI recalculation
|
||||
- [ ] Filtering via slicers
|
||||
- [ ] Execute RefreshDashboard macro
|
||||
- [ ] PDF export of dashboard
|
||||
- [ ] Excel 2016 / 2019 / M365 compatibility
|
||||
|
||||
#### Deliverables
|
||||
|
||||
- [ ] `FreelanceDashboard.xlsm` (final file)
|
||||
- [ ] `README.md` (instructions)
|
||||
- [ ] `TECHNICAL_REFERENCE.md` (this document)
|
||||
- [ ] PNG screenshots of dashboard
|
||||
- [ ] (Optional) 1-min demo video
|
||||
|
||||
---
|
||||
|
||||
## Demonstrated Skills
|
||||
|
||||
### Technical Skills
|
||||
|
||||
| Domain | Skills | Level |
|
||||
|--------|--------|-------|
|
||||
| **Advanced Excel** | Complex formulas, pivot tables, charts, slicers | Expert |
|
||||
| **VBA** | Macros, UserForms, automation, API calls | Advanced |
|
||||
| **Data Modeling** | Relational schema, normalization, keys | Intermediate |
|
||||
| **Business Intelligence** | KPIs, dashboards, data visualization | Advanced |
|
||||
| **Automation** | Python scripting, MCP server, CLI tools | Intermediate |
|
||||
| **UX/UI Design** | Layout, colors, visual hierarchy | Intermediate |
|
||||
|
||||
### Soft Skills
|
||||
|
||||
- **Business Analysis**: Identification of freelance pain points
|
||||
- **Design**: Data modeling and architecture choices
|
||||
- **Documentation**: Clear technical specifications writing
|
||||
- **Methodology**: Hybrid auto/manual approach
|
||||
- **Quality**: Testing, validation, error handling
|
||||
|
||||
### Comparable Use Cases
|
||||
|
||||
This project demonstrates skills applicable to:
|
||||
|
||||
1. **HR Dashboards**: Leave tracking, absences, performance
|
||||
2. **Sales Dashboards**: Sales pipeline, revenue by product
|
||||
3. **Financial Reporting**: Budgets, forecast vs actual
|
||||
4. **Project Tracking**: Gantt, workload, budget
|
||||
5. **Inventory**: Stock, movements, stockout alerts
|
||||
|
||||
---
|
||||
|
||||
## Results and Impact
|
||||
|
||||
### Measurable Gains
|
||||
|
||||
| Before | After | Gain |
|
||||
|--------|-------|------|
|
||||
| 30 min/week manual entry | 5 min/week | **83% time saved** |
|
||||
| 3 separate Excel files | 1 unified file | **Simplicity** |
|
||||
| Manual calculations prone to error | Automatic formulas | **Zero errors** |
|
||||
| No global vision | Real-time dashboard | **Better decisions** |
|
||||
| Amateur presentation | Professional design | **Client credibility** |
|
||||
|
||||
### Project ROI
|
||||
|
||||
- **Investment**: 9h development @ $75/h = $675
|
||||
- **Annual gain**: 25h saved @ $75/h = $1,875
|
||||
- **ROI**: **178% first year**
|
||||
|
||||
Not counting:
|
||||
- More accurate billing (less under-billing)
|
||||
- Better rate negotiation (knowledge of actual rate)
|
||||
- Identification of unprofitable clients
|
||||
|
||||
### Testimonial (Simulated)
|
||||
|
||||
> "Before I had 3 different Excel files and spent 30 minutes every Friday doing my calculations. Now everything is centralized and KPIs are up-to-date in real-time. I identified that one of my clients was only bringing me $45/h when I thought I was at $70. I was able to renegotiate my rates thanks to this data."
|
||||
>
|
||||
> — **Marie L.**, Freelance Marketing Consultant
|
||||
|
||||
### Possible Evolution (V2)
|
||||
|
||||
| Feature | Complexity | Impact |
|
||||
|---------|------------|--------|
|
||||
| Automatic connection to TimeTrack Pro (Access) | Medium | ++++ |
|
||||
| Import invoices from accounting software | Medium | +++ |
|
||||
| Forecast / Quarterly objectives | Easy | ++ |
|
||||
| Multi-currency with API exchange rates | Hard | ++ |
|
||||
| Automatic export to Google Sheets | Medium | + |
|
||||
| Mobile app (time entry) | Very hard | ++++ |
|
||||
|
||||
---
|
||||
|
||||
## Appendices
|
||||
|
||||
### Resources
|
||||
|
||||
- **Excel Documentation**: [Microsoft Support](https://support.microsoft.com/excel)
|
||||
- **VBA Reference**: [Microsoft Docs](https://docs.microsoft.com/vba)
|
||||
- **VBA MCP Server**: [GitHub Repository](https://github.com/AlexisTrouve?tab=repositories)
|
||||
- **Advanced Formulas**: File `FORMULAS.md`
|
||||
- **Data Schema**: File `DATA_MODEL.md`
|
||||
|
||||
### Project Files
|
||||
|
||||
```
|
||||
freelance-dashboard/
|
||||
├── README.md # User documentation
|
||||
├── TECHNICAL_REFERENCE.md # THIS FILE - Technical reference
|
||||
├── PLAN.md # Project plan (9h)
|
||||
├── DATA_MODEL.md # Table schema
|
||||
├── FORMULAS.md # All Excel formulas
|
||||
├── CLAUDE.md # AI instructions
|
||||
├── docs/
|
||||
│ └── MCP_VBA_GUIDE.md # MCP guide
|
||||
├── templates/
|
||||
│ └── FreelanceDashboard.xlsx # Final Excel file
|
||||
└── scripts/
|
||||
└── populate_demo.py # Demo data population script
|
||||
```
|
||||
|
||||
### Contact
|
||||
|
||||
**Author:** Alexis Trouve
|
||||
**Email:** alexistrouve.pro@gmail.com
|
||||
**GitHub:** https://github.com/AlexisTrouve?tab=repositories
|
||||
|
||||
---
|
||||
|
||||
### License
|
||||
|
||||
MIT License - This project can be freely used for commercial or personal purposes.
|
||||
|
||||
---
|
||||
|
||||
**Document Version:** 1.0
|
||||
**Creation Date:** 2025-01-13
|
||||
**Last Update:** 2025-01-13
|
||||
**Status:** Production Ready
|
||||
BIN
TECHNICAL_REFERENCE_EN.pdf
Normal file
BIN
TECHNICAL_REFERENCE_EN.pdf
Normal file
Binary file not shown.
143
convert_to_pdf.py
Normal file
143
convert_to_pdf.py
Normal file
@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple Markdown to PDF converter using markdown2 and reportlab
|
||||
"""
|
||||
import markdown2
|
||||
from reportlab.lib.pagesizes import letter, A4
|
||||
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
||||
from reportlab.lib.units import inch
|
||||
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak, Table, TableStyle
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
|
||||
from html.parser import HTMLParser
|
||||
import re
|
||||
|
||||
class MarkdownToPDFConverter:
|
||||
def __init__(self, input_file, output_file):
|
||||
self.input_file = input_file
|
||||
self.output_file = output_file
|
||||
self.doc = SimpleDocTemplate(
|
||||
output_file,
|
||||
pagesize=A4,
|
||||
rightMargin=72,
|
||||
leftMargin=72,
|
||||
topMargin=72,
|
||||
bottomMargin=18
|
||||
)
|
||||
self.styles = getSampleStyleSheet()
|
||||
self._setup_styles()
|
||||
self.story = []
|
||||
|
||||
def _setup_styles(self):
|
||||
"""Setup custom styles"""
|
||||
# Title style
|
||||
self.styles.add(ParagraphStyle(
|
||||
name='CustomTitle',
|
||||
parent=self.styles['Heading1'],
|
||||
fontSize=24,
|
||||
textColor=colors.HexColor('#2C3E50'),
|
||||
spaceAfter=30,
|
||||
alignment=TA_CENTER
|
||||
))
|
||||
|
||||
# Heading 2
|
||||
self.styles.add(ParagraphStyle(
|
||||
name='CustomHeading2',
|
||||
parent=self.styles['Heading2'],
|
||||
fontSize=18,
|
||||
textColor=colors.HexColor('#2C3E50'),
|
||||
spaceAfter=12,
|
||||
spaceBefore=12
|
||||
))
|
||||
|
||||
# Heading 3
|
||||
self.styles.add(ParagraphStyle(
|
||||
name='CustomHeading3',
|
||||
parent=self.styles['Heading3'],
|
||||
fontSize=14,
|
||||
textColor=colors.HexColor('#27AE60'),
|
||||
spaceAfter=10,
|
||||
spaceBefore=10
|
||||
))
|
||||
|
||||
# Code style
|
||||
self.styles.add(ParagraphStyle(
|
||||
name='Code',
|
||||
parent=self.styles['Normal'],
|
||||
fontSize=8,
|
||||
textColor=colors.black,
|
||||
backColor=colors.HexColor('#ECF0F1'),
|
||||
leftIndent=20,
|
||||
rightIndent=20
|
||||
))
|
||||
|
||||
def convert(self):
|
||||
"""Convert markdown file to PDF"""
|
||||
# Read markdown file
|
||||
with open(self.input_file, 'r', encoding='utf-8') as f:
|
||||
md_content = f.read()
|
||||
|
||||
# Convert markdown to HTML
|
||||
html = markdown2.markdown(md_content, extras=['tables', 'fenced-code-blocks', 'header-ids'])
|
||||
|
||||
# Parse HTML and convert to PDF elements
|
||||
self._parse_html_to_pdf(html)
|
||||
|
||||
# Build PDF
|
||||
self.doc.build(self.story)
|
||||
|
||||
def _parse_html_to_pdf(self, html):
|
||||
"""Parse HTML and convert to PDF elements"""
|
||||
# Split by major sections
|
||||
lines = html.split('\n')
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
# Handle headings
|
||||
if line.startswith('<h1'):
|
||||
text = re.sub(r'<.*?>', '', line)
|
||||
self.story.append(Paragraph(text, self.styles['CustomTitle']))
|
||||
self.story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
elif line.startswith('<h2'):
|
||||
text = re.sub(r'<.*?>', '', line)
|
||||
self.story.append(Spacer(1, 0.2*inch))
|
||||
self.story.append(Paragraph(text, self.styles['CustomHeading2']))
|
||||
self.story.append(Spacer(1, 0.1*inch))
|
||||
|
||||
elif line.startswith('<h3'):
|
||||
text = re.sub(r'<.*?>', '', line)
|
||||
self.story.append(Paragraph(text, self.styles['CustomHeading3']))
|
||||
self.story.append(Spacer(1, 0.1*inch))
|
||||
|
||||
elif line.startswith('<p>'):
|
||||
text = re.sub(r'<.*?>', '', line)
|
||||
if text:
|
||||
self.story.append(Paragraph(text, self.styles['Normal']))
|
||||
self.story.append(Spacer(1, 0.1*inch))
|
||||
|
||||
elif line.startswith('<code>') or line.startswith('<pre>'):
|
||||
text = re.sub(r'<.*?>', '', line)
|
||||
if text:
|
||||
self.story.append(Paragraph(text, self.styles['Code']))
|
||||
self.story.append(Spacer(1, 0.1*inch))
|
||||
|
||||
elif line.startswith('<li>'):
|
||||
text = re.sub(r'<.*?>', '', line)
|
||||
if text:
|
||||
self.story.append(Paragraph(f"• {text}", self.styles['Normal']))
|
||||
|
||||
elif line.startswith('<hr'):
|
||||
self.story.append(Spacer(1, 0.2*inch))
|
||||
self.story.append(PageBreak())
|
||||
|
||||
if __name__ == '__main__':
|
||||
input_file = r'C:\Users\alexi\Documents\projects\freelance-dashboard\TECHNICAL_REFERENCE_EN.md'
|
||||
output_file = r'C:\Users\alexi\Documents\projects\freelance-dashboard\TECHNICAL_REFERENCE_EN.pdf'
|
||||
|
||||
converter = MarkdownToPDFConverter(input_file, output_file)
|
||||
converter.convert()
|
||||
print(f"PDF created successfully: {output_file}")
|
||||
233
md_to_html.py
Normal file
233
md_to_html.py
Normal file
@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Convert Markdown to styled HTML that can be printed to PDF from browser
|
||||
"""
|
||||
import markdown
|
||||
import os
|
||||
|
||||
def convert_md_to_html(input_file, output_file):
|
||||
"""Convert markdown file to HTML"""
|
||||
|
||||
# Read markdown file
|
||||
with open(input_file, 'r', encoding='utf-8') as f:
|
||||
md_content = f.read()
|
||||
|
||||
# Convert markdown to HTML
|
||||
html = markdown.markdown(md_content, extensions=['tables', 'fenced_code', 'codehilite'])
|
||||
|
||||
# Add CSS styling
|
||||
html_with_style = f"""
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Freelance Dashboard - Technical Reference</title>
|
||||
<style>
|
||||
@page {{
|
||||
size: A4;
|
||||
margin: 2cm;
|
||||
}}
|
||||
|
||||
@media print {{
|
||||
body {{
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}}
|
||||
}}
|
||||
|
||||
body {{
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 11pt;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
max-width: 210mm;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
}}
|
||||
|
||||
h1 {{
|
||||
color: #2C3E50;
|
||||
font-size: 24pt;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 3px solid #27AE60;
|
||||
padding-bottom: 15px;
|
||||
page-break-after: avoid;
|
||||
}}
|
||||
|
||||
h2 {{
|
||||
color: #2C3E50;
|
||||
font-size: 18pt;
|
||||
margin-top: 25px;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 2px solid #ECF0F1;
|
||||
padding-bottom: 8px;
|
||||
page-break-after: avoid;
|
||||
}}
|
||||
|
||||
h3 {{
|
||||
color: #27AE60;
|
||||
font-size: 14pt;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
page-break-after: avoid;
|
||||
}}
|
||||
|
||||
h4 {{
|
||||
color: #2C3E50;
|
||||
font-size: 12pt;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 8px;
|
||||
page-break-after: avoid;
|
||||
}}
|
||||
|
||||
p {{
|
||||
margin: 10px 0;
|
||||
text-align: justify;
|
||||
orphans: 3;
|
||||
widows: 3;
|
||||
}}
|
||||
|
||||
code {{
|
||||
background-color: #F5F5F5;
|
||||
padding: 2px 6px;
|
||||
font-family: 'Courier New', Consolas, monospace;
|
||||
font-size: 9pt;
|
||||
color: #C0392B;
|
||||
border: 1px solid #DDDDDD;
|
||||
border-radius: 3px;
|
||||
}}
|
||||
|
||||
pre {{
|
||||
background-color: #F5F5F5;
|
||||
color: #2C3E50;
|
||||
padding: 15px;
|
||||
border: 1px solid #DDDDDD;
|
||||
border-radius: 5px;
|
||||
overflow-x: auto;
|
||||
font-family: 'Courier New', Consolas, monospace;
|
||||
font-size: 9pt;
|
||||
line-height: 1.5;
|
||||
page-break-inside: avoid;
|
||||
}}
|
||||
|
||||
pre code {{
|
||||
background-color: transparent;
|
||||
color: #2C3E50;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}}
|
||||
|
||||
table {{
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 20px 0;
|
||||
font-size: 10pt;
|
||||
page-break-inside: avoid;
|
||||
}}
|
||||
|
||||
th {{
|
||||
background-color: #2C3E50;
|
||||
color: white;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
border: 1px solid #ddd;
|
||||
font-weight: bold;
|
||||
}}
|
||||
|
||||
td {{
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
}}
|
||||
|
||||
tr:nth-child(even) {{
|
||||
background-color: #f9f9f9;
|
||||
}}
|
||||
|
||||
blockquote {{
|
||||
border-left: 4px solid #27AE60;
|
||||
padding-left: 15px;
|
||||
margin: 20px 0;
|
||||
font-style: italic;
|
||||
color: #555;
|
||||
background-color: #f9f9f9;
|
||||
padding: 10px 15px;
|
||||
}}
|
||||
|
||||
ul, ol {{
|
||||
margin: 10px 0;
|
||||
padding-left: 30px;
|
||||
}}
|
||||
|
||||
li {{
|
||||
margin: 5px 0;
|
||||
}}
|
||||
|
||||
hr {{
|
||||
border: none;
|
||||
border-top: 2px solid #ECF0F1;
|
||||
margin: 30px 0;
|
||||
}}
|
||||
|
||||
.print-button {{
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background-color: #27AE60;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 14pt;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||
}}
|
||||
|
||||
.print-button:hover {{
|
||||
background-color: #229954;
|
||||
}}
|
||||
|
||||
@media print {{
|
||||
.print-button {{
|
||||
display: none;
|
||||
}}
|
||||
}}
|
||||
</style>
|
||||
<script>
|
||||
function printPDF() {{
|
||||
window.print();
|
||||
}}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<button class="print-button" onclick="printPDF()">Print to PDF</button>
|
||||
{html}
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
# Save HTML
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(html_with_style)
|
||||
|
||||
print(f"HTML created successfully: {output_file}")
|
||||
print(f"\nTo create PDF:")
|
||||
print(f"1. Open the HTML file in your browser")
|
||||
print(f"2. Click 'Print to PDF' button or use Ctrl+P")
|
||||
print(f"3. Select 'Save as PDF' as destination")
|
||||
print(f"4. Save the file")
|
||||
|
||||
if __name__ == '__main__':
|
||||
input_file = r'C:\Users\alexi\Documents\projects\freelance-dashboard\TECHNICAL_REFERENCE_EN.md'
|
||||
output_file = r'C:\Users\alexi\Documents\projects\freelance-dashboard\TECHNICAL_REFERENCE_EN.html'
|
||||
|
||||
if os.path.exists(input_file):
|
||||
convert_md_to_html(input_file, output_file)
|
||||
|
||||
# Open in default browser
|
||||
import webbrowser
|
||||
webbrowser.open(f'file:///{output_file}')
|
||||
else:
|
||||
print(f"Input file not found: {input_file}")
|
||||
211
md_to_pdf_final.py
Normal file
211
md_to_pdf_final.py
Normal file
@ -0,0 +1,211 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Convert Markdown to PDF with perfect UTF-8 support using Playwright
|
||||
"""
|
||||
import markdown
|
||||
import os
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
def convert_md_to_pdf(input_file, output_file):
|
||||
"""Convert markdown file to PDF using browser rendering"""
|
||||
|
||||
# Read markdown file
|
||||
with open(input_file, 'r', encoding='utf-8') as f:
|
||||
md_content = f.read()
|
||||
|
||||
# Convert markdown to HTML
|
||||
html = markdown.markdown(md_content, extensions=['tables', 'fenced_code', 'codehilite'])
|
||||
|
||||
# Add CSS styling
|
||||
html_with_style = f"""
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Freelance Dashboard - Technical Reference</title>
|
||||
<style>
|
||||
@page {{
|
||||
size: A4;
|
||||
margin: 2cm;
|
||||
}}
|
||||
|
||||
body {{
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 11pt;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
}}
|
||||
|
||||
h1 {{
|
||||
color: #2C3E50;
|
||||
font-size: 24pt;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 3px solid #27AE60;
|
||||
padding-bottom: 15px;
|
||||
}}
|
||||
|
||||
h2 {{
|
||||
color: #2C3E50;
|
||||
font-size: 18pt;
|
||||
margin-top: 25px;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 2px solid #ECF0F1;
|
||||
padding-bottom: 8px;
|
||||
page-break-before: always;
|
||||
}}
|
||||
|
||||
h2:first-of-type {{
|
||||
page-break-before: auto;
|
||||
}}
|
||||
|
||||
h3 {{
|
||||
color: #27AE60;
|
||||
font-size: 14pt;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
}}
|
||||
|
||||
h4 {{
|
||||
color: #2C3E50;
|
||||
font-size: 12pt;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 8px;
|
||||
}}
|
||||
|
||||
p {{
|
||||
margin: 10px 0;
|
||||
text-align: justify;
|
||||
}}
|
||||
|
||||
code {{
|
||||
background-color: #F5F5F5;
|
||||
padding: 2px 6px;
|
||||
font-family: 'Courier New', Consolas, monospace;
|
||||
font-size: 9pt;
|
||||
color: #C0392B;
|
||||
border: 1px solid #DDDDDD;
|
||||
border-radius: 3px;
|
||||
}}
|
||||
|
||||
pre {{
|
||||
background-color: #F5F5F5;
|
||||
color: #2C3E50;
|
||||
padding: 15px;
|
||||
border: 1px solid #DDDDDD;
|
||||
border-radius: 5px;
|
||||
overflow-x: auto;
|
||||
font-family: 'Courier New', Consolas, monospace;
|
||||
font-size: 9pt;
|
||||
line-height: 1.5;
|
||||
page-break-inside: avoid;
|
||||
}}
|
||||
|
||||
pre code {{
|
||||
background-color: transparent;
|
||||
color: #2C3E50;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}}
|
||||
|
||||
table {{
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 20px 0;
|
||||
font-size: 10pt;
|
||||
page-break-inside: avoid;
|
||||
}}
|
||||
|
||||
th {{
|
||||
background-color: #2C3E50;
|
||||
color: white;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
border: 1px solid #ddd;
|
||||
font-weight: bold;
|
||||
}}
|
||||
|
||||
td {{
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
}}
|
||||
|
||||
tr:nth-child(even) {{
|
||||
background-color: #f9f9f9;
|
||||
}}
|
||||
|
||||
blockquote {{
|
||||
border-left: 4px solid #27AE60;
|
||||
padding-left: 15px;
|
||||
margin: 20px 0;
|
||||
font-style: italic;
|
||||
color: #555;
|
||||
background-color: #f9f9f9;
|
||||
padding: 10px 15px;
|
||||
}}
|
||||
|
||||
ul, ol {{
|
||||
margin: 10px 0;
|
||||
padding-left: 30px;
|
||||
}}
|
||||
|
||||
li {{
|
||||
margin: 5px 0;
|
||||
}}
|
||||
|
||||
hr {{
|
||||
border: none;
|
||||
border-top: 2px solid #ECF0F1;
|
||||
margin: 30px 0;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{html}
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
# Create temporary HTML file
|
||||
temp_html = output_file.replace('.pdf', '_temp.html')
|
||||
with open(temp_html, 'w', encoding='utf-8') as f:
|
||||
f.write(html_with_style)
|
||||
|
||||
# Convert HTML to PDF using Playwright
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch()
|
||||
page = browser.new_page()
|
||||
page.goto(f'file:///{os.path.abspath(temp_html)}')
|
||||
page.pdf(
|
||||
path=output_file,
|
||||
format='A4',
|
||||
margin={
|
||||
'top': '2cm',
|
||||
'right': '2cm',
|
||||
'bottom': '2cm',
|
||||
'left': '2cm'
|
||||
},
|
||||
print_background=True
|
||||
)
|
||||
browser.close()
|
||||
|
||||
# Clean up temporary HTML
|
||||
if os.path.exists(temp_html):
|
||||
os.remove(temp_html)
|
||||
|
||||
print(f"PDF created successfully: {output_file}")
|
||||
print(f"File size: {os.path.getsize(output_file) / 1024:.2f} KB")
|
||||
|
||||
if __name__ == '__main__':
|
||||
input_file = r'C:\Users\alexi\Documents\projects\freelance-dashboard\TECHNICAL_REFERENCE_EN.md'
|
||||
output_file = r'C:\Users\alexi\Documents\projects\freelance-dashboard\TECHNICAL_REFERENCE_EN.pdf'
|
||||
|
||||
if os.path.exists(input_file):
|
||||
convert_md_to_pdf(input_file, output_file)
|
||||
else:
|
||||
print(f"Input file not found: {input_file}")
|
||||
150
md_to_pdf_fpdf.py
Normal file
150
md_to_pdf_fpdf.py
Normal file
@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Markdown to PDF converter with proper UTF-8 support using fpdf2
|
||||
"""
|
||||
import markdown
|
||||
from fpdf import FPDF
|
||||
import re
|
||||
import os
|
||||
|
||||
class PDF(FPDF):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.set_auto_page_break(auto=True, margin=15)
|
||||
|
||||
def header(self):
|
||||
# Header with title
|
||||
pass
|
||||
|
||||
def footer(self):
|
||||
# Page number
|
||||
self.set_y(-15)
|
||||
self.set_font('Arial', 'I', 8)
|
||||
self.set_text_color(128, 128, 128)
|
||||
self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C')
|
||||
|
||||
class MarkdownToPDF:
|
||||
def __init__(self, input_file, output_file):
|
||||
self.input_file = input_file
|
||||
self.output_file = output_file
|
||||
self.pdf = PDF()
|
||||
self.pdf.add_page()
|
||||
|
||||
def convert(self):
|
||||
# Read markdown
|
||||
with open(self.input_file, 'r', encoding='utf-8') as f:
|
||||
md_content = f.read()
|
||||
|
||||
# Parse line by line
|
||||
lines = md_content.split('\n')
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
|
||||
if not line:
|
||||
self.pdf.ln(3)
|
||||
continue
|
||||
|
||||
# Main title (H1)
|
||||
if line.startswith('# ') and not line.startswith('## '):
|
||||
title = line[2:].strip()
|
||||
self.pdf.set_font('Arial', 'B', 20)
|
||||
self.pdf.set_text_color(44, 62, 80) # #2C3E50
|
||||
self.pdf.multi_cell(0, 10, title, align='C')
|
||||
self.pdf.ln(5)
|
||||
|
||||
# H2
|
||||
elif line.startswith('## ') and not line.startswith('### '):
|
||||
title = line[3:].strip()
|
||||
self.pdf.ln(3)
|
||||
self.pdf.set_font('Arial', 'B', 16)
|
||||
self.pdf.set_text_color(44, 62, 80)
|
||||
self.pdf.multi_cell(0, 8, title)
|
||||
self.pdf.ln(2)
|
||||
|
||||
# H3
|
||||
elif line.startswith('### ') and not line.startswith('#### '):
|
||||
title = line[4:].strip()
|
||||
self.pdf.ln(2)
|
||||
self.pdf.set_font('Arial', 'B', 14)
|
||||
self.pdf.set_text_color(39, 174, 96) # #27AE60
|
||||
self.pdf.multi_cell(0, 7, title)
|
||||
self.pdf.ln(1)
|
||||
|
||||
# H4
|
||||
elif line.startswith('#### '):
|
||||
title = line[5:].strip()
|
||||
self.pdf.ln(1)
|
||||
self.pdf.set_font('Arial', 'B', 12)
|
||||
self.pdf.set_text_color(44, 62, 80)
|
||||
self.pdf.multi_cell(0, 6, title)
|
||||
|
||||
# Horizontal rule
|
||||
elif line.startswith('---'):
|
||||
self.pdf.ln(2)
|
||||
|
||||
# Code block start/end (```)
|
||||
elif line.startswith('```'):
|
||||
continue
|
||||
|
||||
# List item
|
||||
elif line.startswith('- ') or line.startswith('* '):
|
||||
text = line[2:].strip()
|
||||
# Remove markdown formatting
|
||||
text = self._clean_markdown(text)
|
||||
self.pdf.set_font('Arial', '', 10)
|
||||
self.pdf.set_text_color(51, 51, 51)
|
||||
self.pdf.set_x(self.pdf.l_margin + 5)
|
||||
self.pdf.multi_cell(0, 5, f'• {text}')
|
||||
|
||||
# Blockquote
|
||||
elif line.startswith('>'):
|
||||
text = line[1:].strip()
|
||||
text = self._clean_markdown(text)
|
||||
self.pdf.set_font('Arial', 'I', 10)
|
||||
self.pdf.set_text_color(85, 85, 85)
|
||||
self.pdf.set_fill_color(245, 245, 245)
|
||||
self.pdf.multi_cell(0, 5, text, fill=True)
|
||||
|
||||
# Table separator
|
||||
elif line.startswith('|') and '---' in line:
|
||||
continue
|
||||
|
||||
# Table row
|
||||
elif line.startswith('|'):
|
||||
continue # Skip tables for now (complex to render)
|
||||
|
||||
# Normal paragraph
|
||||
else:
|
||||
# Skip if it's likely a table header or separator
|
||||
if '|' in line:
|
||||
continue
|
||||
|
||||
text = self._clean_markdown(line)
|
||||
if text:
|
||||
self.pdf.set_font('Arial', '', 10)
|
||||
self.pdf.set_text_color(51, 51, 51)
|
||||
self.pdf.multi_cell(0, 5, text)
|
||||
|
||||
# Save PDF
|
||||
self.pdf.output(self.output_file)
|
||||
print(f"PDF created successfully: {self.output_file}")
|
||||
|
||||
def _clean_markdown(self, text):
|
||||
"""Remove markdown formatting"""
|
||||
# Remove bold **text**
|
||||
text = re.sub(r'\*\*(.*?)\*\*', r'\1', text)
|
||||
# Remove italic *text*
|
||||
text = re.sub(r'\*(.*?)\*', r'\1', text)
|
||||
# Remove inline code `text`
|
||||
text = re.sub(r'`(.*?)`', r'\1', text)
|
||||
# Remove links [text](url)
|
||||
text = re.sub(r'\[(.*?)\]\(.*?\)', r'\1', text)
|
||||
return text
|
||||
|
||||
if __name__ == '__main__':
|
||||
input_file = r'C:\Users\alexi\Documents\projects\freelance-dashboard\TECHNICAL_REFERENCE_EN.md'
|
||||
output_file = r'C:\Users\alexi\Documents\projects\freelance-dashboard\TECHNICAL_REFERENCE_EN.pdf'
|
||||
|
||||
converter = MarkdownToPDF(input_file, output_file)
|
||||
converter.convert()
|
||||
163
md_to_pdf_simple.py
Normal file
163
md_to_pdf_simple.py
Normal file
@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple Markdown to PDF converter using markdown and xhtml2pdf
|
||||
"""
|
||||
import markdown
|
||||
from xhtml2pdf import pisa
|
||||
import os
|
||||
|
||||
def convert_md_to_pdf(input_file, output_file):
|
||||
"""Convert markdown file to PDF"""
|
||||
|
||||
# Read markdown file
|
||||
with open(input_file, 'r', encoding='utf-8') as f:
|
||||
md_content = f.read()
|
||||
|
||||
# Convert markdown to HTML
|
||||
html = markdown.markdown(md_content, extensions=['tables', 'fenced_code', 'codehilite'])
|
||||
|
||||
# Add CSS styling
|
||||
html_with_style = f"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
@page {{
|
||||
size: A4;
|
||||
margin: 2cm;
|
||||
}}
|
||||
body {{
|
||||
font-family: 'Arial', sans-serif;
|
||||
font-size: 11pt;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}}
|
||||
h1 {{
|
||||
color: #2C3E50;
|
||||
font-size: 24pt;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 3px solid #27AE60;
|
||||
padding-bottom: 10px;
|
||||
}}
|
||||
h2 {{
|
||||
color: #2C3E50;
|
||||
font-size: 18pt;
|
||||
margin-top: 25px;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 2px solid #ECF0F1;
|
||||
padding-bottom: 5px;
|
||||
}}
|
||||
h3 {{
|
||||
color: #27AE60;
|
||||
font-size: 14pt;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
}}
|
||||
h4 {{
|
||||
color: #2C3E50;
|
||||
font-size: 12pt;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 8px;
|
||||
}}
|
||||
p {{
|
||||
margin: 10px 0;
|
||||
text-align: justify;
|
||||
}}
|
||||
code {{
|
||||
background-color: #F5F5F5;
|
||||
padding: 2px 5px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 9pt;
|
||||
color: #C0392B;
|
||||
border: 1px solid #DDDDDD;
|
||||
}}
|
||||
pre {{
|
||||
background-color: #F5F5F5;
|
||||
color: #2C3E50;
|
||||
padding: 15px;
|
||||
border: 1px solid #DDDDDD;
|
||||
overflow-x: auto;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 9pt;
|
||||
line-height: 1.5;
|
||||
}}
|
||||
pre code {{
|
||||
background-color: transparent;
|
||||
color: #2C3E50;
|
||||
padding: 0;
|
||||
}}
|
||||
table {{
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 20px 0;
|
||||
font-size: 10pt;
|
||||
}}
|
||||
th {{
|
||||
background-color: #2C3E50;
|
||||
color: white;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
border: 1px solid #ddd;
|
||||
}}
|
||||
td {{
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
}}
|
||||
tr:nth-child(even) {{
|
||||
background-color: #f9f9f9;
|
||||
}}
|
||||
blockquote {{
|
||||
border-left: 4px solid #27AE60;
|
||||
padding-left: 15px;
|
||||
margin: 20px 0;
|
||||
font-style: italic;
|
||||
color: #555;
|
||||
}}
|
||||
ul, ol {{
|
||||
margin: 10px 0;
|
||||
padding-left: 30px;
|
||||
}}
|
||||
li {{
|
||||
margin: 5px 0;
|
||||
}}
|
||||
hr {{
|
||||
border: none;
|
||||
border-top: 2px solid #ECF0F1;
|
||||
margin: 30px 0;
|
||||
}}
|
||||
.page-break {{
|
||||
page-break-after: always;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{html}
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
# Convert HTML to PDF with encoding
|
||||
with open(output_file, 'wb') as pdf_file:
|
||||
pisa_status = pisa.CreatePDF(
|
||||
html_with_style.encode('utf-8'),
|
||||
dest=pdf_file,
|
||||
encoding='utf-8'
|
||||
)
|
||||
|
||||
if pisa_status.err:
|
||||
print(f"Error creating PDF: {pisa_status.err}")
|
||||
return False
|
||||
else:
|
||||
print(f"PDF created successfully: {output_file}")
|
||||
return True
|
||||
|
||||
if __name__ == '__main__':
|
||||
input_file = r'C:\Users\alexi\Documents\projects\freelance-dashboard\TECHNICAL_REFERENCE_EN.md'
|
||||
output_file = r'C:\Users\alexi\Documents\projects\freelance-dashboard\TECHNICAL_REFERENCE_EN.pdf'
|
||||
|
||||
if os.path.exists(input_file):
|
||||
convert_md_to_pdf(input_file, output_file)
|
||||
else:
|
||||
print(f"Input file not found: {input_file}")
|
||||
11
templates/.vba_backups/.vba_backups.json
Normal file
11
templates/.vba_backups/.vba_backups.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"file": "FreelanceDashboard.xlsx",
|
||||
"backups": [
|
||||
{
|
||||
"id": "20251230_121153",
|
||||
"filename": "FreelanceDashboard_backup_20251230_121153.xlsx",
|
||||
"created": "2025-12-30T12:11:53.147808",
|
||||
"original_size": 17005
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
BIN
templates/CheckboxDemo.xlsm
Normal file
BIN
templates/CheckboxDemo.xlsm
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
templates/FreelanceDashboard_Checkboxes.xlsm
Normal file
BIN
templates/FreelanceDashboard_Checkboxes.xlsm
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user