Developpeur Full-Stack
🎯 Contexte et objectifs
- Faire évoluer des applications naturalistes en production sur des flux critiques: import, synthèse, monitoring, taxonomie et gestion des utilisateurs.
- Stabiliser les interfaces backend/frontend via des contrats API plus robustes, du filtrage/pagination, des formulaires dynamiques et une gestion d’erreurs homogène.
- Réduire les corrections manuelles côté métier en améliorant la cohérence des modèles de données et des parcours UI.
🛠️ Réalisations
Projets
🧩 Conception
- GeoNature (core): conception de routes d’import orientées permissions (CRUVED), pagination et tri/filtrage. Source: commit a38c1bea5e, PR #3277.
- gn_module_monitoring: structuration modulaire (Blueprint + CLI module) pour isoler l’administration et faciliter les évolutions incrémentales. Source: commit f75c9f0f8b, PR #334.
- TaxHub: optimisation de la conception d’endpoints taxonomiques avec chargement relationnel explicite. Source: PR #583, PR #585.
💻 Développement
- Backend :
- GeoNature-citizen: fiabilisation de la sérialisation GeoJSON et enrichissement taxon/media. Source: commit 14b4953925, PR #430.
feature = get_geojson_feature(self.geom)
for k in result_dict:
if k in OBS_KEYS:
feature["properties"][k] = (
result_dict[k].name if isinstance(result_dict[k], Enum) else result_dict[k]
)
feature["properties"]["photos"] = [
{
"url": f"/media/{p.media.filename}",
"date": result_dict["date"],
"author": self.obs_txt,
}
for p in self.medias
]
- GeoNature (core): implémentation d’API d’import paginées/filtrables avec jointures SQLAlchemy et contraintes de scope. Source: commit a38c1bea5e, PR #3277.
query = (
select(TImports)
.options(
contains_eager(TImports.authors),
contains_eager(TImports.destination).contains_eager(Destination.module),
)
.join(TImports.authors, isouter=True)
.join(Destination)
.join(TModules)
.where(TImports.filter_by_scope(scope=scope))
.where(or_(*filters) if len(filters) > 0 else True)
.order_by(order_by)
)
imports = db.paginate(query, page=page, error_out=False, max_per_page=limit)
- gn_module_export: ajout de contrôles de formulaires d’administration (vue SQL/colonnes) et sécurisation des paramètres d’export. Source: commit 4efe827179, PR #145.
def validate_form(self, form):
view_name = getattr(form, "view_name", "")
schema_name = getattr(form, "schema_name", "")
if is_form_submitted() and view_name and schema_name:
query = GenericQueryGeo(
DB,
view_name.data,
schema_name.data,
geometry_field=geometry_field.data,
filters=[],
)
columns = query.view.tableDef.columns.keys()
- TaxHub: optimisation de la route
biblisteset correction de l’expression SQLAlchemynb_taxons. Source: PR #585, PR #583.
def get_biblistes():
biblistes_records = db.session.query(
BibListes.id_liste, BibListes.code_liste, BibListes.nom_liste, BibListes.nb_taxons
).all()
biblistes_infos = {
"data": biblistes_schema.dump(biblistes_records, many=True),
"count": len(biblistes_records),
}
return biblistes_infos
- UsersHub: centralisation des handlers d’erreurs SQL et exceptions globales sur les routes CRUD. Source: PR #241.
from sqlalchemy.exc import ProgrammingError, IntegrityError
from app.utils.errors import (
handle_unauthenticated_request,
handle_integrity_error,
handle_general_exception,
)
app.login_manager.unauthorized_handler(handle_unauthenticated_request)
app.register_error_handler(IntegrityError, handle_integrity_error)
app.register_error_handler(Exception, handle_general_exception)
- Frontend :
- GeoNature-citizen: flux RxJS conditionnel pour taxonomie (chargement, tri, présélection). Source: commit 8b932390d2, PR #381.
this.surveySpecies$ = this.programService
.getAllProgramTaxonomyList()
.pipe(
map((listsTaxonomy) => {
this.taxaCount = listsTaxonomy
.filter((lt) => lt.id_liste === this.taxonomyListID)
.map((lt) => lt.nb_taxons)[0];
return this.taxaCount < this.taxonAutocompleteInputThreshold;
}),
switchMap((shouldFetchTaxonomyList) => shouldFetchTaxonomyList
? this.programService.getProgramTaxonomyList(this.taxonomyListID)
: []
),
share()
);
- GeoNature (core): conteneur modal piloté par la route avec nettoyage des subscriptions et navigation retour. Source: commit d7f8b6a88d, PR #3169.
route.params.pipe(takeUntil(this.destroy)).subscribe((params) => {
this.currentDialog = this.modalService.open(SyntheseInfoObsComponent, {
size: 'lg',
windowClass: 'large-modal',
});
this.currentDialog.componentInstance.idSynthese = params.id_synthese;
this.currentDialog.componentInstance.selectedTab = params.tab;
this.dialogResult = this.currentDialog.result.then(
() => this.location.back(),
() => this.location.back()
);
});
- gn_module_monitoring: refactoring de formulaires Angular dynamiques avec orchestration RxJS (
switchMap,concatMap,iif). Source: commit 07c21ce41b, PR #242.
this._configService
.init(this.obj.moduleCode)
.pipe(
switchMap((_) =>
iif(
() => this.isSiteObject,
this.initTypeSiteConfig(this.obj.config['specific'], this.obj['properties'], this.obj.config['types_site'])
.pipe(concatMap(({ idsTypesSite, typesSiteConfig }) => {
idsTypesSite.forEach((number) => this.idsTypesSite.add(number));
this.allTypesSiteConfig = typesSiteConfig;
return this.initSpecificConfig(this.obj.config['specific'], this.obj.config['types_site']);
})),
this.initSpecificConfig(this.obj.config['specific'])
)
)
);
🏗️ Infrastructure et déploiement
- gn_module_monitoring: contribution directe détectée sur la qualité CI frontend (Prettier/lint). Source: commit 8789aebdfc, commit d37d23ee6e, workflow lint.yml.
frontend:
steps:
- uses: actions/setup-node@v3
- name: Frontend code formatting check (Prettier)
run: npm install prettier@~3.1.0 && npm run format:check
- Sur les autres dépôts, les pipelines sont utilisés dans le cycle de livraison, sans attribution de création globale en l’absence de preuve commit directe sur les fichiers de workflow.
🧭 Organisation / méthodologie
- Cadre de travail Agile/Scrum avec revue de code sur PR.
- Pair programming sur sujets structurants (refactoring, formulaires complexes, API critiques).
- Cycle de sprint régulier (daily, planning, review) avec suivi de priorisation.
📈 Résultats
- Résultats globaux: 547 commits, 76 PR, 45 issues liées sur 6 projets. Les livraisons couvrent API, modèle de données, formulaires UI et qualité CI, avec amélioration de la fiabilité opérationnelle et de la maintenabilité.
Projet GeoNature (core)
- Nb PR: 39 (5 open / 34 closed / 23 merged / 1 draft)
- Nb commits: 178
- Nb issues: 25 (10 open / 15 closed)
- Période: 2023-02-02 -> 2025-07-29
Projet GeoNature-citizen
- Nb PR: 9 (3 open / 6 closed / 4 merged / 0 draft)
- Nb commits: 34
- Nb issues: 3 (1 open / 2 closed)
- Période: 2024-11-20 -> 2025-03-24
Projet gn_module_monitoring
- Nb PR: 19 (3 open / 16 closed / 9 merged / 1 draft)
- Nb commits: 329
- Nb issues: 6 (3 open / 3 closed)
- Période: 2022-12-12 -> 2025-11-14
Projet gn_module_export
- Nb PR: 5 (0 open / 5 closed / 3 merged / 0 draft)
- Nb commits: 6
- Nb issues: 2 (0 open / 2 closed)
- Période: 2023-05-11 -> 2023-05-16
Projet TaxHub
- Nb PR: 3 (0 open / 3 closed / 0 merged / 0 draft)
- Nb commits attribués localement: 0
- Nb issues: 9 (2 open / 7 closed)
- Période: attribution locale indisponible (contributions observées via PR)
Projet UsersHub
- Nb PR: 1 (1 open / 0 closed / 0 merged / 0 draft)
- Nb commits attribués localement: 0
- Nb issues: 0
- Période: attribution locale indisponible (contribution observée via PR)
🔧 Environnement technique
- Langages: Python, TypeScript, SQL.
- Backend: Flask, SQLAlchemy, GeoAlchemy2, Marshmallow, Alembic, Celery, Gunicorn.
- Frontend: Angular, RxJS, Leaflet, Angular Material, Bootstrap.
- Qualité/tests: Pytest, Coverage, Cypress, Black, Isort, Pylint, ESLint, Prettier.
- CI/CD & Ops: GitHub Actions, Docker, scripts d’installation/migration, quality gates.
- Données: PostgreSQL/PostGIS, migrations SQL, optimisation de requêtes, modèles de permissions.
Technologies utilisées
Backend
Alembic
Celery
Flask
Gunicorn
Marshmallow
Python
Frontend
Angular
Angular Material
Bootstrap
Leaflet
RxJS
TypeScript
Qualite / Tests
Cypress
ESLint
Prettier
Pytest
DevOps
Docker
docker-compose
GitHub Actions
GitLab CI/CD
Bases de donnees (SGBD & SQL)
PostGIS
PostgreSQL
Design Patterns & Architecture
SQLAlchemy