Apprendre python : coder une Food app en 3 mois (sunday clone python)

Apprendre à programmer n'est jamais facile pour les débutants. Il faut souvent du temps pour trouver le bon tutoriel, les meilleurs cours, etc.

Apprendre Python (Django) : coder une Food app en 3 mois (Sunday clone)
Apprendre Python (Django) : coder une Food app en 3 mois (Sunday clone)

Apprendre à programmer n'est jamais facile pour les débutants. Il faut souvent du temps pour trouver le bon tutoriel, les meilleurs cours, etc. Les démarrages sont souvent lents et compliqués. Souvent décriés, au bénéfice des systèmes de gestion de contenu ou solution no-code, les langages de programmation évitent pourtant de nombreuses limites. Ils permettent d'avoir la main sur un projet. De coder en dehors d'un cadre imposé. Que tu souhaites programmer un jeu, programmer une app avec python, ou encore apprendre un nouveau langage (Java, Langage C et C ++, Javascript, ...), tu es au bon endroit.

De mon objectif de réouvrir les restaurants au développement de mon application python... Je te partage ici mon expérience en programmation, les étapes que j'ai franchies et les conseils que j'aurai aimé entendre avant de me lancer... Un bon moyen pour toi, programmeur ou futur développeur, de découvrir la programmation dans un but commercial.


Sommaire :

  1. Apprendre Python avec une Food app : réouvrir les restaurants (mars 2020)
  2. Apprendre Python avec une Food app : code et développement avec Django

Adayto (Agence SEO) - Votre business dans le top 3 des résultats Google
Adayto (Agence SEO) - Votre business dans le top 3 des résultats Google

Apprendre Python avec une Food app : réouvrir les restaurants pendant le confinement (mars 2020)

Contexte :

2020. Je suis en école de commerce, tout se passe bien. Je travaille beaucoup sur ma première boîte. Entre 10 et 14h par jours. Quelques informations concernant un virus chinois commencent à émerger sur la toile. Mais tout cela semble pourtant lointain. Les semaines passent, le virus se rapproche. Je sens petit à petit l'angoisse se propager. En France, les premiers cas de COVID 19 se manifestent.

Le lundi 16 mars 2020 marque un avant et un après dans la société française. Nous sommes confinés. Les images que je vois autour de moi me bouleversent. De nombreux restaurants vont devoir fermer, et parfois même définitivement. Comme de nombreux métiers physiques d'ailleurs. Moi, j'ai du temps devant moi. Je ne sais pas combien de temps le confinement va durer. Mais une chose est certaine : il va durer...

Dans ma résidence les conséquences sont immédiates. De nombreux étudiants n'ont plus de travail. Certains rentrent chez eux, et même parfois pire arrêtent leur étude définitivement. Du jour au lendemain, les petits boulots pour payer une chambre ou un appartement n'existent plus... C'est la panique. Les jeunes sont touchés de plein fouet.

La situation me touche particulièrement. Je décide donc de faire une pause dans le développement commercial de ma première entreprise. Pour me rendre utile à la société. Je vais lancer Feedcool.

  • Une app qui respecte les gestes barrières.
  • Une app qui peut révolutionner la restauration.
  • Une app qui permet de garder les restaurants ouverts.
  • Une app qui est un nouveau défi pour moi dans l'apprentissage du développement web.
  • Une app qui permet de commander et de payer au restaurant avec son téléphone portable.

Je décide de me lancer. D'apprendre à coder en Python. À un cœur vaillant rien n'est impossible. Commencer à programmer avec le Framework Django facilite beaucoup les choses. Le choix du langage de programmation python a été influencé par mon pote Lucas. Il développait au même moment une app de géolocalisation d'image, Hatlastravel. Et grâce à son expertise, je pouvais avancer plus vite. Autre avantage, python est un langage facile à programmer.

Vendre le matin et coder la journée :

Il me fallait un cours de Python pour commencer. Pour apprendre les bases. J'ai regardé Youtube. Mais aucune vidéo n'avait vraiment de fil directeur. Il s'agissait plus de vidéos thématiques. Pas vraiment utile pour débuter dans le code, car beaucoup trop spécifique. Mais cela s'est avéré pratique par la suite. Je passais pas mal de temps sur Coursera. À apprendre des cours sur la négociation, sur la philosophie, ou encore la psychologie. Il existait forcément l'équivalent "pratique" en développement web. Après quelques jours de recherche, j'ai trouvé Udemy. Je pouvais apprendre les bases de la programmation et facilement. Même s'il est vrai que j'avais déjà des notions de programmation (Php & Mysql, bases de données, HTML & Css). En clair, j'avais déjà la logique de programmation...

Un cours en particulier attire mon attention. Il est plutôt bien noté. Je vais pouvoir facilement me concentrer sur mon projet. Je décide de m'y mettre pour seulement 14,99 €. C'est parti pour 30 heures de cours. Je lance directement Feedcool sur un serveur web partagé (Shared Web Hosting) proposé par O2swtich. Le service client a été au petit oignon avec moi. Notamment pour l'installation de Django sur le serveur. Mon site ne sera donc pas lancé en local. Il doit être prêt, accessible et visible par tout le monde. Ce sera du build in public.

Je suis le cours avec minutie et reproduis chaque étape (modèle de programmation) en l'adaptant à Feedcool. Ces trois mois d'apprentissage sont faits de nuits blanches, de migraines, de questionnements sans fins, de petits grades récupérés sur Stackoverflow etc... Une chose est sûre : apprendre le code n'est pas de tout repos.

C'est même très éprouvant. Mais je progresse vite. Je suis capable :

  • D'écrire des lignes de programmation à toute vitesse.
  • De me repérer rapidement dans le code-source.
  • D'utiliser une bibliothèque python.
  • De faire mes premiers tests en machine-learning.
  • D’intégrer mes premières API en Intelligence artificielle (Deep Learning).
  • D'utiliser les lignes de commande du Terminal et Shell.

Bref, je me sens comme informaticien dans un environnement de développement.

  • Mon premier constat est que la technique nécessaire pour un tel développement est disponible sur internet. La difficulté réside dans le fait de devoir la trouver.
  • Mon deuxième constat est que le métier de développeur est difficile. Il demande de nombreux sacrifices. Rester des heures devant un ordinateur sans bouger. Pour moi qui suis plutôt très actif, cette expérience n'a pas été facile. Le confinement n'aidant pas, j'ai pris 10kg.

Si j'étais le créateur de mon projet, je devais aussi être le commercial de celui-ci. J'ai donc ouvert l'annuaire et j'ai cherché les restaurants de la région lyonnaise. Je les ai appelés un à un. Le matin, de préférence, pour éviter le rush du click and collect à midi. À ma grande surprise, le développement commercial s'est avéré très très compliqué.

  • Mon troisième constat est que les métiers traditionnels ont horreur du changement. L'innovation n'est pas vécue avec enthousiasme, loin de là. La digitalisation rime souvent avec absence de lien social ou remplacement du métier.

Difficultés :

Mon professeur d'Histoire Antique, Francois Lefèvre, nous parlait souvent de l'importance du kaïros. Le moment opportun. Faire le bon acte au bon moment participe au kaïros. Dans le milieu des start-up et de l'innovation rien ne sert d'arriver le premier, il faut simplement arriver au bon moment. Ce genre de chose ne s'explique pas. Souvent, il faut sentir.

  • Réception des restaurateurs : dans l'ensemble très compliqué. 60% à 80% des restaurants affichaient un "non" catégorique. Pour ne pas dire qu'ils me faisaient sortir aussi rapidement que j'avais franchi le pas de la porte. 15% étaient complètement désabusés par la situation. Et seulement 5% s'intéressaient à mon projet. Posaient des questions et se projetaient dans cette innovation. Au début du projet une vingtaine de restaurants avait donné son accord pour essayer Feedcool. La première leçon de cette expérience est qu'on ne peut pas plaire à tout le monde. Qu'il faut accepter la virulence de certains et s'en servir pour comprendre les craintes et les angoisses du milieu visé.
  • Réception des professeurs de l'EM Lyon : dans l'ensemble très critique. Mais bon pour être honnête, ça n'avait pas beaucoup d'importance. Je n'ai jamais vraiment compris la légitimité d'un prof d'entrepreneuriat à enseigner un domaine qu'il n'a jamais pratiqué.
  • Réception des organes dirigeants de la France : je ne peux pas m'exprimer sur le sujet car je n'ai jamais eu de réponse concernant mes multiples relances.

À "il faut innover" souvent répétée dans l'entrepreneuriat, je te répondrai que l'innovation n'est pas toujours facile à faire intégrer. Chaque mouvement d'innovation créé des vagues de résistances très fortes.

Syndrome du développeur :

Expérimenter quelques mois de développement m'a permis de comprendre ce que pouvait être le syndrome du développeur. Le risque de s'enfermer et de développer un produit pour soit. Le risque de se couper de la finalité de son produit. De se perdre dans ses lignes de code, dans son terminal.

Un produit doit être utilisé par des clients qui sont prêts à payer. Voilà pourquoi, je m'efforçais de vendre tous les jours un produit non terminé. Un produit que j'ai tenté de construire avec les 5% des restaurateurs favorables. Mais également avec les 80% mécontents. Sans le savoir, ils m'aidaient beaucoup. En m'expliquant que j'étais "teubé", ils me donnaient toutes "leurs explications"... Je n'avais plus qu'à écouter sagement et prendre des notes. Aucun consultant ne pourrait remplacer la valeur de ce genre de discussion.

L'avenir de la restauration :

La data sera l'avenir de la restauration. Voilà mon avis sur la question. Un téléphone portable dans un restaurant est un instrument de mesure bien plus efficace qu'un être humain. L'être humain, en société, se conduit en fonction de ce qui l'environne. Ses choix ne sont pas totalement indépendants. Le téléphone portable lui permet de se couper de toutes pressions possibles. Puisque l'on commande face à son portable, et seulement face à lui.

Tout le monde utilise son portable aujourd'hui. Une personne lambda passe presque 4h par jour dessus. Mais cela reste encore peu "convenu" d'utiliser un portable au restaurant.

Pourtant cela arrange les affaires du consommateur, qui n'attend plus pour payer l'addition, et du restaurateur qui gagne beaucoup de temps - en rotation de table, commande et paiement.

Une app de commande et de paiement au restaurant permet également de développer des services annexes :

  • une gestion de satisfaction,
  • carte de commande sur-mesure,
  • un système de comptabilité en un clic,
  • analyse poussée des comportements clients,

Tout l'enjeu réside dans l'intégration du téléphone portable au sein des restaurants. Le téléphone n'a pas pour conséquence de couper les liens sociaux.

L'exemple de Sunday :

Lorsque j'ai contacté Jean de la Rochebrochard, bras droit de Xavier Niels et du fonds d'investissement Kima Ventures, il était trop tard. Sunday avait déjà levé plusieurs millions d'euros auprès de Xavier Niels.

Ils avaient pu expérimenter leur modèle dans la chaîne de restaurants Big Mamma. Qu'ils avaient créé il y a quelques années. Sunday avait pu construire un produit avec ses utilisateurs. Un choix gagnant. Raison pour laquelle ils ont développé une approche moins radicale concernant le rôle des serveurs. Avec Sunday, le serveur apporte un QR Code pour commander et payer. Feedcool proposait clairement de diviser par deux le nombre de serveur.

Les dirigeants de Sunday ont une très grande "assise" dans le domaine de la restauration. Ce qui facilite le déploiement commercial. Malgré des débuts compliqués en acquisition, Sunday a pu se lancer en Europe, en Angleterre et au États-unis.

  • Mon quatrième constat est que la tech ne fait pas tout. Je pense que le plus important réside dans la manière de vendre. Les gens ne trouvent pas d'intérêts dans une approche technique, seulement dans les bénéfices qu'ils vont pouvoir tirer. Et se reconnaissent dans une histoire racontée.
  • Mon cinquième constat est que créer et vendre en même temps est une tâche très compliquée. Le créateur voit souvent son app au travers de ses défauts. Il est donc beaucoup plus craintif pour vendre.

Ma première expérience avec un fonds d'investissement américain :

Rédaction en cours...

Feedcool — 🍕 🍔 🍣 Au resto... commandez et payez avec Feedcool.
🍕 🍔 🍣 Feedcool propose un service de réservation, de commande et paiement au restaurant. N’attendez plus le serveur, commandez depuis votre chaise avec Feedcool.
Résultat de la Food App développée avec Django (Python)

"Créer et vendre est une tâche compliquée. Le créateur voit souvent son app par ses défauts. Il est donc beaucoup plus craintif pour vendre"

Apprendre Python avec une Food app : code et développement avec Django

Étant débutant, mon apprentissage de la programmation s'est fait par strate. J'ai donc modifié à plusieurs reprises mes modèles Django. Ils sont donc imparfaits. Programmer avec python n'a pas toujours été facile. Cependant cette expérience m'a permis d'acquérir de grandes connaissances en programmation. Je vous conseille de faire la même chose. Pour savoir programmer, il faut programmer. Apprendre par la pratique en clair. Voilà comment j'ai procédé dans les grandes lignes.

Django : créer le model UserProfile

  1. Première étape : créer un slug aléatoire
  2. Deuxième étape : créer un UserProfile
  3. Deuxième étape : trouver un système de Géolocalisation
  4. Deuxième étape : créer un SellerProfile et un CustomerProfile

J’ai immédiatement pensé à un UserProfile public. Afin que chaque membre ait une page publique. La première chose a donc été de coder un générateur unique de slug. Afin d'éviter le doublon des slugs. Pour cela j'ai utilisé deux fonctions "random_string_generator" et "unique_slugify". L'objectif étant de voir si le slug existe déjà. Si c'est le cas, alors il sera changé. Si ce n'est pas le cas, alors il reste inchangé. L'objectif était bien d'aborder la programmation sous un angle concret.

On récupère des informations basiques via le model User classique de Django. On lie User à UserProfile dans la base de données. Pour cela, on utilise le champ "OneToOneField", soit Relations un-à-un. En ce sens qu'un utilisateur a uniquement un UserProfile.

Il était nécessaire d'obtenir d'autres informations, telles que :

  • un avatar, ainsi qu'une image par défaut. Pour chaque image déposée, on vient créer un thumbnail. Le format de l'image est redéfini. Ici il s'agit du format 200 par 200 px.

Enfin la géolocalisation, ce qu'il y a de plus important pour cette app. Car nous souhaitons afficher les restaurants en fonction de l'adresse de l'utilisateur. Pour cela nous devons convertir l'adresse en coordonnées GPS. Nous utilisons l'API de OpenCageGeocode. En envoyant l'adresse, l'API nous renvoie toutes les coordonnées dont nous avons besoin (latitude, longitude, ville, etc.). Grâce à cette information, les résultats pourront s'afficher en fonction de la position de l'utilisateur.

Autre point, notre app est construite sous la forme de market place. Ce qui signifie que chaque compte doit être lié avec un compte Stripe. Il est donc nécessaire de créer un autre modèle Profile, le CustomerProfile. Pour celui-ci l'utilisateur n'a rien à faire. Il est créé automatiquement... Et si l'utilisateur devient "Seller", alors il devra remplir manuellement les informations d'un nouveau SellerProfile Stripe.

Les avantages et inconvénients :

⚠️
L'utilisateur doit fournir une adresse manuellement. Le restaurateur doit rentrer sur Stripe ses informations bancaires (pour la validation du compte).
👑
Les résultats de recherche apparaissent du plus près ou plus loin.

Le code :

#models.py


# GEO KEY
key = 'XXXXXXXXXXXXXXXXXXXXX'
geocoder = OpenCageGeocode(key)

# RANDOM SLUG
def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))

def unique_slugify(instance, slug):
    model = instance.__class__
    unique_slug = slug
    while model.objects.filter(slug=unique_slug).exists():
        unique_slug = slug
        unique_slug += random_string_generator(size=4)
    return unique_slug

# UserProfile    
class UserProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
    image = models.ImageField(default='user/user-30.png',upload_to='user/',null=True,blank=True)
    full_address = models.CharField(max_length=128)
    image_url = models.URLField(max_length=300,null=True,blank=True)
    is_certified = models.BooleanField(default=False)
    is_seller = models.BooleanField(default=False)
    #API
    latitude = models.DecimalField(max_digits=9, decimal_places=6, blank=True, default='0')
    longitude = models.DecimalField(max_digits=9, decimal_places=6, blank=True, default='0')
    town = models.CharField(editable=False,null=True,blank=True,max_length=60)
    zipcode = models.CharField(editable=False,null=True,blank=True,max_length=20)
    state = models.CharField(editable=False,null=True,blank=True,max_length=60)
    country = models.CharField(editable=False,null=True,blank=True,max_length=60)
    country_code = models.CharField(editable=False,null=True,blank=True,max_length=10)
    currency_code = models.CharField(editable=False,null=True,blank=True,max_length=10)
    timezone = models.CharField(editable=False,null=True,blank=True,max_length=10)
    slug = models.SlugField(editable=False, unique=True)
    created_on = models.DateTimeField(auto_now_add=True,null=True,blank=True)
    
    def save(self, *args,**kwargs):
        if not self.slug:
            self.slug = unique_slugify(self, slugify(self.user.username))
        
        if self.full_address:
            result = geocoder.geocode(self.full_address)

            if result and len(result):
                try:
                    self.longitude = result[0]['geometry']['lng']
                except Exception as e:
                    self.longitude = None
                try:
                    self.latitude  = result[0]['geometry']['lat']
                except Exception as e:
                    self.latitude = None
                try:
                    self.zipcode  = result[0]['components']['postcode']
                except Exception as e:
                    self.zipcode = None
                try:
                    self.town  = result[0]['components']['municipality']
                except Exception as e:
                    self.town = None
                try:
                    self.state  = result[0]['components']['state']
                except Exception as e:
                    self.state = None
                try:
                    self.country  = result[0]['components']['country']
                except Exception as e:
                    self.country = None
                try:
                    self.country_code = result[0]['components']['country_code']
                except Exception as e:
                    self.country_code = None
                try:
                    self.currency_code = result[0]['annotations']['currency']['iso_code']
                except Exception as e:
                    self.currency_code = None
                try:
                    self.timezone  = result[0]['annotations']['timezone']['offset_string']
                except Exception as e:
                    self.timezone = None
                    
        super(UserProfile, self).save(*args, **kwargs)
        
        img = Image.open(self.image.path)
        if img.height > 200 or img.width > 200:
            new_size = (200, 200)
            img.thumbnail(new_size)
            img.save(self.image.path)
    
    def __str__(self):
        return self.user.username  
    
        

# SAVE AUTOMATIC USERPROFILE        
def create_user_profile(sender,instance,created,**kwargs):
    if created:
        UserProfile.objects.create(user=instance)
        
post_save.connect(create_user_profile,sender=settings.AUTH_USER_MODEL)



# STRIPE ACCOUNT
class CustomerProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,primary_key=True)
    stripe_customer_id = models.CharField(max_length=120)
    
    def __str__(self):
        return self.user.username


# SAVE AUTOMATIC STRIPE ACCOUNT      
@receiver(post_save, sender=User)
def _on_update_user(sender, instance, created, **kwargs):
    if created: 

        customer = stripe.Customer.create(
            email=instance.email,
            name=instance.get_full_name(),
            metadata={
                'user_id': instance.pk,
                'username': instance.username
            },
            description='Créé via Feedcool',
        )

        profile = CustomerProfile.objects.create(user=instance, stripe_customer_id=customer.id)
        profile.save()
Comment créer un model UserProfile avec Django en Python ?

FAQ :

C'est quoi un slug ?

Un slug est une représentation simplifiée d'une ressource (utilisateur, article, page, etc.) et dont le format lui permet d'être passé en paramètre d'une URL tout comme un identifiant. Par exemple, dans cette URL ("https://feedcool.fr/restaurant/letoile/"), le slug du restaurant est "letoile".


Django : créer le model Repas

  1. Première étape : créer des catégories
  2. Deuxième étape : automatiser les catégories
  3. Troisième étape : créer le modèle de repas

J'ai créé différentes catégories pour classer les Repas en fonction :

  • d'une liste d'aliment,
  • d'un type (entrée, plat, dessert, etc.),
  • et d'une orientation (vegan, végétarien, allergies, etc.).

Les catégories "type" et "orientation" sont remplies manuellement dans la création du modèle Repas. En revanche, la liste d'aliments est le résultat d'une requête API en Machine Learning. En ce sens que l'API renvoie une liste à enregistrer dans les champs de la base de données prévue pour ça. Si les champs existent déjà, alors il est simplement lié. S'il n'existe pas, alors il est créé et lié.

AI-Driven Food Model | Food AI For Recognition | Clarifai
Use Clarifai’s AI-driven Food Detection Model in your wellness-related app to recognize over 1,000 different food items, right down to an ingredient level.
API utilisé pour le Food Recognition

Je n'étais pas totalement fan de "l'accuracy". Mais disons que l'IA faisait le boulot à 90%. Elle était là pour aider à programmer quelque chose d'intéressant. Quelque chose qui facilite la recherche des Repas. La difficulté résidait dans le fait qu'un plat transforme naturellement les aliments. Par conséquent, l'IA avait du mal à détecter tous les aliments, en inventait ou en oubliait parfois.

J'ai pensé créer un Nutri-Score en fonction des aliments détectés par l'IA. Mais la marche d'erreur était trop importante. Imaginez une seule seconde que la pâte d'une tarte soit assimilée avec du beurre... Par conséquent, j'ai décidé d'utiliser un autre système d'API qui matchait avec des recettes plus ou moins similaires. Ce système me permettait d'obtenir un Nutri-Score. Cette méthode était moins biaisée que la première.

Il y a là aussi une dimension de géolocalisation, comme pour le modèle UserProfile. La géolocalisation était le résultat des données du modèle Restaurant. En ce sens que chaque plat émanait d'un restaurant. Pour agrémenter les données de localisation, les exifs des photos du Repas pouvaient être utilisés. J'ai trouvé très peu de tutoriels sur les exifs. Et cela m'a demandé beaucoup de travail.

💡
À ce moment-là ma connaissance en programmation avait clairement augmenté. Différentes bibliothèques ont également été utilisées.
#models.py

# TYPE
class Typerepas(models.Model):
    name = models.CharField(max_length=20, unique=True)
    image_url = models.URLField(max_length=200,blank=True)
    slug = models.SlugField(editable=False)
    status = models.IntegerField(choices=STATUS, default=1)     
    
    def save(self, *args,**kwargs):
        if not self.slug:
            self.slug = unique_slugify(self, slugify(self.name))
        super(Typerepas, self).save(*args, **kwargs)  
    
    def __str__(self):
        return self.name

# ALIMENT
class ListAliments(models.Model):
    name = models.CharField(max_length=40, unique=True)
    image_url = models.URLField(max_length=200,blank=True)
    strength = models.CharField(max_length=100,unique=False,blank=True,null=True)
    slug = models.SlugField(editable=False)
    status = models.IntegerField(choices=STATUS, default=1)     
    
    def save(self, *args,**kwargs):
        if not self.slug:
            self.slug = unique_slugify(self, slugify(self.name))
        super(ListAliments, self).save(*args, **kwargs)  
    
    def __str__(self):
        return self.name

# ORIENTATION
class Sperepas(models.Model):
    name = models.CharField(max_length=40, unique=True)
    image_url = models.URLField(max_length=200,blank=True)
    slug = models.SlugField(editable=False)
    status = models.IntegerField(choices=STATUS, default=1)     
    
    def save(self, *args,**kwargs):
        if not self.slug:
            self.slug = unique_slugify(self, slugify(self.name))
        super(Sperepas, self).save(*args, **kwargs)  
    
    def __str__(self):
        return self.name


# PROMO
class Promo(models.TextChoices):
    UN_ACHETE_UN_OFFERT = '1 acheté = 1 offert'
    UN_ACHETE_UN_CADEAU_OFFERT = '1 acheté = 1 cadeau offert'
    
# REPAS
class Cuisine(models.Model):
    # MANU
    title = models.CharField(max_length=90)
    user = models.ForeignKey(User,on_delete=models.CASCADE,related_name='cuisine_user')
    image = models.ImageField(upload_to='nutriscore/')
    description = models.TextField(max_length=1000, blank=True)
    prix = models.DecimalField(max_digits=4,decimal_places=2)
    a_la_carte = models.BooleanField(default=True)
    # PROMO
    discount_prix = models.DecimalField(max_digits=4, decimal_places=2,blank=True,null=True)
    # CLIC & CO
    clic_and_collect = models.BooleanField(default=False)
    is_delivered = models.BooleanField(default=False)
    start_time = models.TimeField(blank=True,null=True)
    deadline_time = models.TimeField(blank=True,null=True)
    start_date = models.DateField(blank=True,null=True)
    quantity = models.IntegerField(default=1)
    # PROMO 
    is_promoted = models.BooleanField(default=False)
    promotion = models.CharField(max_length=50,choices=Promo.choices,default=Promo.UN_ACHETE_UN_OFFERT,)
    # LOCATION
    latitude_user = models.DecimalField(max_digits=9, decimal_places=6, blank=True, default='0')
    longitude_user = models.DecimalField(max_digits=9, decimal_places=6, blank=True, default='0')
    town = models.TextField(max_length=200, blank=True)
    # EXIF
    latitude = models.DecimalField(max_digits=9, decimal_places=6, blank=True, default='0')
    longitude = models.DecimalField(max_digits=9, decimal_places=6, blank=True, default='0')
    date = models.CharField(max_length=200, blank=True)
    software = models.CharField(max_length=200, blank=True)
    lensmodel = models.CharField(max_length=200, blank=True)
    make = models.CharField(max_length=200, blank=True)
    model = models.CharField(max_length=200, blank=True)
    subectlocation = models.CharField(max_length=200, blank=True)
    gpsinfo = models.CharField(max_length=1000, blank=True)
    metadata = models.TextField(max_length=1000, blank=True)
    # API
    score = models.CharField(max_length=1,null=True,blank=True)
    aliments = models.ManyToManyField('ListAliments',blank=True, related_name='listaliments_cuisine') #list des aliments
    # MAN
    typederepas = models.ManyToManyField('Typerepas',blank=True, related_name='typederepas_cuisine') #Déjeuner, Diner, Petit-Dej
    specificitederepas = models.ManyToManyField('Sperepas',blank=True, related_name='specificitederepas_cuisine') #Vegan
    # DATA
    publishing_date = models.DateField(auto_now_add=True)
    created_on = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    status = models.IntegerField(choices=STATUS, default=0)
    slug = models.SlugField(max_length=200, unique=True)
    
    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = unique_slugify(self, slugify(self.title))
            super(Cuisine, self).save(*args, **kwargs)
        
        # EXIF
        
        path = self.image.path
        img = Image.open(path)
        
        # CLASSIQUE
        exif_data = {}
        for tag, value in img.getexif().items():
            if tag in TAGS:
                exif_data[TAGS[tag]] = value
        
        # GPS
        def get_geotagging(exifgps):
        
            geotagging = {}
            for (idx, tag) in TAGS.items():
                if tag == 'GPSInfo':
                    if idx not in exifgps:
                        raise ValueError("No EXIF geotagging found")
        
                    for (key, val) in GPSTAGS.items():
                        if key in exifgps[idx]:
                            geotagging[val] = exifgps[idx][key]
        
            return geotagging
        
        # FORMAT GPS    
        def get_decimal_from_dms(dms, ref):

            degrees = dms[0]
            minutes = dms[1] / 60.0
            seconds = dms[2] / 3600.0
        
            if ref in ['S', 'W']:
                degrees = -degrees
                minutes = -minutes
                seconds = -seconds
        
            return round(degrees + minutes + seconds, 5)
        # GPS    
        def get_lat_coordinates(geotags):
                lat = get_decimal_from_dms(geotags['GPSLatitude'], geotags['GPSLatitudeRef'])
                return lat
        
        # GPS           
        def get_lon_coordinates(geotags):
                lon = get_decimal_from_dms(geotags['GPSLongitude'], geotags['GPSLongitudeRef'])
                return lon
        
        if 'DateTime' in exif_data:
            self.date = exif_data['DateTime']
            
        if 'Software' in exif_data:
            self.software = exif_data['Software']
            
        if 'LensModel' in exif_data:
            self.lensmodel = exif_data['LensModel']
            
        if 'Artist' in exif_data:
            self.artist = exif_data['Artist']
            
        if 'Make' in exif_data:
            self.make = exif_data['Make']
            
        if 'Model' in exif_data:
            self.model = exif_data['Model']
            
        if 'SubjectLocation' in exif_data:
            self.subectlocation = exif_data['SubjectLocation']
            
        if 'GPSInfo' in exif_data:
            try:
                exifgps = img.getexif()
                geotags = get_geotagging(exifgps)
                self.gpsinfo = geotags
            except Exception as e:
                pass
            
            try:
                exifgps = img.getexif()
                geotags = get_geotagging(exifgps)
                self.latitude = get_lat_coordinates(geotags)
            except Exception as e:
                pass
            
            try:
                exifgps = img.getexif()
                geotags = get_geotagging(exifgps)
                self.longitude = get_lon_coordinates(geotags)
            except Exception as e:
                pass
        
        self.metadata = exif_data
        
        
        if self.image:
            request = service_pb2.PostModelOutputsRequest(
                model_id='',
                inputs=[
                    resources_pb2.Input(data=resources_pb2.Data(image=resources_pb2.Image(url="https://feedcool.fr" + self.image.url)))
                ])
            response = stub.PostModelOutputs(request, metadata=metadata_cla)
            
            if response:
                names = []
                for concept in response.outputs[0].data.concepts:
                    current_aliments = ListAliments.objects.filter(name=concept.name)
                    current_post = get_object_or_404(Cuisine, slug=self.slug)
                    if current_aliments.count()<1:
                        create_aliments = self.aliments.create(name=concept.name)
                        current_post.aliments.add(create_aliments)
                    else:
                        existed_aliments = ListAliments.objects.get(name=concept.name)
                        current_post.aliments.add(existed_aliments)
        
        super(Cuisine, self).save(*args, **kwargs)
    
    class Meta:
        ordering = ['-created_on']
    
    def __str__(self):
        return self.title
        
    def distance_post(self, request):
        post_situation = (self.longitude_user, self.latitude_user)
        user_situation = (self.request.user.userprofile.longitude, self.request.user.userprofile.latitude)
        
        return geodesic(post_situation, user_situation).km
        
    # NB VENTE  
    def post_sold(self):
        return self.order_post.filter(is_paid=1).distinct().count()
    
    # NB VENTE LIVREE
    def post_vente_livre(self):
        return self.order_post.filter(is_paid=1).filter(is_received=1).distinct().count()
    
    # NB VENTE A LIVRER
    def post_vente_a_livrer(self):
        return self.order_post.filter(is_paid=1).filter(is_received=0).distinct().count()
Comment créer un model Repas avec Django en Python ?

C'est quoi l'accuracy ?

L'accuracy est une métrique pour évaluer la performance des modèles de classification.


Django : créer le model Restaurant

#models.py

# Restaurant QR Code
class Restaurant(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
    resto_id_table = models.CharField(blank=True, null=True, max_length=6, unique=True)
    full_address = models.CharField(max_length=128,blank=True, null=True)
    image = models.ImageField(upload_to='restaurant/',null=True,blank=True)
    name = models.CharField(max_length=100, unique=True, blank=True, null=True)
    place_id = models.CharField(max_length=100, unique=True, blank=True, null=True)
    qr_code = models.ImageField(upload_to='qr_codes', blank=True)
    # Table Qrcode
    qr_code_table = models.ManyToManyField('TableQrcode',blank=True, related_name='restaurant_tableqrcode')
    #API Google
    capacity_max = models.IntegerField(blank=True, null=True)
    nb_table = models.IntegerField(blank=True, null=True)
    open_time = models.TimeField(blank=True, null=True)
    closed_time = models.TimeField(blank=True, null=True)
    days_closed = models.CharField(max_length=150,choices=Closed_days.choices,blank=True, null=True)
    # User Location
    latitude_user = models.DecimalField(max_digits=9, decimal_places=6, blank=True, default='0')
    longitude_user = models.DecimalField(max_digits=9, decimal_places=6, blank=True, default='0')
    #API
    restaurant_latitude = models.DecimalField(max_digits=9, decimal_places=6, blank=True, default='0')
    restaurant_longitude = models.DecimalField(max_digits=9, decimal_places=6, blank=True, default='0')
    town = models.CharField(editable=False,null=True,blank=True,max_length=60)
    zipcode = models.CharField(editable=False,null=True,blank=True,max_length=20)
    state = models.CharField(editable=False,null=True,blank=True,max_length=60)
    country = models.CharField(editable=False,null=True,blank=True,max_length=60)
    country_code = models.CharField(editable=False,null=True,blank=True,max_length=10)
    currency_code = models.CharField(editable=False,null=True,blank=True,max_length=10)
    timezone = models.CharField(editable=False,null=True,blank=True,max_length=10)
    # Link M2M
    restaurant_typederepas = models.ManyToManyField('Typerepas',blank=True, related_name='restaurant_typederepas')
    restaurant_specificitederepas = models.ManyToManyField('Sperepas',blank=True, related_name='restaurant_specificitederepas')
    #
    accept_resa = models.BooleanField(default=True)
    date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
    slug = models.SlugField(max_length=200, editable=False, unique=True)
    
    def __str__(self):
        return str(self.name)
    
    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = unique_slugify(self, slugify(self.name))
            self.resto_id_table = secrets.token_urlsafe(6)
            
            super(Restaurant, self).save(*args, **kwargs)
        
        if self.full_address:
            result = geocoder.geocode(self.full_address)

            if result and len(result):
                try:
                    self.restaurant_longitude = result[0]['geometry']['lng']
                except Exception as e:
                    self.restaurant_longitude = None
                try:
                    self.restaurant_latitude  = result[0]['geometry']['lat']
                except Exception as e:
                    self.restaurant_latitude = None
                try:
                    self.zipcode  = result[0]['components']['postcode']
                except Exception as e:
                    self.zipcode = None
                try:
                    self.town  = result[0]['components']['municipality']
                except Exception as e:
                    self.town = None
                try:
                    self.state  = result[0]['components']['state']
                except Exception as e:
                    self.state = None
                try:
                    self.country  = result[0]['components']['country']
                except Exception as e:
                    self.country = None
                try:
                    self.country_code = result[0]['components']['country_code']
                except Exception as e:
                    self.country_code = None
                try:
                    self.currency_code = result[0]['annotations']['currency']['iso_code']
                except Exception as e:
                    self.currency_code = None
                try:
                    self.timezone  = result[0]['annotations']['timezone']['offset_string']
                except Exception as e:
                    self.timezone = None
                
                super(Restaurant, self).save(*args, **kwargs)
            
        if not self.qr_code:
            qrcode_img = qrcode.make("https://feedcool.fr/restaurant/" + self.slug)
            canvas = Image.new('RGB', (380, 380), 'white')
            canvas.paste(qrcode_img)
            fname = f'qr_code-{self.name}.png'
            buffer = BytesIO()
            canvas.save(buffer,'PNG')
            self.qr_code.save(fname, File(buffer), save=False)
            canvas.close()
            
            super(Restaurant, self).save(*args, **kwargs)
            
            
        super(Restaurant, self).save(*args, **kwargs)
Comment créer un model Restaurant avec Django en Python ?

Django : créer le model QR Code

#models.py

class TableQrcode(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
    qr_code = models.ImageField(upload_to='qr_codes_table/', blank=True, null=True)
    number = models.IntegerField(blank=True, null=True)
    capacity = models.IntegerField(blank=True, null=True)
    date_created = models.DateTimeField(auto_now_add=True)
    slug = models.SlugField(editable=False, unique=True)
    
    def save(self, *args,**kwargs):
        if not self.slug:
            self.slug = self.user.restaurant.slug + secrets.token_urlsafe(12)
        
        #super().save(*args, **kwargs) 
        
        if not self.qr_code:
            qrcode_img = qrcode.make("https://feedcool.fr/" + self.user.restaurant.resto_id_table + "/order/" + self.slug)
            canvas = Image.new('RGB', (420, 420), 'white')
            canvas.paste(qrcode_img)
            fname = f'qr_code_table_{self.user.restaurant.name}_{secrets.token_urlsafe(12)}.png'
            buffer = BytesIO()
            canvas.save(buffer,'PNG')
            self.qr_code.save(fname, File(buffer), save=False)
            canvas.close()
            
        super(TableQrcode, self).save(*args, **kwargs)
    
    def __str__(self):
        return self.slug
    
    class Meta:
        ordering = ['-date_created']
Comment créer un model Restaurant avec Django en Python ?

Django : créer le model Order

#models.py

class Order(models.Model):
    oder_id = models.CharField(max_length=8, editable=False, unique=True)
    code_verif = models.CharField(max_length=6, editable=False, unique=True)
    code_verif_try = models.CharField(max_length=6, blank=True, null=True)
    consumer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="order_consumer")
    seller = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="order_seller")
    post = models.ForeignKey(Cuisine,on_delete=models.CASCADE,related_name="order_post", null=True,blank=True)
    prix = models.DecimalField(max_digits=4,decimal_places=2)
    # DATA
    start_time = models.TimeField(blank=True,null=True)
    deadline_time = models.TimeField(blank=True,null=True)
    start_date = models.DateField(blank=True,null=True)
    quantity = models.IntegerField(default=1)
    is_delivered = models.BooleanField(default=False)
    date_created = models.DateTimeField(auto_now_add=True)
    is_paid = models.BooleanField('', default=False)
    date_paid = models.DateTimeField(null=True,blank=True)
    is_ready = models.BooleanField('', default=False)
    # CODE
    is_received = models.BooleanField('', default=False)
    # CASH
    seller_get_monney = models.BooleanField('', default=False)
    is_cancelled = models.BooleanField('', default=False)
    slug = models.SlugField(editable=False,unique=True)
    
    
    def save(self, *args, **kwargs):
        # Enregistrement des données confidentielles
        if not self.slug:
            self.slug = secrets.token_urlsafe(12)
            
        if not self.oder_id:
            self.oder_id = secrets.token_urlsafe(8)
            
        if not self.code_verif:
            self.code_verif = secrets.token_urlsafe(6)
            
        super().save(*args, **kwargs)
        
    class Meta:
        ordering = ['-date_created']
        
    def __str__(self):
        return self.post.title
    
    def total_amount(self):
        return self.quantity * self.prix
Comment créer un model Order avec Django en Python ?

Django : créer le model Notification

#models.py

def notifs_order(sender, instance, *args, **kwargs):
	order = instance
	sender = order.consumer
	no_seller = order.seller
	order_paid = order.is_paid and not order.is_ready and not order.is_received
	order_ready = order.is_paid and order.is_ready  and not order.is_received
	order_received = order.is_paid and order.is_ready and order.is_received
	if order_paid:
	    notify = Notification(order=order, sender=sender, user=no_seller, notification_type=1)
	    title = "Feedcool - Commande - un utilisateur vient de commander"
	    message_infos = render_to_string('notifs_commande.html', {'sender': order.consumer,'date': order.start_date,'quantity': order.quantity,'n_userb': no_seller,'channel': order.post.slug,})
	
	elif order_received:
	    notify = Notification(order=order, sender=sender, user=no_seller, notification_type=1)
	    title = "Feedcool - Commande - votre repas a bien été collecté"
	    message_infos = render_to_string('notifs_livre.html', {'sender': order.consumer,'n_userb': no_seller,'channel': order.post.slug,})
	
	elif order_ready:
	    notify = Notification(order=order, sender=no_seller, user=sender, notification_type=6)
	    title = "Feedcool - Commande - votre commande est prête"
	    message_infos = render_to_string('notifs_ready.html', {'sender': order.consumer,'n_userb': no_seller, 'restaurant':order.seller.restaurant.name, 'addresse': order.seller.restaurant.full_address,'channel': order.post.slug, 'code': order.code_verif, 'number':order.oder_id,})
	
	else:
	    notify = Notification(order=order, sender=sender, user=no_seller, notification_type=2)
	    title = "Feedcool - Panier - un utilisateur a ajouté votre repas à son panier"
	    message_infos = render_to_string('notifs_panier.html', {'sender': order.consumer,'n_userb': no_seller,'channel': order.post.slug,})
	    #pass
	notify.save()
	
	send_mail(
    	    subject = title,
    	    message = message_infos,
    	    from_email="yep@nutri.feedcool.fr",
    	    recipient_list=[sender.email]
        )
        
post_save.connect(notifs_order, sender=Order)
Comment créer un model de Notifications avec Django en Python ?


Django : créer le model Newsletter

#models.py

class Subscriber(models.Model):
    email = models.EmailField(unique=True)
    conf_num = models.CharField(max_length=15)
    confirmed = models.BooleanField(default=False)

    def __str__(self):
        return self.email + " (" + ("not " if not self.confirmed else "") + "confirmed)"

class Newsletter(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    subject = models.CharField(max_length=150)
    contents = models.FileField(upload_to='uploaded_newsletters/')

    def __str__(self):
        return self.subject + " " + self.created_at.strftime("%B %d, %Y")
    
    def send(self, request):
        contents = self.contents.read().decode('utf-8')
        subscribers = Subscriber.objects.filter(confirmed=True)
        sg = SendGridAPIClient(settings.SENDGRID_API_KEY)
        for sub in subscribers:
            message = Mail(
                    from_email=settings.FROM_EMAIL_SENDGRID,
                    to_emails=sub.email,
                    subject=self.subject,
                    html_content=contents + (
                        '<br><a href="{}newsletters/delete/?email={}&conf_num={}">Unsubscribe</a>.').format(
                            request.build_absolute_uri('newsletters/delete/'),
                            sub.email,
                            sub.conf_num))
            sg.send(message)
Comment créer un model de Newsletter avec Django en Python ?

Django : créer le model Blog

#models.py

class Blog(models.Model):
    title = models.CharField(max_length=70)
    user = models.ForeignKey(User, on_delete= models.CASCADE,related_name='blog_user')
    tableofcontent = RichTextField(blank=True, null=True)
    content = RichTextField(blank=True, null=True)
    image = models.ImageField(upload_to='blog/')
    section = models.ManyToManyField('Blogcategory', related_name='blog_section')
    related = models.ManyToManyField('self',blank=True, related_name="blog_related")
    seo_keyword = models.TextField(blank=True, null=True)
    seo_schema = RichTextField(blank=True, null=True)
    #updated_on = models.DateTimeField(auto_now= True)
    created_on = models.DateTimeField(auto_now_add=True)
    status = models.IntegerField(choices=STATUS, default=0)
    slug = models.SlugField(editable=False)
    
    def save(self, *args,**kwargs):
        if not self.slug:
            self.slug = unique_slugify(self, slugify(self.title))
        super(Blog, self).save(*args, **kwargs)
        
    class Meta:
        ordering = ['-created_on']    

    def __str__(self): 
        return self.title
Comment créer un model de Blog avec Django en Python ?

Feedcool — Restaurateur : gagnez du temps avec Feedcool
🍕 🍔 🍣 Notre objectif : construire ensemble un SAAS d’optimisation déstiné aux restaurateurs pour la période post-COVID.
Résultat de la Food App développée avec Django (Python)

Si tu codes déjà en Python, et que tu souhaites récupérer une partie des lignes de code informatique, ou même l'améliorer, n'hésite pas à me contacter.

Au-delà des techniques de programmation, je pense que cette expérience pourra t'aider dans ton approche commerciale. N'oublie pas que le meilleur moyen d'apprendre la programmation est de te trouver un projet à coder. Commencer from scratch avec un cap en tête...

Avec Udemy, ou d'autres services, tu seras capable de programmer un projet perso, des scripts, des jeux vidéo, des algorithmes, un langage machine, une programmation web, et j'en passe... Les vidéos disponibles et cours complet vont faciliter la programmation de ton projet. Utilise-les comme un tremplin au service de ton application Python ou tout autre langage de programmation.