Intermittent fasting, aka time-restricted eating, can help people lose weight—but the reason why may not be complicated hypotheses about changes from fasting metabolism or diurnal circadian rhythms. It may just be because restricting eating time means people eat fewer calories overall.
In a randomized-controlled trial, people who followed a time-restricted diet lost about the same amount of weight as people who ate the same diet without the time restriction, according to a study published Friday in Annals of Internal Medicine.
The finding offers a possible answer to a long-standing question for time-restricted eating (TRE) research, which has been consumed by small feeding studies of 15 people or fewer, with mixed results and imperfect designs.
SUPIR (Scaling-UP Image Restoration) est une avancée majeure dans le domaine de la restauration d’images intelligente et réaliste. En s’appuyant sur des techniques multi-modales et des générateurs préentraînés avancés, SUPIR repousse les limites de ce qui est possible en termes de qualité et de contrôle de la restauration en combinant ce qui se fait de mieux en ce moment en IA.
Au cœur de SUPIR se trouve un modèle génératif que vous connaissez bien : StableDiffusion-XL (SDXL) et ses 2,6 milliards de paramètres. Pour l’appliquer efficacement à la restauration, les dev du projet ont du concevoir et entrainer un adaptateur de plus de 600 millions de paramètres.
Mais l’autre atout clé de SUPIR est son jeu de données d’entraînement titanesque, avec plus de 20 millions d’images haute résolution et haute qualité, chacune annotée avec une description textuelle détaillée. Cela permet à SUPIR de réaliser des restaurations guidées par des instructions en langage naturel, offrant un contrôle sans précédent sur le résultat final.
Je l’ai testé sur une image culte d’Internet : Les Horribles Cernettes. Pour ceux qui ne connaissent pas, il s’agit de la première photo qui a été envoyée via Internet à Tim Berners-Lee.
Voici l’image d’origine bien dégeu en terme de qualité :
Et voici ce que ça donne une fois passé dans SUPIR. Vous n’avez jamais vu cette photo avec cette qualité, j’en suis sûr !
Des prompts de « qualité négative » sont également utilisés pour améliorer encore la qualité perceptuelle. Et une méthode d’échantillonnage guidé par la restauration a été développée pour préserver la fidélité à l’image source, un défi courant avec les approches génératives.
Par contre, au niveau de certains détails comme les yeux, on n’y est pas encore mais je sais qu’il y a d’autres IA capable de gérer ça.
Grâce à cette combinaison unique de modèles, de données d’entraînement massives et de fonctionnalités plutôt avancées, SUPIR produit des restaurations d’une qualité exceptionnelle, en particulier sur des photos dégradées. Le tout avec la possibilité inédite de contrôler finement le résultat via des instructions en langage naturel.
Voici quelques exemples de cas d’utilisation :
Si vous ne me croyez pas, il y a plein d’exemples sur le site officiel du projet. Alors par contre, y’a pas de version en ligne officielle donc vous avez 2 possibilités pour jouer avec. Soit vous taper l’install à la main sur votre ordinateur, soit aller sur Replicate qui héberge une version en ligne avec laquelle vous pourrez améliorer vos images.
Pour en savoir plus sur SUPIR, rendez-vous sur le dépôt officiel du projet.
Depending on who you ask about AI (and how you define it), the technology may or may not be useful, but one thing is for certain: AI hype is dominating corporate marketing these days—even in fast food. According to a report in The Wall Street Journal, corporate fast food giant Yum Brands is embracing an "AI-first mentality" across its restaurant chains, including Taco Bell, Pizza Hut, KFC, and Habit Burger Grill. The company's chief digital and technology officer, Joe Park, told the WSJ that AI will shape nearly every aspect of how these restaurants operate.
"Our vision of [quick-service restaurants] is that an AI-first mentality works every step of the way," Park said in an interview with the outlet. "If you think about the major journeys within a restaurant that can be AI-powered, we believe it’s endless."
As we've discussed in the past, artificial intelligence is a nebulous term. It can mean many different things depending on the context, including computer-controlled ghosts in Pac-Man, algorithms that play checkers, or large language models that give terrible advice on major city websites. But most of all in this tech climate, it means money, because even talking about AI tends to make corporate share prices go up.
Aujourd’hui, j’aimerais vous présenter LocalAI, une alternative open source à OpenAI. En tout cas, c’est comme ça que le créateur du projet le présente. Il s’agit d’une solution idéale pour tous ceux qui cherchent une API REST compatible avec les spécifications de l’API OpenAI pour l’inférence locale.
Grâce à LocalAI, vous pouvez exécuter des modèles linguistiques, générer des images, de l’audio et bien d’autres choses encore, localement ou sur site avec du matériel grand public, et ce, sans avoir besoin d’un GPU ! Le projet a pour principal objectif de rendre l’IA accessible à tous.
Pour résumer, voici les principales caractéristiques de LocalAI :
En termes de fonctionnalités, LocalAI offre une large gamme d’options, parmi lesquelles :
LocalAI est bien sûr un projet communautaire donc n’hésitez pas si vous souhaitez vous impliquer !
Pour commencer rapidement avec LocalAI, vous pouvez consulter leur guide Getting Started qui décrit les différentes méthodes d’installation et les exigences matérielles ou aller consulter les guides de la communauté. Je vous ferais aussi probablement un tutoriel prochainement si mon emploi du temps me le permet.
LocalAI est disponible sous forme d’image conteneur et de binaire, compatible avec divers moteurs de conteneurs tels que Docker, Podman et Kubernetes. Les images de conteneurs sont publiées sur quay.io et Docker Hub, et les binaires peuvent être téléchargés à partir de GitHub.
Concernant les exigences matérielles, ça varie en fonction de la taille du modèle et de la méthode de quantification utilisée mais pour choper quelques repères de performance avec différents backends, comme llama.cpp, vous pouvez consulter ce lien.
Maintenant pour en savoir plus, vous pouvez explorer le site localai.io. Vous y trouverez de nombreuses informations et des exemples d’utilisation pour vous aider à tirer le meilleur parti de LocalAI.
Merci à Lorenper
Thomas Mahler, directeur des jeux Ori and the Blind Forest et Ori and the Will of the Wisps, s'est exprimé sur l'accumulation des jeux en monde ouvert. Il pointe du doigt leur problème commun, avec des expériences qui cofondent souvent quantité et qualité.
On Wednesday, authorities arrested former Google software engineer Linwei Ding in Newark, California, on charges of stealing AI trade secrets from the company. The US Department of Justice alleges that Ding, a Chinese national, committed the theft while secretly working with two China-based companies.
According to the indictment, Ding, who was hired by Google in 2019 and had access to confidential information about the company's data centers, began uploading hundreds of files into a personal Google Cloud account two years ago.
The trade secrets Ding allegedly copied contained "detailed information about the architecture and functionality of GPU and TPU chips and systems, the software that allows the chips to communicate and execute tasks, and the software that orchestrates thousands of chips into a supercomputer capable of executing at the cutting edge of machine learning and AI technology," according to the indictment.
Les trois épisodes de Raël : Le Prophète des Extraterrestres sont désormais sur Netflix. Voici d'autres séries documentaires faisant l'anatomie de scandales particulièrement médiatisés, à voir aussi sur Netflix.
Besoin d’accéder à un site bien chelou, bien dangereux ? Seulement, voilà, vous sanglotez de peur à l’idée de vous faire véroler.
Pas de panique, je suis là, comme d’habitude avec mes bonnes astuces.
Pour aller quand même sur une URL peu voire pas fiable du tout, il est possible de passer par un navigateur distant lancé au travers d’une sanbox. Et c’est exactement ce que propose le site Browser.lol.
Avec des fonctionnalités comme l’accès illimité, vous pouvez contourner les restrictions géographiques pour accéder à des contenus bloqués dans certaines localités. Cela garantit la protection de votre vie privée en ligne, permettant une navigation anonyme et sécurisée de votre identité numérique.
C’est également pratique pour vérifier la compatibilité des sites web avec différents navigateurs et appareils. De plus, l’outil permet de débloquer des sites web qui sont restreints sur un réseau ou par une organisation. Comme ça tout paaasssseeee !
Sur Browser.lol, vous trouverez plusieurs navigateurs comme Chrome, Firefox, Edge, ou encore Bravo, Opera et Tor, afin de faire vos tests.
Le site offre également des capacités de test de vulnérabilité pour identifier les failles de sécurité sur les sites web. Et pour les recherches discrètes (hmm-hmm), l’outil assure une recherche incognito, vous permettant de naviguer sans laisser de trace.
Enfin, l’expérience de navigation est améliorée par un blocage efficace des publicités, et le masquage du trafic permet de garder vos activités internet privées, loin des yeux de votre réseau local et de votre fournisseur d’accès internet.
Bref, c’est le genre de site bien pratique à garder dans vos favoris.
Imaginez que vous deviez partager des fichiers avec vos collaborateurs, travailler sur des projets avec des personnes situées aux quatre coins du monde ou simplement sauvegarder vos photos de vacances, mais que vous ne savez pas par quoi commencer ?
Et bien, ne cherchez plus, SFTPGo est là pour vous faciliter la vie.
SFTPGo c’est un serveur SFTP open source, complet et hautement configurable qui supporte HTTP/S, FTP/S ou encore WebDAV. Il prend en charge plusieurs backends de stockage : du système de fichiers local avec ou sans chiffrement, du stockage d’objets S3, du Google Cloud Storage, de l’Azure Blob Storage, et bien sûr, SFTP.
L’outil est gratuit, mais si vous voulez le soutenir ou profiter d’un support technique, il faudra porter la main à la bourse ^^. C’est donc plus Open Bourse qu’Open Source.
Parmi les fonctionnalités offertes, on trouve la possibilité de créer des dossiers virtuels privés ou partagés, des commandes personnalisables, des comptes virtuels stockés, de l’isolement chroot, des autorisations virtuelles par utilisateur et par répertoire, ainsi qu’une API REST, un gestionnaire d’événements, une interface d’administration…etc.
SFTPGo facilite le paramétrage d’une authentification multifacteur personnalisable. Authentification partielle, par utilisateur et avec plusieurs méthodes, incluant l’authentification LDAP/Active Directory et des programmes externes. Il supporte l’administration des utilisateurs, le chiffrement des données, la modification dynamique des utilisateurs, les quotas, la limitation de bande passante et de débit.
Pour sécuriser vos données, SFTPGo propose une limitation du nombre de sessions simultanées avec ou sans filtrage par IP ou dossier. Terminaison automatique des connexions inactives, gestion de liste de blocage, filtrage Geo-IP et il est bien sûr compatible avec Git, SCP, rsync, FTP/S et WebDAV.
Pour vous faciliter la vie, SFTPGo propre aussi des analyses de performance, des logs ultra-précis et un support Infrastructure as Code (IaC) via Terraform. Il est compatible avec Linux, macOS, Windows et FreeBSD et nécessite Go ainsi qu’un serveur SQL. Des binaires et une image Docker officielle sont également disponibles.
SFTPGo est disponible sur plusieurs plateformes, y compris les dépôt Ubuntu, Void Linux, AWS Marketplace, Azure Marketplace et Elest.io. Il est également disponible pour Windows et macOS via des packages tels que winget, Chocolatey et Homebrew. Et il existe des portages pour FreeBSD et DragonFlyBSD.
Voilà, si vous cherchez un serveur SFTP totalement configurable (y’a même un système de plugins) et supportant S3, Google Cloud, Azure et j’en passe, celui-ci est un must-have !
Si vous aimez les ordinateurs un peu légers, type Raspberry Pi, Odroid et j’en passe, alors DietPi est fait pour vous. Il s’agit d’un système d’exploitation basé sur une Debian allégée dans le but de consommer le moins de CPU et de RAM possible.
Voici un tableau qui compare DietPi avec Raspberry Pi OS Lite (64 bits). On peut y voir que niveau RAM, on économise 58%, sur le CPU c’est 39% en moyenne et en matière d’espace disque, c’est plus léger de 41%. Évidemment, cela se ressent sur la rapidité du boot, puisque c’est plus rapide à démarrer de 73%.
Avec DietPi, vous bénéficiez de paramètres personnalisables pour configurer votre système selon vos besoins, et vous pouvez facilement gérer les priorités de vos logiciels. De plus, le système prend en charge les mises à jour automatiques et l’automatisation de l’installation grâce à un dietpi.txt
dans lequel vous pouvez spécifier votre config (genre les accès wifi, l’activation du serveur ssh…etc).
DietPi est également livré avec un utilitaire de Backup ce qui vous permettra très facilement de sauvegarder et de restaurer votre installation.
Comme je le disais, DietPi est compatible avec une variété d’ordinateurs, notamment les modèles Raspberry Pi, Odroid, PINE A64 et bien d’autres.
Les machines virtuelles telles que Hyper-V, Virtualbox, Vmware, Parallels, UTM et Proxmox sont également prises en charge par DietPi, ce qui vous permet de l’utiliser lorsque les performances des micro-ordis sont insuffisantes. Ça peut même tourner sur un bon vieux PC x86/64.
Pour en savoir plus sur DietPi et comment l’utiliser, je vous invite à consulter le site officiel ici. Vous y trouverez des informations détaillées sur les différentes fonctionnalités offertes par DietPi, ainsi que des conseils pour bien démarrer avec ce système d’exploitation léger et optimisé.
En résumé, DietPi est un choix fantastique pour ceux qui souhaitent tirer le meilleur parti de leur matériel, tout en minimisant la consommation de ressources. Qu’il s’agisse de créer un serveur NAS, d’expérimenter avec des projets IoT ou simplement de vous familiariser avec le monde des SBC (Single Board Computer), DietPi vous permettra de vous lancer rapidement et en toute simplicité.
[Deal du jour] L'e-Presto Max, de la marque française Easy-Watts, est un excellent scooter électrique équivalent 125 cm³. Son joli design et ses bonnes performances en font un deux-roues pratique en ville. Il devient plus abordable avec cette promotion.
A second person has died after drinking Panera's caffeinated "Charged Lemonade" drinks, which contain caffeine levels comparable to strong coffee and are sold in cups as large as 30 fluid ounces that are free to refill.
According to a lawsuit filed by family members Monday, 46-year-old Dennis Brown fell dead on a sidewalk from cardiac arrest while walking home from a Panera in Florida on October 9. Before he left the restaurant, he had ordered a Charged Lemonade and had two refills.
In September, college student Sarah Katz, 21, went into cardiac arrest after drinking a Charged Lemonade from a Panera in Philadelphia. Katz had a heart condition called long QT syndrome type 1 and had avoided energy drinks because of it, according to a lawsuit filed in October by her family.
AI face detection now counts among the tools that could be used to help adult sites effectively estimate UK user ages and block minors from accessing pornography, the UK's Office of Communications (Ofcom) said in a press release on Tuesday.
The only foreseeable problem, Ofcom noted: There's little evidence that the AI method of age estimation will be fair, reliable, or effective.
The UK's legal age to watch porn is 18. To enforce that restriction, under the Online Safety Act, Ofcom will soon require all apps and sites displaying adult content to introduce so-called "age assurance" systems that verify and/or estimate user ages. Sites and apps risk potential fines if they fail to "ensure that children are not normally able to encounter pornography on their service."
Monterey Car Week always brings out the automotive industry’s heavy hitters, from world-class vintage cars to brand-new seven-figure track toys, classic Vespa scooters, and everything in between. And yet, a surprising duo wound up as my favorite experience of the week: driving a pair of two tiny “Super Coopers” brought to Monterey by Gildred Racing to promote the debut of a new Tesla-powered electric project.
The company's Super Cooper electric powertrain conversion might have deserved the lion’s share of headlines as the latest and greatest offering, but the company’s previous Super Cooper “S” also delivers an absurd 502 hp (375 kW) to the rear wheels from a mid-mounted supercharged V6.
Taking an expansive view, the pair truly serves as a microcosm that perfectly encapsulates the current enthusiast industry, right down to the differences between rear- and front-wheel drive—and even to this day, I can’t decide which I liked more.
To Shelby Mustang fans, the Original Venice Crew (OVC) is the stuff of legends. This was the actual team that designed and built the original GT350s, Cobras, Daytona Coupes, and GT40s that introduced Carroll Shelby's name to the masses. Today, OVC builds modern Mustang re-creations so accurately that one was approved for last year's Le Mans Classic, which celebrated 100 years of the world's most famous endurance race.
But OVC also offers updated versions of those classics, bringing to life ideas that bounced around the shop back in the day but that Shelby never built in series production. Want a 1965 GT350 with independent rear suspension? OVC can do that, after dialing in a design that Ford originally believed would be too expensive as a replacement for the first-gen Mustang's solid rear axle.
These projects don't quite fit under the "restomod" umbrella, instead falling more along the lines of the ideas that OVC founder and boss Jim Marietta remembers from his days back at 1042 Princeton Drive. Think fender flares cut by hand rather than being machined or updated fiberglass front fascias to provide additional airflow.
Five former Ubisoft executives have reportedly been detained for questioning by French authorities, years after they departed from the company amid widespread sexual assault allegations.
According to a report from France's Libération newspaper (as translated by GamesIndustry.biz), this week's arrests by the Bobigny public prosecutor's office include Ubisoft's former chief creative officer Serge Hascoët and ex-VP of editorial and creative services, Tommy François. Hascoët resigned from the company in July 2020, while Francois left less than a month later. A year after those departures, French labor union Solidaires Informatique worked with two of the alleged victims to file a formal complaint about the alleged assaults, which seems to have led to this week's move by French police.
It's not immediately clear who else has been caught up in this week's police actions or whether the former executives will be released from detention after questioning. Other high-profile Ubisoft employees who resigned or were fired amid the 2020 allegations include Assassin's Creed Valhalla director Ashraf Ismail, former Ubisoft Canada managing director Yannis Mallat; former Ubisoft PR director Stone Chin; former Ubisoft global head of HR Cécile Cornet, and former Ubisoft vice president of editorial Maxime Beland.
Electrifying classic sports cars is swiftly becoming big business. Battery-powered 911s, E-Types, Triumphs, and more have been around for a while, but the humble MGB has thus far been overlooked… until now. UK-based MG specialists Frontline Developments has a rich history of restoring, modifying, and generally sprucing up MGBs, and has decided that now is the time to inject a cleaner, greener heart into what was once a British sportscar staple.
The MG BEE EV is a neat little thing. Currently in prototype form, it's packing a 40 kWh battery attached to a 114 hp (85 kW) 162 lb ft (220 Nm) Hyper9 motor that sends power to the rear wheels via a five-speed Mazda Miata gearbox. Range is about 140 miles (225 km) if you drive sensibly, and charging takes about five hours. Frontline will build you one as a BEE GT (coupe), or Roadster should you wish to feel the wind in your beard as you roll along.
Frontline Developments is well known in the MG world. Founded in 1991 by Tim Fenna, it started with gearbox swaps, then widened its business with suspension, brake, and even engine upgrades for customers wanting a little more go. The company offers full restorations, as well as its own special editions from time to time. Its LE50 and Abingdon editions came with beautiful aesthetics, twinned with more modern (and powerful) internal combustion engines. It's launching a V8-powered LE60 alongside the BEE for those who fancy old-school thrills.
S'agit-il d'une découverte retentissante... ou d'un nouveau canular scientifique de premier ordre ?
Extraterrestres : deux “corps non-humains” exhibés devant le congrès mexicain
Vous aimez faire le mariole sur Youtube en live et c’est trop cool de pouvoir parler à des tas de gens en même temps et de montrer vos exploits sur Minecraft. Mais vous avez également une communauté sur TikTok, Twitch et Instagram…etc.
Alors comment faire pour contenter tout ce petit monde ? Changer de plateforme tout au long de la journée ?
Trop le bordel, alors vous laissez tomber.
Aaaah si seulement vous aviez eu un outil pour faire du multi-stream ! Eh bien, ne cherchez pas plus loin : Restreamer est là pour vous sauver !
Restreamer est un serveur vidéo open-source facile à utiliser, qui offre une interface sympa et ainsi qu’une API. Ainsi, il est capable de diffuser votre flux en direct sur n’importe quelle plateforme vidéo de l’univers (Twitch, Youtube, TikTok, Instagram…etc) en même temps et sans payer de coût de licence.
Parmi les fonctionnalités de Restreamer, on retrouve la possibilité de configurer des flux vidéo en direct, de surveiller les statistiques, de contrôler la bande passante et surtout de gérer plusieurs sources vidéo. Et pour ceux qui se demandent, oui si vous êtes Partenaire Twitch, vous pouvez faire un live simultané sur une plateforme mobile comme TikTok ou Instagram… mais pas Youtube Live.
Sachez que Restreamer est compatible avec divers protocoles de streaming tels que RTMP, RTSP, HLS, DASH et SRT et qu’il fonctionnera aussi bien sous Linux, que macOS ou Windows, ainsi que sur Raspberry Pi et autres petits ordinateurs du style.
Pour installer Restreamer, assurez-vous d’avoir Docker installé sur votre système puis lancez la commande suivante dans votre terminal pour télécharger et exécuter l’image Docker de Restreamer :
docker run -d --restart=always --name restreamer \ -p 8080:8080 -p 8181:8181 -p 1935:1935--privileged \ datarhei/restreamer:latest
Il est libre, sous licence Apache 2.0, ce qui signifie qu’il est gratuit pour un usage privé et commercial et vous pouvez télécharger sa dernière version directement depuis le site officiel.
Une démo est là pour ceux qui veulent en voir plus. (user : admin / pass : demo)
Merci à JohnnyBeGood pour le partage !
Si vous cherchez un bon moyen d’auto-héberger vos magazines, vos livres et bandes dessinées, ne cherchez plus, Komga est la solution !
Si les comics vous intéressent et que vous avez tout ça au format numérique, c’est forcement un peu galère pour organiser tout ça et s’y retrouver. Grâce à Komga vous pourrez vous sortir de ce pétrin et créer des bibliothèques pour vos BDs afin d’organiser totalement vos collections.
Komga est donc un serveur de comics / mangas open source et gratuit. Vous pouvez même y mettre vos magazine PDF ou vos livres. Il supporte les formats epub, pdf, cbz et cbr et une fois en place, vous profiterez d’une jolie d’une interface web responsive.
Vous pourrez ainsi organiser votre bibliothèque avec des collections et des listes de lecture, et même modifier les métadonnées de vos séries de BDs et de vos livres. Komga permet également d’importer automatiquement les métadonnées intégrées à ces fichiers et vous pouvez tout lire sans quitter votre navigateur via le lecteur web.
D’ailleurs plusieurs modes de lecture sont dispo et vous pouvez même gérer plusieurs users avec un contrôle d’accès par bibliothèque, mais également des restrictions d’âge.
En plus de cela, il dispose d’une API REST et de nombreux outils et scripts développés par la communauté qui sont capables d’interagir avec Komga.
Vous pourrez, d’un clic, télécharger les fichiers à l’unité ou des séries entières de BDs entières et si vous avez des petits soucis de mémoire lors de vos imports, l’outil est également capable de détecter les fichiers en double et même les pages en double. Komga peut également importer automatiquement les BDs déposées dans un répertoire.
Le plus beau là-dedans, c’est que ça s’installe très facilement avec Docker Compose, ou lancé directement via le .jar fourni (java). Donc vous l’aurez compris, ça peut tourner sur un Windows, un Linux mais également un NAS.
Si vous voulez tester par vous-même, une démo est accessible ici : https://demo.komga.org/
demo@komga.org
komga-demo
Vous vous souvenez de Restic que je vous ai présenté en 2020 ?
Ce logiciel permet de faire des backup sous forme de snapshot (object storage) localement ou sur des services distants type AWS S3, OpenStack Swift, BackBlaze B2, Microsoft Azure Blob Storage ou encore Google Cloud Storage sans oublier via SFTP.
Sauf que Restic, et bien c’est de la ligne de commande et je sais que certains d’entre vous sont allergiques à ça. Heureusement, il existe une interface graphique (moche) nommée Jarg qui devrait vous aider à prendre ça en main.
Il vous faudra évidemment télécharger et installer Restic avant de pouvoir utiliser cette application.
Lors de votre première utilisation de Jarg, un profil « par défaut » sera alors créé, ainsi qu’un « répertoire restic » dans votre dossier utilisateur et c’est votre dossier courant qui sera utilisé comme source pour la sauvegarde.
Jarg exécutera alors les commandes Restic correspondantes à ce que vous voulez et affichera les résultats affichés sur la droite de l’interface.
Notez que niveau sécurité, Jarg ne chiffre pas pour le moment les mots de passe et les secrets S3 donc soyez prudent avec la sécurité de votre ordinateur.
Jarg est dispo sous macOS, Windows et Linux (soit vous compilez, soit vous utilisez l’AppImage) en cliquant ici.
On a tous de vieilles photos qui trainent dans nos archives et notamment de vieux JPEG qui datent d’une époque où la compression d’image était vraiment dégradée. Mais grâce à l’algo DifFace qui utilise de l’IA, il est possible de gérer les dégradations complexes d’une photo pour la restaurer.
Et je dois dire que le résultat est bluffant. Alors tout le code se trouve sur Github et vous pouvez le déployer de votre côté, mais si la flemme est ce qui conditionne l’ensemble de votre vie, le plus simple est encore d’aller sur HuggingFace (édit : ça marche plus, passez par le Colab plutôt).
Vous uploadez votre image, vous cliquez sur « Soumettre » et voilà ! vous aurez un super résultat qui fera plaisir à votre petit coeur.
J’ai testé beaucoup d’outils similaires, mais je crois que celui-ci fournit la meilleure des restaurations, y compris quand c’est hyper hyper flou… Même si évidemment, tout n’est pas encore parfait puisqu’en zoomant, on a encore ce petit côté « dessiné sur l’image », mais on y arrive doucement.
Je vous invite vraiment à le tester et à le comparer avec d’autres systèmes similaires.
— Article en partenariat avec talent.io —
Certains pensent que le monde se divise en deux avec d’un côté le bien et de l’autre le mal. Mais la vérité est ailleurs surtout pour les développeurs qui savent que le monde se divise en réalité entre le Frontend et le Backend.
Et pour que ces 2 faces d’une même pièce puissent « discuter », il nous faut des API. API, ça veut dire Application Programming Interface et c’est tout simplement un ensemble d’instructions permettant aux applications de communiquer entre elles. Maintenant des types d’API, il y en a des tonnes, mais je vous propose qu’aujourd’hui on s’intéresse aux API qui respectent le standard architectural REST.
En effet, quand on travaille en tant que développeur, on ne peut pas passer à côté des API REST, tant cette pratique est très répandue. Si vous débutez dans votre métier, c’est forcement quelque chose que vous devez apprendre et maitriser. De la même manière, quand on débute sa carrière de développeur ou qu’on a déjà beaucoup d’années au compteur mais qu’on ne connaît pas sa valeur sur le marché de l’emploi, il est bon de connaître la grille des salaires en France. Ça tombe bien puisque talent.io a mis en ligne une étude des salaires de la tech en 2022 pour que vous puissiez déterminer si votre salaire est correct ou sous évalué.
Je vous invite à vous plonger dedans afin de savoir où vous vous situez sur cette grille des salaires.
REST, ça veut dire « Representational State Transfer ». Lorsqu’une API respecte les contraintes de REST, on parle alors d’une API RESTful.
Quand un développeur veut créer une API RESTful, il doit alors respecter les contraintes édictées par REST, notamment sur le fait que son API doit fonctionner au travers du protocole HTTP et de ses verbes (GET, POST…etc.) et que les requêtes et les réponses de l’API soient dans un format texte tels que JSON, HTML, XML…etc.
Les API RESTful sont alors mises à disposition des applications via ce qu’on appelle des endpoints. Un endpoint est un assemblage de 2 choses : une URI et un verbe HTTP.
Admettons que j’ai une API qui permette de lister des utilisateurs, mais également d’en créer des nouveaux.
L’URI de mon API est la suivante :
https://url.com/api/user
Le but du jeu, c’est d’appeler par exemple ce endpoint avec le verbe GET pour obtenir une liste de tous les utilisateurs.
GET: /user/
Ou pour ajouter un nouvel utilisateur, on peut utiliser le verbe POST comme ceci, en transmettant un bout de JSON contenant toutes les informations liées à l’utilisateur qu’on souhaite créer (nom, prénom…etc.) :
POST: /user/
L’idée là, c’est de vous montrer qu’on peut interagir avec l’API à l’aide de différents verbes HTTP. Get peut être assimilé au verbe « lire », POST au verbe « créer », PUT et PATCH au verbe « Mettre à jour » et DELETE au verbe « supprimer ». C’est aussi simple que cela. Et en retour le serveur peut alors répondre avec des codes de status HTTP classiques comme 200, 201, 2002…etc. pour dire que tout est OK, ou des codes de redirection 3xx, d’erreur client 4xx ou d’erreur serveur 5xx.
En ce qui concerne le format utilisé par les requêtes et les réponses, vous pouvez utiliser du texte, peu importe son format (html, xml.etc.), mais par convention, on utilise surtout du JSON (JavaScript Object Notation).
Le JSON a la particularité d’être facile à lire et à écrire pour les humains et est utilisé pour enregistrer des données dans des bases de données, mais également pour transmettre ces données entre les applications. C’est pourquoi on l’utilise dans les API RESTful.
Le JSON est une collection de paires nom / valeur qui sont ordonnées dans une liste. Ainsi les valeurs contenues dans le JSON peuvent être une chaine de caractère (string), un nombre, mais également des objets JSON, des tableaux de valeurs (Array), des booléens (true / false) ou la fameuse valeur null.
Je ne vais pas les aborder en détail pour ne pas alourdir cette initiation débutant mais sachez que pour qu’une API soit RESTful, elle doit en plus de tous ces aspects, intégrer également la notion de contrôle hypermedia (Hypermedia as the Engine of Application State – HATEOAS). C’est un attribut de REST qui permet de renvoyer des liens hypertextes afin d’indiquer les actions disponibles directement dans les réponses JSON
Voilà concernant les grands principes des API RESTful.
Maintenant je vous propose qu’on apprenne à en créer une de zéro. Vous allez voir, c’est super simple.
Pour cette démo, j’ai choisi d’utiliser Python comme d’habitude. Et je vais importer dans mon code le framework Flask qui va me permettre de faire monter mon API RESTful très facilement. Installez Flask comme ceci :
pip3 install flask-restful
Pour tester l’API, nous allons utiliser un outil qui permet à la fois de balancer nos requêtes et de voir si les réponses correspondent bien. L’un des plus connus s’appelle Postman et c’est gratuit en version de base pour tester. L’application Desktop de Postman est disponible ici.
Ensuite, nous allons définir ce que va faire notre API. Ce sera quelque chose d’assez simple permettant de créer, supprimer, lire et mettre à jour des données. Admettons que j’ai un site sur lequel je dois référencer des produits. Chacun de ces produits aura un id, un nom, un prix et une catégorie.
Je vais donc créer un fichier productAPI.py et je vais importer Flask comme ceci avec ses imports spécifiques aux API et au parsing JSON :
from flask import Flask
from flask_restful import Resource, Api, reqparse
app = Flask(__name__)
api = Api(app)
Puis je vais créer une espèce de mini base de données JSON directement dans mon code afin d’y référencer mes produits :
products = [
{ "id": 1, "name": "Stylo 4 couleurs", "price": 10, "category": "Papeterie" },
{ "id": 2, "name": "Cahier vert", "price": 20, "category": "Papeterie" },
{ "id": 3, "name": "Télévision 4K", "price": 30, "category": "Tech" },
{ "id": 4, "name": "Souris sans fil", "price": 40, "category": "Tech" },
{ "id": 5, "name": "Ananas", "price": 50, "category": "Alimentaire" },
{ "id": 6, "name": "Kinder Bueno", "price": 60, "category": "Alimentaire" },
{ "id": 7, "name": "Chemise de président", "price": 70, "category": "Textile" },
{ "id": 8, "name": "T-shirt de punk", "price": 80, "category": "Textile" },
{ "id": 9, "name": "Tournevis bleu", "price": 90, "category": "Bricolage" },
{ "id": 10, "name": "Marteau rouge", "price": 100, "category": "Bricolage" }
]
Maintenant on va créer une classe Product qui sera vu comme un endpoint au niveau de notre API. Et dans cette class, nous allons placer nos 4 fonctions correspondantes aux 4 verbes HTTP dont je vous ai parlé plus haut.
class Product(Resource):
def get(self, id):
def post(self, id):
def put(self, id):
def delete(self, id):
La structure de base de notre API est OK. Maintenant, on va définir chacune de ces fonctions en commençant par la fonction get utilisée pour récupérer les informations d’un produit à partir de son ID. Comme vous pouvez le voir, on fait une boucle « for » qui va parcourir l’ensemble des produits et si l’ID correspond à ce qui spécifié dans la requête, on renvoie l’objet « product » avec son code HTTP 200 et si ce n’est pas le cas, on renvoie un message « Produit introuvable » accompagné d’un code d’erreur HTTP 404 qui correspond à une ressource non trouvée.
def get(self, id):
for product in products:
if (id == product["id"]):
return product, 200
return "Produit introuvable", 404
La fonction suivante va nous permettre de créer un nouveau produit. La donnée utilisée dans la requête étant structurée en JSON, cette fonction va utiliser le parseur de Flask pour extraire toutes les données du JSON et les ajouter à notre tableau de produits :
def post(self, id):
parser = reqparse.RequestParser()
parser.add_argument("name")
parser.add_argument("price")
parser.add_argument("category")
args = parser.parse_args()
for product in products:
if (id == product["id"]):
return "Ce produit {} existe deja".format(id), 400
product = {
"id": id,
"name": args["name"],
"price": args["price"],
"category": args["category"]
}
products.append(product)
return product, 201
Une fois que le produit est créé, on retourne le code 201 qui veut dire que la création s’est bien déroulée. Et si le produit existait déjà avec cet ID, on renvoie le code 400 avec un message d’erreur.
La fonction put suivante est assez similaire puisqu’elle permet à la fois de créer un nouveau produit (si l’id n’est pas encore utilisé) ou de mettre à jour un produit existant.
def put(self, id):
parser = reqparse.RequestParser()
parser.add_argument("name")
parser.add_argument("price")
parser.add_argument("category")
args = parser.parse_args()
for product in products:
if (id == product["id"]):
product["name"] = args["name"]
product["price"] = args["price"]
product["category"] = args["category"]
return product, 200
product = {
"id": id,
"name": args["name"],
"price": args["price"],
"category": args["category"]
}
products.append(product)
return product, 201
Enfin, vous l’aurez compris, la fonction delete permettra de supprimer un enregistrement à partir de son ID.
def delete(self, id):
global products
products = [product for product in products if product["id"] != id]
return "{} is deleted.".format(id), 200
Une fois ces fonctions définies, il ne reste plus qu’à spécifier le format de l’URI qui sera utilisé pour appeler l’API et lancer l’API (en mode debug pour démarrer). Le paramètre <int:id> permet d’indiquer que dans le chemin du endpoint, on peut ajouter une variable acceptée par l’API (ici l’ID du produit).
api.add_resource(Product, "/product/<int:id>")
app.run(debug=True)
Enfin, il ne reste plus qu’à lancer le script python à l’aide de la commande suivante :
python3 productAPI.py
Le serveur web de Flask se lancera alors et vous verrez apparaitre une URL locale que vous pourrez appeler dans Postman pour ensuite tester votre API.
Voici le code complet du script pour information.
from flask import Flask
from flask_restful import Resource, Api, reqparse
app = Flask(__name__)
api = Api(app)
products = [
{ "id": 1, "name": "Stylo 4 couleurs", "price": 10, "category": "Papeterie" },
{ "id": 2, "name": "Cahier vert", "price": 20, "category": "Papeterie" },
{ "id": 3, "name": "Télévision 4K", "price": 30, "category": "Tech" },
{ "id": 4, "name": "Souris sans fil", "price": 40, "category": "Tech" },
{ "id": 5, "name": "Ananas", "price": 50, "category": "Alimentaire" },
{ "id": 6, "name": "Kinder Bueno", "price": 60, "category": "Alimentaire" },
{ "id": 7, "name": "Chemise de président", "price": 70, "category": "Textile" },
{ "id": 8, "name": "T-shirt de punk", "price": 80, "category": "Textile" },
{ "id": 9, "name": "Tournevis bleu", "price": 90, "category": "Bricolage" },
{ "id": 10, "name": "Marteau rouge", "price": 100, "category": "Bricolage" }
]
class Product(Resource):
def get(self, id):
for product in products:
if (id == product["id"]):
return product, 200
return "Product not found", 404
def post(self, id):
parser = reqparse.RequestParser()
parser.add_argument("name")
parser.add_argument("price")
parser.add_argument("category")
args = parser.parse_args()
for product in products:
if (id == product["id"]):
return "Ce produit {} existe deja".format(id), 400
product = {
"id": id,
"name": args["name"],
"price": args["price"],
"category": args["category"]
}
products.append(product)
return product, 201
def put(self, id):
parser = reqparse.RequestParser()
parser.add_argument("name")
parser.add_argument("price")
parser.add_argument("category")
args = parser.parse_args()
for product in products:
if (id == product["id"]):
product["name"] = args["name"]
product["price"] = args["price"]
product["category"] = args["category"]
return product, 200
product = {
"id": id,
"name": args["name"],
"price": args["price"],
"category": args["category"]
}
products.append(product)
return product, 201
def delete(self, id):
global products
products = [product for product in products if product["id"] != id]
return "{} is deleted.".format(id), 200
api.add_resource(Product, "/product/<int:id>")
app.run(debug=True)
Côté Postman, un simple GET se forme ainsi et renvoie l’intégralité du JSON propre au produit (grâce à son ID) :
Pour faire un POST, il faut bien penser à indiquer qu’on souhaite transmettre un contenu de type « RAW » au format JSON et appeler le endpoint avec l’ID de notre nouveau produit (ce n’est pas l’idéal bien sûr, car le principe d’un nouvel ID c’est qu’il soit généré directement par le code de l’API, mais c’est pour illustrer mon exemple).
Ensuite pour la mise à jour avec le verbe PUT, ça donne ça…
Et pour la suppression, on obtient ce genre de retour :
Vous l’aurez compris, ce n’est vraiment pas compliqué à développer. Ici on est sur un exemple simple, mais ensuite, vous devrez pourquoi pas sortir vos données d’une base de données, penser à correctement gérer les erreurs et faire preuve de rigueur dans la structure de vos endpoints. Par exemple, dans mon endpoint, l’URI est xxxx/product/. J’aurais pu faire mieux en respectant la convention et en mettant/products/ au pluriel. Car en cas de GET, je peux aussi demander à récupérer l’ensemble des produits existants. Dans ce cas, je dois modifier ma fonction GET comme ceci :
def get(self, id):
if (id == 0):
return products, 200
else:
for product in products:
if (id == product["id"]):
return product, 200
return "Product not found", 404
Ainsi, en passant l’id 0 lors de mon GET, je récupérerai alors tous les produits.
N’oubliez pas également de documenter l’ensemble de votre API afin de vous y retrouver et surtout d’indiquer à d’autres développeurs, comment l’intégrer. Penchez vous également sur l’aspect HATEOAS pour intégrer les actions disponibles dans les réponses de l’API REST. Pour notre exemple, on pourrait ainsi avoir quelque chose qui ressemble à ça :
"links": [
{"rel": "product", "method":"post", "href":"http://example.org/product/1"},
{"rel": "product", "method":"put", "href":"http://example.org/product/1"}, ...
]
Voilà, j’espère que cette petite initiation et explications aux API RESTful vous aura intéressé et donné envie de vous y mettre plus sérieusement. Peut-être changer un peu votre façon de coder, voire carrément changer de job pour trouver une équipe plus agile et plus au fait des pratiques de dev modernes. Et pourquoi pas en profiter pour continuer à évoluer dans les technologies, ou obtenir un meilleur salaire. C’est une démarche qui peut s’avérer assez compliquée, stressante parfois, tant on a envie de trouver un travail qui nous corresponde. On peut souhaiter avoir un travail plus proche de chez soi voire en télétravail complet, un meilleur salaire, une meilleure ambiance au travail, ou même tout ça à la fois (mais ce n’est pas forcément facile à trouver).
Heureusement, talent.io est là pour vous aider. Il vous suffit de créer un compte sur la plateforme talent.io en quelques clics pour ensuite recevoir des offres de la part d’entreprises qui correspondent à vos critères précis et qui affichent un salaire d’entrée de jeu. talent.io est vraiment le moyen le plus simple de trouver son prochain job tech, d’ailleurs les inscrits trouvent leur emploi en 20 jours en moyenne.
As you might know, I have been developing, providing, and supporting the prioritization tool 1st things 1st. One of the essential features to implement was exporting calculated priorities to other productivity tools. Usually, building an export from one app to another takes 1-2 weeks for me. But this time, I decided to go a better route and use Zapier to export priorities to almost all possible apps in a similar amount of time. Whaaat!?? In this article, I will tell you how.
The no-code tool Zapier takes input from a wide variety of web apps and outputs it to many other apps. Optionally you can filter the input based on conditions. Or format the input differently (for example, convert HTML to Markdown). In addition, you can stack the output actions one after the other. Usually, people use 2-3 steps for their automation, but there are power users who create 50-step workflows.
The input is managed by Zapier's triggers. The output is controlled by Zapier's actions. These can be configured at the website UI or using a command-line tool. I used the UI as this was my first integration. Trigger events accept a JSON feed of objects with unique IDs. Each new item there is treated as a new input item. With a free tier, the triggers are checked every 15 minutes. Multiple triggers are handled in parallel, and the sorting order of execution is not guaranteed. As it is crucial to have the sorting order correct for 1st things 1st priorities, people from Zapier support suggested providing each priority with a 1-minute interval to make sure the priorities get listed in the target app sequentially.
The most challenging part of Zapier integration was setting up OAuth 2.0 provider. Even though I used a third-party Django app django-oauth-toolkit for that. Zapier accepts other authentication options too, but this one is the least demanding for the end-users.
OAuth 2.0 allows users of one application to use specific data of another application while keeping private information private. You might have used the OAuth 2.0 client directly or via a wrapper for connecting to Twitter apps. For Zapier, one has to set OAuth 2.0 provider.
The official tutorial for setting up OAuth 2.0 provider with django-oauth-toolkit
is a good start. However, one problem with it is that by default, any registered user can create OAuth 2.0 applications at your Django website, where in reality, you need just one global application.
First of all, I wanted to allow OAuth 2.0 application creation only for superusers.
For that, I created a new Django app oauth2_provider_adjustments
with modified views and URLs to use instead of the ones from django-oauth-toolkit
.
The views related to OAuth 2.0 app creation extended this SuperUserOnlyMixin
instead of LoginRequiredMixin
:
from django.contrib.auth.mixins import AccessMixin
class SuperUserOnlyMixin(AccessMixin):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_superuser:
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
Then I replaced the default oauth2_provider
URLs:
urlpatterns = [
# …
path("o/", include("oauth2_provider.urls", namespace="oauth2_provider")),
]
with my custom ones:
urlpatterns = [
# …
path("o/", include("oauth2_provider_adjustments.urls", namespace="oauth2_provider")),
]
I set the new OAuth 2.0 application by going to /o/applications/register/
and filling in this info:
Name: Zapier
Client type: Confidential
Authorization grant type: Authorization code
Redirect uris: https://zapier.com/dashboard/auth/oauth/return/1stThings1stCLIAPI/
(copied from Zapier)
Algorithm: No OIDC support
If you have some expertise in the setup choices and see any flaws, let me know.
Zapier requires creating a test view that will return anything to check if there are no errors authenticating a user with OAuth 2.0. So I made a simple JSON view like this:
from django.http.response import JsonResponse
def user_info(request, *args, **kwargs):
if not request.user.is_authenticated:
return JsonResponse(
{
"error": "User not authenticated",
},
status=200,
)
return JsonResponse(
{
"first_name": request.user.first_name,
"last_name": request.user.last_name,
},
status=200,
)
Also, I had to have login and registration views for those cases when the user's session was not present.
Lastly, at Zapier, I had to set these values for OAuth 2.0:
Client ID: The Client ID from registered app
Client Secret: The Client Secret from registered app
Authorization URL: https://apps.1st-things-1st.com/o/authorize/
Scope: read write
Access Token Request: https://apps.1st-things-1st.com/o/token/
Refresh Token Request: https://apps.1st-things-1st.com/o/token/
I want to automatically refresh on unauthorized error:
Checked
Test: https://apps.1st-things-1st.com/user-info/
Connection Label: {{first_name}} {{last_name}}
There are two types of triggers in Zapier:
The feeds for triggers should (ideally) be paginated. But without meta information for the item count, page number, following page URL, etc., you would usually have with django-rest-framework
or other REST frameworks. Provide only an array of objects with unique IDs for each page. The only field name that matters is "id" – others can be anything. Here is an example:
[
{
"id": "39T7NsgQarYf",
"project": "5xPrQbPZNvJv",
"title": "01. Custom landing pages for several project types (83%)",
"plain_title": "Custom landing pages for several project types",
"description": "",
"score": 83,
"priority": 1,
"category": "Choose"
},
{
"id": "4wBSgq3spS49",
"project": "5xPrQbPZNvJv",
"title": "02. Zapier integration (79%)",
"plain_title": "Zapier integration",
"description": "",
"score": 79,
"priority": 2,
"category": "Choose"
},
{
"id": "6WvwwB7QAnVS",
"project": "5xPrQbPZNvJv",
"title": "03. Electron.js desktop app for several project types (42%)",
"plain_title": "Electron.js desktop app for several project types",
"description": "",
"score": 41,
"priority": 3,
"category": "Consider"
}
]
The feeds should list items in reverse order for the (A) type of triggers: the newest things go at the beginning. The pagination is only used to cut the number of items: the second and further pages of the paginated list are ignored by Zapier.
In my specific case of priorities, the order matters, and no items should be lost in the void. So I listed the priorities sequentially (not newest first) and set the number of items per page unrealistically high so that you basically get all the things on the first page of the feed.
The feeds for the triggers of (B) type are normally paginated from the first page until the page returns empty results. The order should be alphabetical, chronological, or by sorting order field, whatever makes sense. There you need just two fields, the ID and the title of the item (but more fields are allowed too), for example:
[
{
"id": "5xPrQbPZNvJv",
"title": "1st things 1st",
"owner": "Aidas Bendoraitis"
},
{
"id": "VEXGzThxL6Sr",
"title": "Make Impact",
"owner": "Aidas Bendoraitis"
},
{
"id": "WoqQbuhdUHGF",
"title": "DjangoTricks website",
"owner": "Aidas Bendoraitis"
},
]
I used django-rest-framework
to implement the API because of the batteries included, such as browsable API, permissions, serialization, pagination, etc.
For the specific Zapier requirements, I had to write a custom pagination class, SimplePagination
, to use with my API lists. It did two things: omitted the meta section and showed an empty list instead of a 404 error for pages that didn't have any results:
from django.core.paginator import InvalidPage
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
class SimplePagination(PageNumberPagination):
page_size = 20
def get_paginated_response(self, data):
return Response(data) # <-- Simple pagination without meta
def get_paginated_response_schema(self, schema):
return schema # <-- Simple pagination without meta
def paginate_queryset(self, queryset, request, view=None):
"""
Paginate a queryset if required, either returning a
page object, or `None` if pagination is not configured for this view.
"""
page_size = self.get_page_size(request)
if not page_size:
return None
paginator = self.django_paginator_class(queryset, page_size)
page_number = self.get_page_number(request, paginator)
try:
self.page = paginator.page(page_number)
except InvalidPage as exc:
msg = self.invalid_page_message.format(
page_number=page_number, message=str(exc)
)
return [] # <-- If no items found, don't raise NotFound error
if paginator.num_pages > 1 and self.template is not None:
# The browsable API should display pagination controls.
self.display_page_controls = True
self.request = request
return list(self.page)
To preserve the order of items, I had to make the priorities appear one by one at 1-minute intervals. I did that by having a Boolean field exported_to_zapier
at the priorities. The API showed priorities only if that field was set to True
, which wasn't the case by default. Then, background tasks were scheduled 1 minute after each other, triggered by a button click at 1st things 1st, which set the exported_to_zapier
to True
for each next priority. I was using huey
, but the same can be achieved with Celery
, cron jobs, or other background task manager:
# zapier_api/tasks.py
from django.conf import settings
from django.utils.translation import gettext
from huey.contrib.djhuey import db_task
@db_task()
def export_next_initiative_to_zapier(project_id):
from evaluations.models import Initiative
next_initiatives = Initiative.objects.filter(
project__pk=project_id,
exported_to_zapier=False,
).order_by("-total_weight", "order")
count = next_initiatives.count()
if count > 0:
next_initiative = next_initiatives.first()
next_initiative.exported_to_zapier = True
next_initiative.save(update_fields=["exported_to_zapier"])
if count > 1:
result = export_next_initiative_to_zapier.schedule(
kwargs={"project_id": project_id},
delay=settings.ZAPIER_EXPORT_DELAY,
)
result(blocking=False)
One gotcha: Zapier starts pagination from 0, whereas django-rest-framework
starts pagination from 1. To make them work together, I had to modify the API request (written in JavaScript) at Zapier trigger configuration:
const options = {
url: 'https://apps.1st-things-1st.com/api/v1/projects/',
method: 'GET',
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${bundle.authData.access_token}`
},
params: {
'page': bundle.meta.page + 1 // <-- The custom line for pagination
}
}
return z.request(options)
.then((response) => {
response.throwForStatus();
const results = response.json;
// You can do any parsing you need for results here before returning them
return results;
});
For the v1 of Zapier integration, I didn't need any Zapier actions, so they are yet something to explore, experiment with, and learn about. But the Zapier triggers seem already pretty helpful and a big win compared to individual exports without this tool.
If you want to try the result, do this:
Cover photo by Anna Nekrashevich
Baltic is a handsome 1962 vintage tugboat that was built in Norway, where she operated until the 1980s. She’s now in English waters, having been registered in Southampton once renovations were complete. After some initial hull restoration work in France she sailed to the western Ligurian coast in Italy, where it took about five years to complete the work. The boat’s original exterior was restored, while the inside was fully refurbished to the standard of a luxury yacht.
Ulderico Arcidiaco, who coordinated the digital side of Baltic’s makeover, is the CEO of Sfera Labs, so naturally he turned to Raspberry Pi Compute Module 3+ in the guise of Sfera’s Strato Pi CM Duo for the new digital captain of the vessel.
Strato Pi CM Duo is an industrial server comprising a Raspberry Pi Compute Module 3+ inside a DIN-rail case with a slew of additional features. The MagPi magazine took a good look at them when they launched.
The Strato Pi units are the four with red front panels in the cabinet pictured below. There are four other Raspberry Pi Compute Modules elsewhere onboard. Two are identical to the Strato Pi CM Duos in this photo; another is inside an Iono Pi Max; and there’s a Compute Module 4 inside an Exo Sense Pi down in the galley.
Baltic now has fully integrated control of all core and supplementary functions, from power distribution to tanks and pump control, navigation, alarms, fire, lighting, stabilisers, chargers, inverters, battery banks, and video. All powered by Raspberry Pi.
Ulderico says:
“When it was built sixty years ago, not even the wildest science fiction visionary could have imagined she would one day be fully computer controlled, and not by expensive dedicated computer hardware, but by a tiny and inexpensive device that any kid can easily buy and play with to have fun learning.
And, if there is some old-fashioned patriotism in things, the Raspberry Pi on board will surely like the idea of being back under their home British Flag.”
The post Raspberry Pi helped restore this boat to former glory appeared first on Raspberry Pi.
These award-winning, solar-powered audio recorders, built on Raspberry Pi, have been installed in the Borneo rainforest so researchers can listen to the local ecosystem 24/7. The health of a forest ecosystem can often be gaged according to how much noise it creates, as this signals how many species are around.
And you can listen to the rainforest too! The SAFE Acoustics website, funded by the World Wide Fund for Nature (WWF), streams audio from recorders placed around a region of the Bornean rainforest in Southeast Asia. Visitors can listen to live audio or skip back through the day’s recording, for example to listen to the dawn chorus.
The device records data in the field and uploads it to a central server continuously and robustly over long time-periods. And it was built for around $305.
Here’s all the code for the platform, on GitHub.
The Imperial College London team behind the project has provided really good step-by-step photo instructions for anyone interested in the fine details.
The recorders have been installed by Imperial College London researchers as part of the SAFE Project – one of the largest ecological experiments in the world.
Dr Sarab Sethi designed the audio recorders with Dr Lorenzo Picinali. They wanted to quantify the changes in rainforest soundscape as land use changes, for example when forests are logged. Sarab is currently working on algorithms to analyse the gathered data with Dr Nick Jones from the Department of Mathematics.
Let the creators of the project tell you more on the Imperial College London website.
The post Raspberry Pi listening posts ‘hear’ the Borneo rainforest appeared first on Raspberry Pi.
I am getting the following error and my app stops working:
2020/07/12 14:32:03 [error] 45625#45625: *10918 upstream sent too big header while reading response header from upstream, client: 139.xxx.yyy.zzz, server: www.nixcraft.com, request: "POST /app/mg/posts HTTP/2.0", upstream: "fastcgi://unix:/run/php/php-fpm.sock:", host: "www.nixcraft.com", referrer: "https://www.nixcraft.com/mg/admin/index.app"
How do I fix this error for Nginx web server running on Linux or Unix-like systems?
The post Nginx upstream sent too big header while reading response header from upstream appeared first on nixCraft.