Sign in

hughevans.dev

@blog.hughevans.dev.ap.brid.gy
3 followers 0 following 10 posts

Engineering, data, and community. 🌉 bridged from ⁂ hughevans.dev, follow @ap.brid.gy to interact

PostsRepliesMedia
hughevans.dev @blog.hughevans.dev.ap.brid.gy · 15/09/2026
I really like projects that explore the overlap of traditional crafts and computing / digital technology. Some of my favourite examples of this include Madeleine Shepard's hacked knitting machine cellular automata, adversarial print that protects against surveillance cameras, and Charlie’s […]
hughevans.dev
Making a QR Code Quilt, Again
I really like projects that explore the overlap of traditional crafts and computing / digital technology. Some of my favourite examples of this include Madeleine Shepard's hacked knitting machine cellular automata, adversarial print that protects against surveillance cameras, and Charlie’s quiltagon project. Coming back from EMF camp (the most traditional craft x digital technology place in the world) I was inspired to revisit a past project of mine and improve on it.Last year I was looking at the stack of old (black and white) conference T-shirts I had accrued from years and conferences, and was wondering about what to do with them. I had the thought, could I make a quilted QR code? I had no idea what I was doing but I made it, the “quilt” (or QRuilt, thanks Jack) actually scanned but only when stretched in a particular way. This was a “postage stamp” quilt where all the modules were one individual fabric block. I later learned that what I made above isn’t even really a quilt, more of a fancy cushion cover. A quilt comprises three layers: the front; batting, which adds weight and warmth, and the back. Quilting is actually the process of sewing through all three of these layers to join them and ensure the batting is distributed evenly. Facing down another stack of old conference shirts, I decided to try making this project again. This time, I asked for some advice from my quilting-expert: nan; who armed me with a stack of books, quilting rulers, adhesive spray, and curved safety pins; not to mention some much needed encouragement throughout the project! Here’s how it went. ## Making a Plan Rather than making a postage stamp quilt like the last time, I opted for a simpler approach this time: around 25 8x8” white blocks with the black QR code modules appliqued on top. These would then be pieced together to form the QR code with a 2” white border added to avoid any of the black modules being folded over the edge like in my first version. I wasn’t really sure how I was going to finish the edge when I started, but eventually landed on creating a binding out of leftover fabric. I kept a copy of the design of my QR code split into all 25 blocks to hand at all times throughout the project so I could keep track of where I was and make sure that I was putting my blocks together correctly. ## Cutting and Assembling Blocks T-shirt material is not ideal for quilting because, as a knitted material, it is very stretchy and prone to puckering and warping. So, after cutting all my (freshly washed) old t-shirts into usable sections, I ironed on some interfacing to stabilise the fabric. The interfacing provided an interesting opportunity for this project in particular: for the white fabric I just used a simple one-sided interfacing, but for the black fabric I used a double-sided, paper-backed, interfacing which had two main advantages. The paper backing helped provide extra stability to the black fabric whilst I was cutting it and when it came to attaching the black details, the fabric could be placed and pressed to fuse them into place before being appliqued on my sewing machine. I cut all the white blocks out with an 8” quilting square and rotary cutter on a large cutting mat. I did the same for the black details, and then used my Silhouette Cameo 5 vinyl cutter to cut out the black shapes, using SVGs exported from my design in the previous section, re-using as much of the waste pieces as possible to cut down on fabric use. The vinyl cutter did a really good job of cutting through the black fabric face down on a sticky cutting mat and the interfacing backing paper left on. I used a depth of 7, force of around 32, and all other settings set to the default for cutting cotton fabric with the default knife attachment. I had a few hairy moments when the fabric came unstuck from the mat as, unfortunately, cutting fabric quickly removes the stickiness from the mat due to the dust produced. After ironing all of the black details onto the white blocks, I used black thread and a running stitch on my sewing machine to trace the edge of all the black details to make sure they were properly attached. After repeating this process for all 25 blocks, I was ready to start piecing them together. ## Piecing Blocks Piecing these blocks was pretty simple as they were all square and the same size. I pinned the blocks together making sure to line up black modules neatly, used a running stitch to attach the blocks together, and pressed the seams flat as I went to keep things nice and square. Once I’d completed two rows, I trimmed the corner of the end of each seam to remove excess fabric where the corners of four blocks met and then used one long seam to join each row together, repeating this process until I had a finished QR code! Despite my best efforts there were some extremely visible but small gaps between the black modules around the center of the QR code. Towards the end of the project, I used a washable permanent marker to go over them and some errant white thread from quilting and it left the quilt looking much sharper! ## Adding the Border To add the border, I cut 2” wide, 8” long strips of the white; interfacing backed T-shirt fabric and sewed them together along the ends to create two 40” long rectangles and two 52” long rectangles. This was much longer than I needed but meant I could cut all my strips the same and use the rougher / stained ones at the ends. I pinned the shorter strips along the top and bottom of the pieced QR code and then attached them with a straight stitch. I then repeated the process for the two remaining sides with the longer strips, trimming off the excess with a quilting square to get some nice tidy corners for my finished front. ## Making the Quilt Sandwich Next came making the back and cutting the batting. I made a huge rookie error here: I cut the back and the batting to the exact size of the front. This is a bad idea for several reasons but primarily because, as you quilt the front, it spreads out more than the batting and back, even when really well basted. This meant the edges of my finished quilt were missing some batting and there were gaps between the back and the binding in places. For better results I should have left 4” or so of extra material around the edge. This would also have given me the option to finish the edge of my quilt by folding over the back rather than making a binding. Otherwise, this step went pretty smoothly. I made the back by sewing together two large pieces of cream cotton fabric and pressing the seam flat. I basted all three layers together using curved safety pins. This took a while but worked pretty well. I think I may have got better results using the spray adhesive to baste the layers together instead as I definitely had some squishing and stretching in my blocks whilst quilting, but overall I was still happy with the result. ## Quilting This was the part of the project I was most nervous about - stitching through the whole sandwich to lock the layers in place. My nan suggested I try "stitch in the ditch” a quilting technique where you sew on top of the existing seams to hide your quilt on the front side. This was the hardest part of the project by far, and surprisingly physical even for this relatively small quilt. I replaced my sewing machine foot with a walking foot to help reduce the sliding of the quilt front. I sewed around the center square and worked my way out to evenly align the blocks. I did this in bursts and took several breaks. I also had some headaches where the curved safety pins got caught on my walking sewing foot because I’d placed them too close to the seams. I managed to keep my stitches in the existing seams for the most part but some of my wonky piecing from earlier steps made this tricky. My less than steady hands also led to some slips and wonky stitches in places. I also had puckering throughout, particularly on the thicker sections where the seams joining the blocks added bulk, but not so much that I went back to redo it. ## Making and adding binding I made 3 metres of binding and, as it was exactly the right length, attaching the last side was a little nerve racking. I made this by cutting a series of 3” wide strips out of white t-shirt fabric with interfacing leftover from making the blocks and sewing them together. I folded it in half and pressed it and then folded each side into the middle and pressed it again to create my finished binding. I found making the binding weirdly relaxing although it was definitely a little uneven in place and definitely too thick which became an issue in the corners. When it came to attaching the binding I just pinned it to the raw edge of the quilted fabric and then sewed through the binding, front, batting, back, binding sandwich with a straight stitch. At the corners I folded the binding back on itself to create a neat corner but, with the interfacing, this meant the corners got pretty chunky. I worked my way around all four sides like this and was just short of binding to finish the last corner neatly so I just left it rough. ## Final product With the binding attached, all that was left to do was to trim some excess threads and dust off my quilt. I am so happy with the result, not least that the QR code actually scans! Whilst it’s not quite big enough to use as an actual quilt, it is warm and has a nice weight to it. This project was so much better for asking for advice, taking the time to understand the techniques that make a proper-quilt, and generally, at least aspiring to, doing things “the right way” as opposed to making things up as I go as I’m usually inclined to do. I’m already excited to start my next quilting project. I really enjoyed putting this together and learnt so much for next time. I’m toying with the idea of building a tool for automatically creating SVGs and a PDF pattern for QR quilts, if that’s something you’re interested in let me know.
021
hughevans.dev @blog.hughevans.dev.ap.brid.gy · 11/06/2026
PyData London is a conference bringing together data scientists and engineers from all over the world for workshops and talks covering everything from document parsing to weather forecasting. This was my fourth year volunteering and I was really excited for the conference, not least because […]
hughevans.dev
My PyData London 2026 Conference Highlights
PyData London is a conference bringing together data scientists and engineers from all over the world for workshops and talks covering everything from document parsing to weather forecasting. This was my fourth year volunteering and I was really excited for the conference, not least because Aiven would be sponsoring this year which was a great opportunity to give back to a community that means so much to me. I went into the conference hoping to learn something and catch up with friends old and new, as well as seeing some of the folks I met on my tour of UK PyData groups. Now that the dust has settled (and I’ve had some time to recover from a packed weekend) I’ve written up my highlights from the event. ## Friday (Workshops) After arriving bright and early with a cab full of last year's conference supplies (97 PyData rubber ducks anyone?) I settled in for a day of workshops. It was difficult to pick between the three tracks but I ended up going to the Docling workshop, the MCP OTel workshop, and Cheuk’s workshop about LightEval—all of which were fantastic, although the Tom Nook “Tanuki” MCP from Fei and Tun’s workshop definitely stole my heart and Cheuk’s workshop was by far the most challenging.We rounded out the day with the social at Fleets I (badly) organised (mentioning that there was a tab before we arrived would have been smart, sorry John S and John C who kindly sponsored)! ## Saturday Saturday kicked off with an amazing keynote from Rachel-Lee Nabors which was a much needed story about the winding paths careers can take, the importance of community, and hope in a time of layoffs. I’m extremely grateful to Rachel-Lee for speaking, I can’t overstate the positive impact this talk had and I know I wasn’t alone in coming away inspired and hopeful. I spent the rest of the day on the Aiven booth having conversations about our work on Diskless Kafka and our free and developer tiers. I got to chatting with MK from NumFOCUS about my PyData maps project and his awesome Give Me 5 series. It seemed like by Saturday (almost) everyone had figured out the badge maker Aiven sponsored, and every time I walked past I could see people making a badge which was really cool. Likewise our Disk Invaders arcade cabinet definitely captured people’s attention with no fewer than 82 games played over the weekend. I was also interviewed by Cheuk along with some friends for her YouTube channel about our experiences at PyData London. We wrapped up the day with a social event in the venue and then I grabbed some dinner with the other organisers nearby. ## Sunday I had the pleasure of MCing the lightning talks Sunday morning which was a real treat and we had some fantastic speakers, all of whom kept to the 5 minute time limit like absolute pros. We didn’t make it through everyone who signed up but everyone who didn’t get a chance to speak has been invited to speak at the PyData London Meetup. At lunch the PyData organisers meetup quickly ran out of chairs and we ended up hunting for more. There were some great opportunities and having 30+ UK PyData organisers all in one place meant it was easy to work through the problems some organisers are facing. It was also a great reminder of just how big the UK PyData community is. After lunch I went along to Damian’s talk exploring the correlation between NIMBYism and cancelled renewable energy projects. This was my third time seeing this talk and I enjoy it more every time! The last talk of the event I saw was Adam Hill’s talk on creating quizzes based on PDFs with LLMs as judges. ## Looking Forward to 2027 As ever this year the volunteers were amazing and put on a fantastic event that I was proud to be a part of. I'm really looking forward to next year's event. The conference closed out with the volunteer dinner, where we celebrated another successful event and the bittersweet end of John Carney’s time as conference chair. John is a lovely guy and has done a fantastic job these past few years, but I know we’re in safe hands with Prashant and Jiarui as our chairs for 2027.See you at the next one! And if you want some PyData in the meantime, why not check out your local group?
011
hughevans.dev @blog.hughevans.dev.ap.brid.gy · 25/03/2026
I have a habit of setting myself ambitious if slightly wacky goals: riding the London to Brighton bike ride, building an ASCII art photobooth, pursuing a career in devl rel etc and my latest is no exception. One of my aims for 2026 was to give back to the Python community and to that end I’m […]
hughevans.dev
The Tour de PyData: Challenging Myself to Speak at Every PyData Meetup in the UK and Ireland
# I have a habit of setting myself ambitious if slightly wacky goals: riding the London to Brighton bike ride, building an ASCII art photobooth, pursuing a career in devl rel etc and my latest is no exception. One of my aims for 2026 was to give back to the Python community and to that end I’m helping to organise meetups for PyData London, running socials at this year's PyData London conference (literally organising a piss up in a brewery) but I wanted to do more. > PyData, for those who are unaware, is the educational/community arm of NUMFocus, an organisation that supports many open-source scientific and data science Python projects like Scipy and Pandas. PyData has groups all over the world hosting regular meetups on a variety of topics.As I covered in my blog post on the topic I’ve long been equally inspired and frustrated by the map of PyData Meetups: frustrated by its inaccuracies (and, as I’ll dig into, incompleteness), and inspired to visit some of those groups. After a few months of careful preparation, tentatively reaching out to other PyData organisers across the UK, and ultimately making my own map(s) of PyData groups I was ready to begin my journey. ## The Tour de PyData So here’s the plan: I’m going to try and speak at as many PyData Meetups across the UK and Ireland as possible this year. My plan for this little adventure is both to see some places I’ve never visited before (after two years in dev rel I’ve been to Charlotte airport more than I’ve been to Scotland) and to help promote something really cool: across the UK and Ireland are more PyData groups than anywhere else in the world. I’ll be documenting my journey here and doing my best to showcase the depth and breadth of the wonderful PyData community. What better time to call attention to our awesome community than in 2026 with community budgets tightening across the board and volunteers needed more than ever? Above is yet another map I cooked up, this one listing all the PyData groups I’m scheduled to speak at and charting my progress. To make this challenge even close to possible I’m limiting the list of groups I plan to speak at to groups that are regularly hosting events, bringing the total number of groups down from 21 to 15. A huge thank you to all the PyData organisers who have invited me to speak at their groups already! ## Another Problem With The Meetup Map When I last spoke about this project I outlined four major issues I had with the official PyData group map on Meetup: it’s impossible to see how many meetups are in the same city, several groups are in the wrong location, it’s unclear if groups are still active, and every group is shown dozen of miles north of its actual location. After the talk Stelios Christodoulou raised an issue on my GitHub repo for the project that would expose a fifth, even more significant, problem. Stelios pointed out I was missing PyData Edinburgh from my map. Initially I kicked myself, wondering what I’d missed in my webscraping code that had caused me to miss a group as large as Edinburgh, before realising this was in fact a symptom of a wider problem: **_not every PyData group is included in the official map!_** I was able to write a hacky script to run through every major city on Earth looking for PyData groups and turned up some interesting results: 7 PyData groups are missing from the official map: Tokyo, Abu Dhabi, Vilnius, Krakow, Poznan, Basel, Lausanne, and of course, Edinburgh. Why are these groups missing from the official map? It’s unclear but all of these groups have been inactive for a while, which might explain why. My search also turned up some interesting _former_ PyData groups like Software Talks Lancaster, which still have “PyData” group URLs but seem to have moved onto new topics. ## Grand Depart I’m set to get started on my Tour de PyData later this week: starting off with the wonderful PyData Manchester, the UK’s second largest PyData group, and PyData Hull, the newest. Of course the obvious question is what do I plan on speaking about 15 times? The PyData mapping project! A little meta though it may be this project is a great fit for PyData events, as it features: data engineering, web scraping, geoencoding, and of course Python. Now, with my map of PyData groups more complete than ever, I’m excited to share it with the community. I’m really looking forward to spending some time with the PyData community this year, and hopefully to help encourage some folks to support their local PyData group. Groups always need support in the form of speakers, organisers, venues, sponsors, and in general just people to show up and build the community. Are you up for the challenge of speaking at every PyData Group in the UK and Ireland in a year? Feel free to fork my map and create your own if you like. I’ll be updating on the progress of my journey here, watch this space and see you at a local PyData soon!
041
hughevans.dev @blog.hughevans.dev.ap.brid.gy · 20/03/2026
Follow up video to my blog on my homelab getting hacked, covering how I secured Umami with Tailscale. Photo credit Ellie Geddis.
hughevans.dev
Securing My Homelab With Tailscale
Follow up video to my blog on my homelab getting hacked, covering how I secured Umami with Tailscale. Photo credit Ellie Geddis.
122
hughevans.dev @blog.hughevans.dev.ap.brid.gy · 16/03/2026
I've been into homelabbing for a few years now. For those unaware, a homelab is a self managed IT environment, typically used for hosting apps and learning, which in practice usually means a hodgepodge of compute, storage, and networking. Here's my homelab: Here's the parts of my homelab […]
hughevans.dev
A Homelab Cautionary Tale: How Crypto Scammers Hacked My Analytics Dashboard
# I've been into homelabbing for a few years now. For those unaware, a homelab is a self managed IT environment, typically used for hosting apps and learning, which in practice usually means a hodgepodge of compute, storage, and networking. Here's my homelab: Here's the parts of my homelab actually plugged in and doing meaningful(ish) work: In my defence the desktop is the only Windows box in my flat and I'm damned if I'm unracking that UPS, even if the batteries have long since ceased to hold charge. The three clustered Raspberry Pi 4s provide me with a decent enough environment to play around. I use my homelab to self-host things like my website, HomeAssistant, personal projects, and analytics to see if anyone is using any of the former. I had fun gradually bodging this together over the years until eventually my lacklustre approach to system administration best practices came back to bite me. ## **Discovering the Hack** Cryptocurrency and all other financial instruments with the crypto prefix are in my view at best ponzi schemes and legal sleight of hand to dodge gambling regulations and at worst climate destroying instruments of global corruption and crime. I'd thoroughly recommend _Molly White's_ writing if you’re interested in what a mess the crypto industry is. With all that said, you could imagine I was particularly annoyed when checking my web analytics dashboard to find that not only had someone hacked my homelab: they had somehow managed to inject a crypto casino ad into the web app html. I didn't actually get a screenshot of this but it looked a little something like this: After recovering from the digital equivalent of finding a stranger in my living room, I started digging into how exactly this happened. ## **The Vulnerability** A quick web search turned up _an issue on GitHub describing exactly what I was seeing_. In short, a vulnerability in _Next.js_ (_CVE-2025-29927_) allowed attackers to access compromised systems with Umami installed. Two things became immediately clear: one, that I had been very lucky, the scope of the impact on my homelab was very limited; two, I was late to the party–I hadn't noticed this issue until late February 2026, despite the vulnerability having been known about since December 2024. ## **Assessing the Blast Radius** I deployed apps to my homelab with Docker, which limited the attacker's access to the Umami container. Whilst the attacker gained authenticated access to my Umami dashboard, which allowed them to insert the gambling popups visible to anyone viewing that dashboard, the host server was not compromised. After stopping the container, and carefully inspecting its contents, I could see that the attacker had injected a malicious _middleware.ts_ file into the file system. This file seemingly would have caused more serious issues, but luckily the injection was partially blocked by file permission restrictions on the container. I'd like to chalk this up to me implementing **_robust zero-trust across best practices™_** across my entire homelab but this was mostly dumb luck. After figuring out how the attack had compromised my Umami container I stopped it and did some digging around in the filesystem to understand the impact. Having seen in the GithHub issue evidence of malware more serious than just adding ads to my dashboard, I gave the host a thorough check to make sure it hadn't been compromised: I looked for evidence of this in the form of new shell profiles, cron jobs, and any newly created files. All in all, after a tense couple of hours spent inspecting my homelab I was able to confirm that the hosts and other containers were completely unaffected. The analytics data in the Umami database showed no evidence of tampering, and visitor data across my personal projects had not been accessed. ## **Remediation** Thanks to Docker limiting the spread of the attack, remediation was fairly simple. I deleted the compromised container, rotated my Umami database credentials, and pulled the patched image: docker rm umami docker image pull umami I then recreated the container with the direct port 3000 exposure removed from my Docker Compose file and updated the admin password. I also checked in on all my other containers for any other outstanding CVEs and updated them across the board. ## **Lessons Learned** Despite having spent years being the "_pin your versions_" guy at work, I was running containers on :latest tags with no update monitoring, which led to being five months behind on the patch that would have protected me from this issue. Whilst Docker container isolation was the key factor that prevented this from being a full server compromise, I learned that I need to be more careful with my homelab security: no more default passwords for me! Some next steps to further secure my homelab are adding a basic nginx authentication layer in front of my Umami dashboard and other private services, and setting up container update monitoring (potentially with _Diun_, which looks promising) to avoid missing another CVE for the best part of six months.
012
hughevans.dev @blog.hughevans.dev.ap.brid.gy · 25/02/2026
Gathering data, building maps, and the places that it can take you I really like maps. I collect (mostly transit) maps and, when sitting down to write this blog, I quickly ran out of fingers counting all the maps up on the wall in my flat. Maps have an ability to inspire us to explore the world […]
hughevans.dev
Mapping the PyData Community
_Gathering data, building maps, and the places that it can take you_ **I really like maps.** I collect (mostly transit) maps and, when sitting down to write this blog, I quickly ran out of fingers counting all the maps up on the wall in my flat. Maps have an ability to inspire us to explore the world around us, within them containing just enough information to travel an unknown landscape whilst still leaving enough blank space to inspire the imagination. I also really like being a part of the PyData community having spent the past few years attending and later helping to organise PyData London. PyData, for those who are unaware, is the educational/community arm of _NUMFocus_, an organisation that supports many open-source scientific and data science Python projects like Scipy and Pandas. PyData has groups all over the world hosting regular meetups and each PyData London meetup starts with a slide f _eaturing this map of those communities_. Odds are if you’ve been to a PyData event before it was PyData London as with 16,000 members it’s the largest PyData community in the world. PyData UK is much more than just a mega-meetup in the capital though, in fact the UK has more PyData groups than any other country in the world with a whopping 20 groups, narrowly beating out the US where there are 19. This abundance of groups means there’s likely a PyData community near you. Meetup’s search feature has long been a bit of a joke so if you want to find your nearest PyData community (or any Meetup community for that matter) your only real option is to use the map Meetup provides. And that map has a problem. * * * ## Meetup’s _Map_ is Terrible Despite its flaws I’ve always found this map particularly inspiring, much like _pythondeadlin.es_ and other similar resources, it showcases the sheer scale of the international Python community, and serves as something of an invitation to go seek out like-minded people the world over. For all that inspiration though, there's a major issue with it: it’s built and maintained by Meetup. _I’ve spent a lot of time complaining about Meetup over the last few years_ (no hate to the folks actually building the Meetup platform, I’m sure it’s very difficult trying to get work done in the kind of environment private equity takeovers create). It's a tool I rely on heavily as a community organiser, but if we zoom in specifically on the UK position of the map we can see the issues with Meetup’s map of PyData communities. Immediately obvious is this issue of how Meetup chose to display the locations of each group with every marker sitting dozens of miles north of the group's actual location. Closer inspection reveals some more glaring issues: some groups like PyData Katsina are shown in entirely the wrong location (not just a few miles off the mark but in fact on the wrong continent), and it’s impossible to see if there is more than one group in the same city.Rather than fall back on my usual shtick of complaining about Meetup I decided to build my own improved version of this map. I was motivated to do this because of what this map, despite its flaws, inspired me to do which was to see how many UK Meetups I could actually speak at or attend: PyData London is an awesome community and the promise contained within this map is two dozen other communities of like minded people all over the UK. This particular goal required some data that was currently entirely absent from this map: which groups are actively hosting events and how do you submit a talk. I’ve previously spoken at PyData Southampton but this project would require more preparation than a friendly email to Adam Hill, so I set out to make my own maps of the PyData community that addressed the issues with the Meetup map and would provide me with the information I needed to plan my little adventure. * * * ## Making My Own Maps ### Collecting Group Data I had a couple of options for collecting the data I would need to produce my maps: use the Meetup API (which I have access to as a paid up Meetup Pro organiser), or scrape the data directly from the Meetup website (_as permitted by their robots.txt_). Meetup deprecated their REST API in 2025, _which in turn killed off various third-party wrappers_, and their docs now start with a _“GraphQL good” screed_ explaining their decision to do this. Apart from that somewhat jarring piece of product management, the Meetup API is actually fairly robust and revisiting it, I found that for getting groups from the PyData network you could easily use a GraphQL query like:        query($cursor: String) {           proNetwork(urlname: "pydata") {             groupsSearch(input: { first: 20, after: $cursor }) {               totalCount               pageInfo { endCursor hasNextPage }               edges {                 node {                   id                   name                   urlname                   city                   memberships { totalCount }                 }               }             }           }         } Granted this requires _negotiating the Oauth process_ and refreshing tokens periodically. I also particularly like their _GraphQL Playground_ in the API Docs which uses your logged in Meetup account to give you access to a live GraphQL sandbox where you can experiment with queries, this is a nice touch and for me it’s a sign that there are actually engineers at Meetup who care about user experience. Ultimately though for simplicity I opted to just scrape everything I needed for my maps work from a local copy of the group data in a Pandas dataframe. Whilst the API does make it relatively easy to request some of the data I needed, like group names and upcoming events, it doesn’t have some of the data that was crucial for building maps of active groups, namely time since last event, and total number of past events. Scraping from Meetup brings its own challenges. The list of groups on Pro Network pages is loaded dynamically. My code for scraping from the Meetup group list is sprawling and clunky (_you can check it out on GitHub here_) but boils down to loading the page with PlayWright, spoofing scrolling inputs and periodically dumping the static HTML to get a complete list of all the groups in the PyData Pro Network. I also used a similar approach to scrape the past and upcoming events for each individual group so I could collect the activity data I wanted. ### Data Preparation After scraping the data some preparation was needed to make it usable for mapping. The main issue (as I previously mentioned) is that the latitude and longitude for each group is incorrect when scraped from Meetup, and the city field is also junk because for some groups it was populated wrong i.e. PyData Katsina’s city field is set to Norwich. This meant that the only “accurate” location information for groups on Meetup is the group name itself. Most PyData groups are named something like “PyData <location>” i.e. PyData London, PyData Boston etc so I was able to use string replacement to remove common phrases like “PyData” to get just the city name string. I could then geoencode the city name to return plottable co-ordinates with _geopy_. This worked great apart from a few notable exceptions like PyData En Español Global and PyMc Global which don’t have physical locations and some groups which ignore the naming convention entirely, I’m looking at you “Datenanalyse, Data Science und Statistik - PyData Dortmund”. {   "hints": {     "PyMC Online Meetup": null,     "PyData En Espa\u00f1ol Global.": null,     "NEO AI - a PyData Group": "Cleveland, Ohio, USA",     "PyData Ireland": "Dublin, Ireland", … I got around this by adding a list of “hints” for the geoencoder which allowed me to flag which online only groups should be ignored and create aliases for other groups where required. The only other data preparation required was a simple join of all group lists and individual group data (i.e. past event count, total members) which was easy enough with Pandas. ### Bonus: Playing with the Dataset One fun upshot of going to the effort of scraping, enriching, and preparing the PyData community data was that it allowed me to easily interrogate the data in a _Marimo_ notebook. You can clone the repo to play around with the data yourself but here are a few insights I found particularly interesting: **Top 10 largest PyData groups by members** Group| City| Events| Members ---|---|---|--- PyData London Meetup| London| 123| 16,263 PyData Berlin| Berlin| 138| 9,506 PyData NYC| New York| 88| 8,041 PyData Amsterdam| Amsterdam| 99| 6,576 PyData Chicago| Chicago| 134| 6,191 PyData Seattle| Bellevue| 99| 4,199 PyData Atlanta| Atlanta| 126| 3,911 PyData Manchester| Manchester| 115| 3,866 Data Engineering Pilipinas - a PyData group| Mandaluyong| 75| 3,839 PyData PDX| Portland| 106| 1,471 **Top 10 countries by total PyData members** Country| Members ---|--- United States| 37,762 United Kingdom| 30,283 India| 26,088 Deutschland| 19,409 Brasil| 11,471 ישראל| 9,361 Canada| 8,495 Nederland| 7,729 Polska| 7,073 Singapore| 5,767 **Top 10 countries by total PyData groups** Country| Groups ---|--- United Kingdom| 20 United States| 19 India| 8 Nigeria| 7 Deutschland| 6 Brasil| 5 España| 5 Italia| 4 Australia| 3 Canada| 3 ### Plotting Maps For plotting the maps I elected to use _Folium_. Folium has its limitations but is well suited for easily creating custom markers to make the types of maps I wanted. Based on data scraped from www.meetup.com/pro/pydata, last updated... The first map I plotted is this one with plain markers for each PyData and PyData affiliated group closely mimicking the existing PyData group map on meetup. The only difference is the location of groups is plotted as the geocoded name. Making this second map was a decent chunk of the motivation for this entire project. My goal was to make it easy at a glance to spot active groups to speak at or attend, and to identify dormant or inactive groups that may require support (although I later decided to split that last part into the third map below). To that end I set up custom markers: the marker radius is scaled with the number of group members, the markers become fainter the longer it has been since the last event was scheduled, blue groups have no events scheduled, and green groups have an event scheduled. The blue / green distinction I added in later so brand new groups wouldn’t appear as faint but instead would be extremely prominent on the map, I added this feature after spotting the new PyData Hull group that was created during this project. This final map shows groups that haven’t hosted an event in 100 days in bright red. I decided to make it because the map above by design makes groups like these almost invisible and I felt it would be useful to specifically draw attention to groups that may need support. I landed on 100 days as even mostly dormant groups that still had active organisers would have uploaded the link to _last year's PyData global_, which at time of writing was 77 days ago, meaning that it is likely that these groups are in desperate need of organisers or support in order to host events in future. ### Publishing and Keeping Maps Up to Date Up until this point in the project I’d been hacking away in a Marimo notebook which took up to 15 minutes to scrape the data from Meetup and produce the maps. Before I shared the maps I wanted to make sure they would stay up to date with the data available from Meetup. To this end I created a Python script that would spit out the maps as static HTML that I could serve from GitHub pages and then set up a GitHub action to run the script every 24 hours. I also added some basic caching to handle edge cases when the scraping timed out. I also went back and updated the maps at this point to allow specific zooms and views to be saved in the URL to make it easy to create country or region specific maps. * * * I had a lot of fun with this project and these maps did end up being useful for thinking about a potential tour of UK PyData groups, even if it did mostly reveal the scale of that challenge. From a technical perspective I wouldn’t use Folium if I were to do this again because of limited options for maps in different languages, ideally I would like to create localised views of maps for each PyData group.As part of this project I presented my work as a lightning talk at PyData London, and subsequently the group used my map to replace the existing one (as it turned out I wasn’t the only one annoyed by the locations being wrong on the original meetup map) which was really cool, it’s always nice to see tools you build getting adopted. I’m proud to be a part of the international PyData community, but this project has made me especially proud to be part of the UK PyData community: I’ve learned so much by coming along to PyData over the last few years and I know I’m not alone in that. The UK PyData community is an amazing resource that we’re lucky to have but it only exists because of the time and effort our community invests in it, especially the awesome organisers of these groups, some of whom I’ve been lucky enough to meet at the PyData London conference organisers lunch over the years. **If you take one thing away from this blog: if your local PyData group hasn’t hosted an event in 100 days, please consider supporting them either through volunteering, speaking, sponsoring, or otherwise participating in building this awesome community.** If you aren’t inspired by maps to seek out regional PyData communities, I’m sure meeting the organisers of these groups would, they’re great people! Watch this space for updates on my journey to as many UK PyData meetups who will have me. If you’re an organiser and you’d like me to speak, give me a shout!
011
hughevans.dev @blog.hughevans.dev.ap.brid.gy · 19/12/2025
hughevans.dev
Everything I made in 2025
Revisiting 2025 through projects made.
011
hughevans.dev @blog.hughevans.dev.ap.brid.gy · 10/12/2025
hughevans.dev
How I discovered pigeons sabotaging my project with Aiven Free-Tier Kafka
How I used Aiven's free Kafka tier to monitor my smart bird feeder in real-time
011
hughevans.dev @blog.hughevans.dev.ap.brid.gy · 30/11/2025
hughevans.dev
Migrating from Jekyll to Ghost Blog
I decided to migrate my simple static Jekyll site to Ghost Blog: a powerful open-source blog and newsletter platform.
011
hughevans.dev @blog.hughevans.dev.ap.brid.gy · 22/11/2025
hughevans.dev
Big Data Europe 2025 Highlights
I was lucky enough to attend and speak at this years Big Data Europe event in Vilnius, Lithuania.
021