GIS Map in Elasticsearch with Kibana using Vega, Scripted Fields & Painless

Elasticsearch is a distributed open source, RESTful search engine built on top of Apache Lucene and released under an Apache license. Kibana is an open source data visualization plugin for Elasticsearch. Vega (and Vega-lite) allows to beyond the built-in visualizations offered by Kibana.

In this short tutorial we will create a GIS map that displays individual documents in Elasticsearch into a map as marks, that have different shapes and colors, with information about the documents on the marks.

The graph will look like

GIS Map using Vega

GIS Map using Vega

"$schema": "",
config: {
kibana: {
type: map
latitude: 15
longitude: -10
zoom: 3
"data": [
"name": "observations",
"url": {
index: logstash-2018*
"body": {
"size": 10000
_source: {
includes: ["@timestamp", "items_per_minute", "school", "geoip.location", "county", "subcounty","zone", "lesson_start_time","subject", "end_time", "start_time", "class"]
script_fields : {
lesson_duration : {
script : {
lang: 'painless',
source: doc['end_time'].date.minuteOfDay - doc['start_time'].date.minuteOfDay;
lesson_date : {
script : {
lang: 'painless',
// Get the time in the correct format
source: doc['start_time'].date.yearOfEra + '-' + doc['start_time'].date.monthOfYear + '-' + doc['start_time'].date.dayOfMonth
grade : {
script : {
lang: 'painless',
"source": "if(doc.containsKey('formId.keyword')){if(doc['formId.keyword'].value == 'Gradethreeobservationtool'){return 3;}if(doc['formId.keyword'].value == 'maths-grade3'){return 3;}if(doc['formId.keyword'].value == 'class-12-lesson-observation-with-pupil-books'){if(doc.containsKey('class.keyword')){return doc['class.keyword'].value;}}if(doc['formId.keyword'].value == 'maths-teachers-observation-tool'){if(doc.containsKey('class.keyword')){return doc['class.keyword'].value;}}}"
"sort": {"start_time":"desc"}
"query": {
"bool": {
"must": [
// This string will be replaced with the auto-generated "MUST" clause
// apply timefilter (upper right corner) to the @timestamp variable
"range": {
"@timestamp": {
// "%timefilter%" will be replaced with the current
// values of the time filter (from the upper right corner)
"%timefilter%": true
// Only work with %timefilter%
// Shift the current timefilter by 10 units back
"shift": 10,
// supports week, day (default), hour, minute, second.
"unit": "minute"
{"exists": {"field": "geoip.location"}}
{"exists": {"field": "school"}}
{"exists": {"field": "start_time"}}
"must_not": [
// This string will be replaced with the auto-generated "MUST-NOT" clause
{"match": { "school": "orphaned" }}
"format": { "type": "json", "property": "hits.hits"}
"transform": [
"lookup": "geoip_location",
"type": "geopoint",
"projection": "projection",
"fields": [
"name": "selected",
"source": "observations",
"transform": [
"type": "filter",
"expr": "datum === selected"
"signals": [
"name": "selected",
"value": null,
"on": [
{"events": "symbol:mouseover", "update": "datum"},
{"events": "symbol:mouseout", "update": "null"}
"marks": [
name: "observation"
type: "symbol"
from: {data: "observations"}
encode: {
"enter": {
// different shapes for grades
"shape": [
{"test": "datum.fields.grade == 1", "value": "square"}
{"test": "datum.fields.grade == 2", "value": "triangle-up"}
{"value": "circle"}
// different colors for subjects
"fill": [
{"test": "datum._source.subject === 'English'", "value": "#159"}
{"test": "datum._source.subject === 'Maths'", "value": "#195"}
{"value": "#815"}
"size": {"value": "200"}
update: {
xc: {signal: "datum.x"}
yc: {signal: "datum.y"}
tooltip: {
signal: "{School:, County: datum._source.county, Subcounty: datum._source.subcounty, Zone:, Subject: datum._source.subject, Grade:''+ datum.fields.grade, 'Items Per Minute': datum._source.items_per_minute, Date: ''+datum.fields.lesson_date, 'Start Time': hours(datum._source.start_time) + 3 + ':' + minutes(datum._source.start_time), 'End Time': hours(datum._source.end_time) + 3 + ':' + minutes(datum._source.end_time) + ' (' + datum.fields.lesson_duration + ' minutes)'}"
"fillOpacity": {"value": 0.7}
"hover": {
"fillOpacity": {"value": 0.3}

How to create a CloudFront distribution with AWS SDK for Go

Amazon Web Services provides different SDKs, Toolkits and Command Line Tools to develop and manage application running on AWS. AWS SDK for Go is one of the latest tools provided. New versions are pushed almost every 5 days.

In this blog post, we will write a simple Go code to create a CloudFront distribution with the default settings and the following:

  • An S3 bucket as origin for the distribution
  • A Lambda@Edge function associated to the default behavior
  • A WAF Rule

For more information about:

  • CloudFront
  • Installing and configuring AWS SDK for Go
  • CloudFront APIs with AWS SDK for Go
  • Lambda@Edge
  • WAF (Web Application Firewal)

  • package main

    import (

    func main() {

    creds := aws.Creds(accessKey, secretKey, "")
    svc := cloudfront.New(creds, "us-east-1", nil)

    // svc := cloudfront.New(session.New()) Can replace the 2 lines above if using Instance Role or Env. Variables

    input := &cloudfront.CreateDistributionWithTagsInput{
    Tags: &cloudfront.Tags{
    Items: []*cloudfront.Tag{
    DistributionConfig: &cloudfront.DistributionConfig{
    CallerReference: aws.String("Sat Sept. 30 2017"),
    Comment: aws.String("My WordPress Blog"),
    Enabled: aws.Bool(true),
    WebACLId: aws.String("eSamplec-5a3e-4857-9b92-0a5Sample3a4"),
    Origins: &cloudfront.Origins{
    Quantity: aws.Int64(1),
    Items: []*cloudfront.Origin{
    Id: aws.String("Jil_S3Origin"),
    DomainName: aws.String(""),
    S3OriginConfig: &cloudfront.S3OriginConfig{
    OriginAccessIdentity: aws.String(""),
    DefaultCacheBehavior: &cloudfront.DefaultCacheBehavior{
    TargetOriginId: aws.String("Jil_S3Origin"),
    MinTTL: aws.Int64(10),
    ViewerProtocolPolicy: aws.String("allow-all"),
    LambdaFunctionAssociations: &cloudfront.LambdaFunctionAssociations{
    Quantity: aws.Int64(1),
    Items: []*cloudfront.LambdaFunctionAssociation{
    EventType: aws.String("viewer-request"), // "viewer-request" | "viewer-response" | "origin-request" | "origin-response"
    LambdaFunctionARN: aws.String("arn:aws:lambda:us-east-1:123456789012:function:myFunctionName:2"), // the version of the function must be added
    TrustedSigners: &cloudfront.TrustedSigners{
    Enabled: aws.Bool(false),
    Quantity: aws.Int64(0),
    ForwardedValues: &cloudfront.ForwardedValues{
    Cookies: &cloudfront.CookiePreference{
    Forward: aws.String("none"),
    QueryString: aws.Bool(false),

    result, err := svc.CreateDistributionWithTags(input)

    if err != nil {
    if aerr, ok := err.(awserr.Error); ok {
    switch aerr.Code() {
    case cloudfront.ErrCodeCNAMEAlreadyExists:
    fmt.Println(cloudfront.ErrCodeCNAMEAlreadyExists, aerr.Error())
    case cloudfront.ErrCodeDistributionAlreadyExists:
    fmt.Println(cloudfront.ErrCodeDistributionAlreadyExists, aerr.Error())
    case cloudfront.ErrCodeInvalidOrigin:
    fmt.Println(cloudfront.ErrCodeInvalidOrigin, aerr.Error())
    case cloudfront.ErrCodeInvalidOriginAccessIdentity:
    fmt.Println(cloudfront.ErrCodeInvalidOriginAccessIdentity, aerr.Error())
    case cloudfront.ErrCodeAccessDenied:
    fmt.Println(cloudfront.ErrCodeAccessDenied, aerr.Error())
    case cloudfront.ErrCodeTooManyTrustedSigners:
    fmt.Println(cloudfront.ErrCodeTooManyTrustedSigners, aerr.Error())
    case cloudfront.ErrCodeTrustedSignerDoesNotExist:
    fmt.Println(cloudfront.ErrCodeTrustedSignerDoesNotExist, aerr.Error())
    case cloudfront.ErrCodeInvalidViewerCertificate:
    fmt.Println(cloudfront.ErrCodeTooManyCertificates, aerr.Error())
    case cloudfront.ErrCodeInvalidLocationCode:
    fmt.Println(cloudfront.ErrCodeInvalidLocationCode, aerr.Error())
    case cloudfront.ErrCodeInvalidGeoRestrictionParameter:
    fmt.Println(cloudfront.ErrCodeInvalidGeoRestrictionParameter, aerr.Error())
    case cloudfront.ErrCodeInvalidProtocolSettings:
    fmt.Println(cloudfront.ErrCodeInvalidProtocolSettings, aerr.Error())
    case cloudfront.ErrCodeInvalidTTLOrder:
    fmt.Println(cloudfront.ErrCodeInvalidTTLOrder, aerr.Error())
    case cloudfront.ErrCodeInvalidWebACLId:
    fmt.Println(cloudfront.ErrCodeInvalidWebACLId, aerr.Error())
    case cloudfront.ErrCodeTooManyOriginCustomHeaders:
    fmt.Println(cloudfront.ErrCodeTooManyOriginCustomHeaders, aerr.Error())
    case cloudfront.ErrCodeTooManyQueryStringParameters:
    fmt.Println(cloudfront.ErrCodeTooManyQueryStringParameters, aerr.Error())
    case cloudfront.ErrCodeInvalidQueryStringParameters:
    fmt.Println(cloudfront.ErrCodeInvalidQueryStringParameters, aerr.Error())
    case cloudfront.ErrCodeTooManyDistributionsWithLambdaAssociations:
    fmt.Println(cloudfront.ErrCodeTooManyDistributionsWithLambdaAssociations, aerr.Error())
    case cloudfront.ErrCodeTooManyLambdaFunctionAssociations:
    fmt.Println(cloudfront.ErrCodeTooManyLambdaFunctionAssociations, aerr.Error())
    case cloudfront.ErrCodeInvalidLambdaFunctionAssociation:
    fmt.Println(cloudfront.ErrCodeInvalidLambdaFunctionAssociation, aerr.Error())
    case cloudfront.ErrCodeInvalidOriginReadTimeout:
    fmt.Println(cloudfront.ErrCodeInvalidOriginReadTimeout, aerr.Error())
    case cloudfront.ErrCodeInvalidOriginKeepaliveTimeout:
    fmt.Println(cloudfront.ErrCodeInvalidOriginKeepaliveTimeout, aerr.Error())
    } else { // Print the error, cast err to awserr.Error to get the Code and Message from an error.

14 ans déjà que le Lion du Panjshir nous a quitté

Le Lion du Panjshir

Commandant Massoud

09 septembre 2001 – 09 septembre 2015, 14 ans déjà que le Lion du Panjshir nous a quitté. Celui qui a combattu les russes, les Talibans et Al Qaïda est mort suite à un attentat-suicide de deux prétendus journalistes qui font exploser leur camera pendant une interview.

A 25 ans (1978) Ahmed Chah Massoud (qui deviendra plus tard le Commandant Massoud) crée et prend la tête du “Conseil de surveillance”, il est un tacticien et un stratège hors pair et le seul chef de la Résistance à avoir jamais réussi à imposer une trêve avec l’Armée Rouge en échange de son retrait.

Il périt deux jours avant les attentats du 11 septembre, il avait pourtant plusieurs fois tenté d’alerter la communauté internationale sur le danger Al Qaïda.

RIP‬ Massoud!

Rapport mHealth au Congo – Etude de faisabilité et recommandations

De septembre à décembre 2013, j’ai mené une étude de faisabilité de l’utilisation de la téléphonie mobile pour l’amélioration de la couverture vaccinale au Congo (Brazzaville).

Objectifs de l’étude

L’objectif de la mission était d’étudier la faisabilité tant sur les plans technique, organisationnel que technologique du projet d” « utilisation de la téléphonie mobile pour l’amélioration de la couverture des interventions à haut impact au Congo : Vaccination, déparasitage, supplémentation en vitamine A » et de réviser le draft du projet sur la base des résultats et du contexte effectif au Congo.

L’étude devrait fournir suffisamment d’informations à l’UNICEF et aux principaux partenaires (gouvernement, compagnies de téléphonie mobile, etc.) pour leur permettre de valider l’implémentation du projet. Elle devra également permettre d’orienter les choix stratégiques et les modalités de mise en œuvre d’un tel projet, les informations suivantes doivent en ressortir :

  • Les usages des téléphones portables par les ménages au Congo,
  • Mapping des projets utilisant la téléphonie mobile et évaluation des bonnes
    pratiques en matière de téléphonie mobile pour le développement au Congo,
  • Développement du document du projet pilote dans les départements de Brazzaville et Pointe Noire (plan d’implémentation, budget détaillé, équipe projet avec rôle de chaque partie prenante, suivi et évaluation, risques du projet, etc.),
  • Recommandations sur l’implémentation du projet pilote, les négociations avec les compagnies de téléphonie mobile, l’appropriation par le gouvernement et son
    extension à l’ensemble du territoire congolais.

Méthodologie utilisée

Etude de faisabilité : Méthodologie

Télécharger le rapport

Rapport sur l’étude de faisabilité et les recommandations (PDF, 40 pages, 1.9Mo)


Téléphonie mobile pour le développement – Risques et mesures d’atténuation

Total African Mobile Connections and Penetration Rate (million, percentage penetration).

Total African Mobile Connections and Penetration Rate (million, percentage penetration).

Plusieurs organisations internationales et ONG (UNESCO, UNICEF, Plan International, Carter Center, etc.) souhaitent profiter du taux de pénétration de la téléphonie mobile dans les pays en développement pour améliorer les conditions de vie des populations. De plus en plus projets sur la santé, l’éducation, la bonne gouvernance, etc.  incluant les téléphones portables voient donc le jour, un bon pourcentage utilise les SMS (Short Message Services) via les applications telles que RapidSMS et FrontLineSMS.

Comme pour toute initiative, en particulier celles basées sur les technologies de l’information, plusieurs risques peuvent impacter son bon déroulement et affecter de façon significative les résultats attendus. Dans le tableau ci-dessous un certain nombre de risques et des mesures d’atténuation à prendre en compte dans la conception du projet, risques à adapter en fonction du projet bien évidemment ;-), divisés en deux types :

  1. Les risques fonctionnels, techniques et liés aux processus sont associés au fonctionnement, aux procédures, aux ressources, à la communication du projet
  2. Les risques du projet sont associés aux aspects techniques et de mise en œuvre
Risque Prob. Impact Mesures d’atténuation
Risques fonctionnels, techniques et liés aux processus
Erreurs dans l’envoi des SMS par le public cible Moyen Elevé. Données non utilisables par le système Limiter le nombre de procédures, utiliser une nomenclature simple, simple bien former les utilisateurs
Les interfaces utilisateurs sont difficiles à comprendre, ou l’utilisation requiert une longue procédure Moyen Modéré. Système difficile à utiliser, utilisateurs finaux découragés Prendre en compte l’utilisateur pendant la conception, Produire les règles d’ergonomie
Anomalies de fonctionnement et instabilité de l’environnement Bas Elevé. Intégrité des données. Système souvent hors utilisation Renforcement des tests, Recensement des bugs, Choix techniques
Risques du projet
Ne pas avoir l’adhésion du gouvernement
et des partenaires
Appropriation et viabilité à long terme du projet Impliquer le gouvernement et les partenaires dès la conception du projet
Ne pas héberger le système au sein du gouvernement Inclure un coût pour l’hébergement et le personnel technique. Renforcer les capacités afin d’assurer le support
Ne pas avoir un spécialiste TIC et un coordonateur du projet au sein du gouvernement
Les personnes chargées du monitoring sur le terrain ne possèdent pas de téléphone, ou ont des téléphones non fiables Impact élevé sur la mise à jour de la BD et l’intégrité des données Inclure un coût supplémentaire dans le budget, évaluer les téléphones pendant les formations
La langue d’envoi des messages n’est pas appropriée Impact élevé, les utilisateurs auront de la peine à les lire Savoir les préférences des utilisateurs finaux. Utiliser plusieurs langues au besoin
Le public bénéficiaire ne consulte pas sa messagerie Un bon pourcentage de messages ne sont pas lus Renforcer la communication autour du projet
Ne pas avoir une compagnie IT locale (ou un développeur local) pour développer l’application et en assurer le suivi Difficultés dans l’implémentation et le support du système Mettre sur pied une équipe technique à long terme (entreprise IT ou recruter un développeur)
Les utilisateurs finaux ne sont pas impliqués dès le début du projet, et ne sont pas bien formés Mauvaise utilisation et appropriation du dispositif Créer un groupe de travail pour valider les spécifications, Créer un comité d’utilisateurs

Utilisation de la téléphonie mobile pour l’amélioration de la santé de l’enfant au Congo

Le bureau de l’UNICEF à Brazzaville met sur pieds en ce moment un projet innovant sur l’utilisation de la téléphonie mobile pour l’amélioration de la couverture des interventions à haut impact (Vaccination, déparasitage, supplémentation en vitamine A). Utilisation des mobiles pour le développement

Au Congo 54,5% d’enfants de 12-24 mois n’ont pas été complètement vaccinés, de plus la malnutrition chronique touche 24.4% d’enfants de moins de 5 ans et constitue un problème de santé publique majeur. Le projet vise à exploiter le fort taux de pénétration de la téléphonie (plus de 92% de ménages en milieu urbain possèdent un téléphone) pour informer (alerter) les parents des dates de vaccination de leurs enfants par SMS et éventuellement par messages vocaux automatiques (OBD, IVR). Le projet mettra également sur pieds un système de calendrier vaccinal électronique.

Un serveur enregistrera quelques informations lorsqu’une femme vient accoucher (nom de l’enfant, date et lieu d’accouchement, noms des parents et numéros de téléphones, centre de santé, etc.) et enverra ensuite automatiquement des SMS (et éventuellement messages vocaux) de rappel aux parents, centres de santé et agents de santé, une semaine et un jour avant un événement, et éventuellement 3 jours et une semaine après l’événement, si les parents ne se sont pas présentés. Il sera aussi possible pour les parents d’envoyer des SMS gratuits au système afin de donner leur feedback sur les services dont ils bénéficient, des sondages par SMS seront aussi organisés pendant toute la durée du projet.

Toutes les informations et données enregistrées pendant la durée du projet seront consultables en temps réel sur un site web internet, ce qui pourra permettre au gouvernement et partenaires à mieux orienter leurs politiques. L’architecture technique du serveur sera conçue autour des technologies suivantes: la librairie RapidSMS, un OS GNU/Linux (Debian ou Ubuntu Server), Python et les Packages requis, le Framework Web Django, un Gateway SMS (Kannel?), MySQL comme base de données et un serveur web.

Présentation PowerPoint de l’étude de faisabilité du projet et des recommandations

6 things you should know about Amazon CloudSearch

Amazon CloudSearch is a fully-managed service in the AWS Cloud that makes it simple and cost-effective to set up, manage, and scale a custom search solution for your website or application.

Amazon CloudSearch supports a rich set of features including language-specific text processing for 34 languages, free text search, faceted search, geospatial search, customizable relevance ranking, highlighting, autocomplete and user configurable scaling and availability options [1].

Here are six key things you should know about Amazon CloudSearch.

1. How to build a search solution with Amazon CloudSearch?

There are three main steps:

  1. Create and configure a search domain: A search domain includes your searchable data and the search instances that handle your search requests. If you have multiple collections of data that you want to make searchable, you can create multiple search domains
  2. Upload the data you want to search to your domain: Amazon CloudSearch indexes your data and deploys the search index to one or more search instances,
  3. Search your domain: You send a search request to your domain’s search endpoint as an HTTP/HTTPS GET request.

2. How much data can you store in CloudSearch?

The number of partitions you need depends on your data and configuration. When you upload data, Amazon CloudSearch deploys one or more search instances. As your data volume grows, more search instances or larger search instances are deployed to contain your indexed data. The maximum number of search instances that can be deployed for a domain is 50, and a search index can be splited across a maximum of 10 partitions. For more information about CloudSearch limits, see Ref. [2].

3. How are you charged?

You are charged for Search instances, Document batch uploads, IndexDocuments requests and Data transfer. The Ref. [3] provides a useful resource to estimate your CloudSearch monthly bill.

4. Can you query for documents older than X and then send a batch delete request?

At the moment, Amazon CloudSearch does not provide this feature, but you can use one of their SDKs to list documents based on some parameters, and then delete them.

5. Do you need to keep a copy of the index anywhere?

You don’t really need, you can rely only on CloudSearch as source of the analytics data, as far as you respect AWS Security Best practices [5]. Amazon CloudSearch stores your data internally in high availability stores, you will not have to re-index your data, should a CloudSearch instance have an issue (this will be handled transparently). It offers key benefits including automatic node monitoring and recovery, built in data durability, easy setup and configuration and hands off auto scaling.

6. What’s the difference in the different instance sizes for CloudSearch?

A search instance is a single search engine that indexes documents and responds to search requests. It has a finite amount of RAM and CPU resources for indexing data and processing requests. There are four available instance types within CloudSearch: search.m1.small (2 Million documents), search.m1.large (8 Million documents), search.m2.xlarge (16 Million documents) and search.m2.2xlarge (32 Million documents).

Short Amazon CloudSearch Video

[1] – Amazon CloudSearch
[2] – Understanding Amazon CloudSearch Limits
[3] – AWS Simple Monthly Calculator
[4] – AWS Security Best practices [pdf]

Uploading a Large File to Amazon Web Services S3


Amazon Web Services Simple Storage Service

The largest single file that can be uploaded into an Amazon S3 Bucket in a single PUT operation is 5 GB. If you want to upload large objects (> 5 GB), you will consider using multipart upload API, which allows to upload objects from 5 MB up to 5 TB.

The Multipart Upload API is designed to improve the upload experience for larger objects, which can be uploaded in parts, independently, in any order, and in parallel. The AWS tool to use to perform this is API-Level (s3api) command set.

In this tutorial, we assume:

  • You have installed and configured AWS Command Line Interface on a Linux OS computer/server,
  • You have an Amazon account and a S3 Bucket (MyBucketName),
  • The size of the file to upload is 20 GB (,
  • 100 MB can be uploaded without problem using our internet connection.

Theoretically, how it works

The process involves in 4 steps:

  1. Separate the object into multiple parts. There are several ways to do this in Linux, ‘dd‘, ‘split‘, etc. We will use ‘dd’ in this tutorial,
  2. Initiate the multipart upload and receive an upload id in return (aws s3api create-multipart-upload),
  3. Upload each part (a contiguous portion of an object’s data) accompanied by the upload id and a part number (aws s3api upload-object),
  4. Finalize the upload by providing the upload id and the part number / ETag pairs for each part of the object (aws s3api complete-multipart-upload).

And practically?

1. Separate the object into multiple parts

We will create 205 parts (100 MB * 204 + 80 MB):

$ dd if=/dev/urandom bs=1024k count=20000

$ dd bs=1024k skip=0 count=100
$ dd bs=1024k skip=100 count=100
$ dd bs=1024k skip=200 count=100
$ dd bs=1024k skip=900 count=100
$ dd bs=1024k skip=1000 count=100
$ dd bs=1024k skip=1100 count=100
$ dd bs=1024k skip=20200 count=100
$ dd bs=1024k skip=20300 count=100
$ dd bs=1024k skip=20400 count=100

A one line shell script can be written to automate this process:

$ for i in {1..205}; do dd of=MyObject"$i".zip bs=1024k skip=$[i*100 - 100] count=100; done

2. Initiate the multipart upload and receive an upload id in return

aws s3api create-multipart-upload --bucket MyBucketName --key
You will received as output something like:
"UploadId": "UVditMTG8U--MyLongUploadId--ksmFT7N6bNTWD",
"Bucket": "MyBucketName",
"Key": ""

3. Upload each part

For the following commands, note the console output:
"ETag": "\"fggcd799--ETagValue1--dhe76dd8dc\""

$ aws s3api upload-part --bucket MyBucketName --key --upload-id \ MyLongUploadId --part-number 1 --body
$ aws s3api upload-part --bucket MyBucketName --key --upload-id \ MyLongUploadId --part-number 2 --body
$ aws s3api upload-part --bucket MyBucketName --key --upload-id \ MyLongUploadId --part-number 100 --body
$ aws s3api upload-part --bucket MyBucketName --key --upload-id \ MyLongUploadId --part-number 204 --body
$ aws s3api upload-part --bucket MyBucketName --key --upload-id \ MyLongUploadId --part-number 205 --body

Note: Once more you can write a small shell script to automate this process.

Finalize the upload

Create a JSON file MyMultiPartUpload.json containing the following:

"Parts": [
"ETag": "\"ETagValue1\"",
"PartNumber": 1
"ETag": "\"ETagValue2\"",
"PartNumber": 2
"ETag": "\"ETagValue100\"",
"PartNumber": 100
"ETag": "\"ETagValue204\"",
"PartNumber": 204
"ETag": "\"ETagValue205\"",
"PartNumber": 205

$ aws s3api complete-multipart-upload --bucket MyBucketName --key \ --upload-id MyLongUploadId --multipart-upload MyMultiPartUpload.json

That is all, you can verify that the large file is uploaded with:
aws s3 ls s3://MyBucketName/
2014-09-18 20:29:19 20495340

References and resources:

Solution de portail Intranet : Intégration de Liferay 6.1 et Alfresco 4.2 sous Ubuntu Server avec MySQL

Liferay est une solution aboutie Open Source de gestion de contenu, orientée portail social et collaboratif (internet, extranet ou intranet) ; plusieurs grandes entreprises et administrations l’ont utilisé pour déployer leur portail internet.

Lorsqu’il faut cependant mettre sur pied un portail intranet ou extranet, la fonctionnalité GED (gestion et archivage électronique de documents) est indispensable (partage de documents, sécurité, accès CIFS/FTP/Lecteur réseau, etc.), malgré les efforts des équipes de développement de Liferay dans l’ajout de cette fonctionnalité (notamment dans sa dernière version (6.1) avec l’apparition de Liferay Sync qui permet de synchroniser les documents distants avec un dossier local ou sur un mobile – un peu comme DropBox), les fonctionnalités de base d’une GED sont loin d’être remplies.

Heureusement Liferay a dans cette même version 6.1, facilité l’intégration de quelques systèmes de gestion documentaire, en particulier Alfresco, SharePoint et Documentum. La collaboration entre Liferay et Alfresco permet:

  • soit d’intégrer Alfresco à Liferay – Liferay joue ici un rôle de présentation des documents
  • soit de faire cohabiter les deux applications – toutes les données de Alfresco peuvent être vues dans Liferay, avec une gestion commune des droits d’accès.

Le but de ce tutoriel est de mettre sur pied la deuxième solution.
Alfresco Liferay Integration

Principales étapes

  1. Installation de Alfresco 4.2.c sur Ubuntu Server 12.04 avec MySQL

    Suivre ce tutoriel. Il est préférable d’installer le serveur Tomcat à part, et d’utiliser un port différent que 8080 (utiliser par exemple 8081) pour Alfresco.

  2. Installation de Liferay 6.1 sur Ubuntu Server 12.04 avec MySQL

    Suivre ce tutoriel

  3. Démarrer Alfresco (service alfresco start)
  4. Le couple (utilisateur, mot de passe) doit être le même à Alfresco et Liferay, Alfresco étant prioritaire. Par défaut Liferay ne sauvegarde pas les mots de passe en session, il faut l’activer dans le fichier des propriétés – Modifier ce fichier comme suit :
  5. Démarrer Liferay (service liferay start)
  6. Ouvrir Liferay dans votre navigateur favori et se connecter comme administrateur, puis aller sur le panneau d’administration.
  7. Cliquer sur Documents et médias puis ajouter un nouveau dépôt (voir Image 1 ci-dessous). Vous ajoutez ici les détails de connexion à Alfresco comme suit:
    • Nom: Donner un nom au dépôt (Documents Alfresco par exemple)
    • Description: Une brève description du dépôt
    • Type de dépôt: Vous pouvez choisir Services Web ou AtomPub (que nous utliserons)
    • AtomPub URL: http://localhost:8081/alfresco/cmisatom
    • ID du dépôt: paramètre optionnel, important si nous avons plusieurs dépôts. Si rien n’est mis le système choisira le 1er dépôt
    • Cliquer sur Enregistrer. Liferay essaiera de se connecter à Alfresco avec les paramètres entrés et affichera le résultat (rassurez-vous que Alfresco et bien démarré et fonctionnel). En cas de succès vous verrez s’afficher tous les dossiers et fichiers d’Alfresco dans la partie Documents et médias de Liferay (Image 2 ci-dessous)

Tous les documents ajoutés à Liferay seront visibles dans Alfresco, et vice versa, tout est synchronisé, les droits d’accès sont conservés. Elle n’est pas belle la vie???

Nouveau Depot
Paramètres de configuration de la connexion de Liferay à Alfresco
Alfresco Dans Liferay
Documents présents à la fois dans Alfresco et dans Liferay

Installation du PGI OpenERP 7.0 sur Debian Squeeze et CentOS 6.4

OpenERP est un PGI (Progiciel de Gestion Intégré) Open Source dont le développement a commencé il y a maintenant 14 ans par Fabien Pinckaers (pinky) pour les business de son père, et plus tard de son oncle.
Développé avec Python et utilisant le SGBD libre PostGreSQL, le logiciel est aujourd’hui très abouti et plusieurs grandes entreprises l’ont implémenté; il peut très facilement être installé et configuré sur plusieurs distributions de Linux, dont Debian.

Les principaux modules présents sont la gestion:

  • des ventes,
  • de la gestion de relation client (CRM),
  • de projet,
  • d’entrepôt,
  • de la production,
  • de la comptabilité et
  • des ressources humaines.

Processus d’installation sur Debian Squeeze

Mettre à jour les paquets

sudo apt-get update

Installer la base de données PostGreSQL

sudo apt-get install postgresql

Créer un utilisateur pour la base de données

su - postgres -c "createuser --createdb --no-createrole --no-superuser openerp"
echo "ALTER USER openerp WITH PASSWORD 'MonMotDePasse';ALTER USER postgres WITH PASSWORD 'MonMotDePasse';" | su postgres -c psql

Ajouter le dépôt OpenERP

echo "deb ./" >> /etc/apt/sources.list

Mettre à jour la base de données des paquets

aptitude update && aptitude && upgrade && aptitude clean

Installer OpenERP

aptitude install openerp
Accepter le Package en tapant ‘Yes’ et en validant sur le clavier, toutes les dépendances seront installées.

Accéder à OpenERP via le navigateur

Installation sur CentOS 6.3 ou 6.4

L’installation ici est moins automatique que sous Debian, heureusement dans le domaine des logiciels libres le partage d’expérience d’expertise nous permet d’avancer relativement vite! Je vous conseille ce simple script de Mario Gielissen ( :

# Modified script from Carlos E. Fonseca Zorrilla
yum -y install wget unzip
rpm -ivh
rpm -ivh
yum -y install python-psycopg2 python-lxml PyXML python-setuptools libxslt-python pytz \
python-matplotlib python-babel python-mako python-dateutil python-psycopg2 \
pychart pydot python-reportlab python-devel python-imaging python-vobject \
hippo-canvas-python mx python-gdata python-ldap python-openid \
python-werkzeug python-vatnumber pygtk2 glade3 pydot python-dateutil \
python-matplotlib pygtk2 glade3 pydot python-dateutil python-matplotlib \
python python-devel python-psutil python-docutils make\
automake gcc gcc-c++ kernel-devel byacc flashplugin-nonfree poppler-utils pywebdav\
yum -y install postgresql92-libs postgresql92-server postgresql92
service postgresql-9.2 initdb
chkconfig postgresql-9.2 on
service postgresql-9.2 start
su - postgres -c "createuser --superuser openerp"
cd /tmp
rm -rf
cd gdata*
python install
cd /tmp
adduser openerp
DIR="/var/run/openerp /var/log/openerp"
for NAME in $DIR
if [ ! -d $NAME ]; then
mkdir $NAME
chown openerp.openerp $NAME
rm -rf openerp*
tar -zxvf openerp-7.0-latest.tar.gz --transform 's!^[^/]\+\($\|/\)!openerp\1!'
cd openerp
python install
rm -rf /usr/local/bin/openerp-server
cp openerp-server /usr/local/bin
cp install/openerp-server.init /etc/init.d/openerp
cp install/openerp-server.conf /etc
chown openerp:openerp /etc/openerp-server.conf
chmod u+x /etc/init.d/openerp
chkconfig openerp on
service openerp start

Voilà, c’est tout! Profitez de toutes les fonctionnalités de l’application!