DTTH Blog

Reader

Read the latest posts from DTTH Blog.

from Zumi's Blog

I got some motivation to move away from Gmail, after a lot of procrastinating.

Of course Gmail is kind of the devil's service here — from the data processing and hoarding from users, to AI training rumors and everything else. That said I've owned my email address from 2014 and have signed up for countless (throwaway sometimes) accounts, what should I do to deal with this elephant in the migration room? — This question has been keeping me from doing anything with the account for several years.

That said, I got a lot of motivation to just do things this year, after a happy vacation to Korea... So I decided, let's give it a try anyway. My Gmail inbox has always been a disaster nevertheless, with countless garbage advertising, invitation, random update emails that completely drown me from seeing actual, useful emails. If we're doing the migration, might as well do it properly.

My plan is going to be – Import all emails from Gmail, manually categorizing and cleaning up as much as I can – Nuke all my emails from the Gmail account, to make sure I have a case against Google if they keep them

Some statistics: – My first email in Gmail dates back as far as 2013 – The number of emails in archive was about 107.000(!) – I have about 30.000 unread emails (sigh)

These adds up to about 7GB of emails (yikes), and so if I wanted to move my emails elsewhere, I need a service that was capable of storing and indexing that much.

Choosing Fastmail

To be honest, not much to say about why here. I've tried a bit of Fastmail before with my now self-hosted email address, but found them a bit expensive at the time (I was a poor student — well I still am, but at least I got some salary...) The service was good, and they seem to be trusted by many people on this Lobste.rs thread. They also support fully importing emails from Gmail as well, which is something I really wanted to do.

The standard single plan gives 50GB of emails, one personal inbox with multiple aliases, alongside the usual Contacts (which I don't use) and Calendar (which I do use). Costs 60CHF / year, fine, a bit expensive but it's probably worth it for the privacy.

I pulled the plug and signed up for one year.

Importing from Gmail

The very first thing I wanted to perform was to pull in all my existing emails. It was actually very simple from Fastmail: open settings, import, from Gmail, grant Fastmail all the email/contacts/calendar access to my google account. The rest happens automatically, although at not very fast speed: it took about 3 hours to move all 107.000 emails.

Fastmail was very useful at automatically setting up forwarding future emails from Gmail, as well as letting me send emails from Fastmail as if I was sending it from the Gmail address.

Managing this mess

To be frank, I have always wanted to try Inbox Zero for a while, but it's a bit difficult to reach from 30.000 unread emails down to 0... Or at least, not without some dramatic measures.

My first thought was: okay, we cannot go through all the emails, so let's just bite the bullet and archive most of them. As we are fairly close to the beginning of 2026 still, I decided that it's a good cut-off: let's just archive all emails before 2026, and store them in case I needed access to them later on. Doing that leaves me with about 400 emails(!), which I went into one by one.

Before talking about these emails, I should mention that Fastmail has a labeling system that seems to be translated into IMAP folders: you can assign multiple labels to the same email, and they seem to be stored in the first label assigned. Furthermore, unlabeled emails (Inbox automatically gets an #inbox label) are considered “archive”. One neat feature: you can configure, per-label, how long the emails are going to be kept. They don't affect starred mails however, so that's an easy override for single emails.

Of the rest: – About 75 were GitHub notification emails. I want to have them, but I don't need to keep them. I did some light filtering (Scala and research-related emails gets a persistent Scala tag), and then set a catch-all filter that automatically archives the email (remove the #inbox label) and to a #notification label, that is cleaned up after 31 days. – About 100(!) of them are LinkedIn spam. Easy, delete all and unsubscribe. Except, the notification settings of LinkedIn looks like this:

LinkedIn Notification settings

Yes, you have to go into each of them. Yes, you have to toggle each of them off. I hate LinkedIn with all my heart.

  • The rest are mostly weekly advertising spam. I simply use them as the query to delete further emails, and click unsubscribe. Big shout out to Lenovo Japan, whose unsubscribe link leads to a 403. What do you mean, I am not allowed to opt-out?
  • Some emails are proper notifications of real services. As I went through each of them, I also visited the website and moved by email address to the new one.

Finally, Inbox zero!

What's next?

I requested a full deletion of all my emails in Gmail. It took one evening to do so, at about 100 emails per few seconds. My Gmail is now Inbox Zero as well (and Zero in every other folder).

I will keep the Gmail account for a while, forwarding all emails to Fastmail and slowly update all the accounts' email address as needed. It's gonna take months, but I hope to fully disable Gmail some day. Looking forward to it!

 
Read more...

from Zumi's Blog

Welcome back to my travel log ;)

Day 3: Hamburg and Train to Copenhagen

Having stayed up until 3am writing the last part, I had a good late sleep, taking off at around 10. As it was not the best weather, we decided to just walk around until we got to a food place this time. During this walk, I did the usual: took some photos of the every day streets and places. As always, I don't try to find the best angles or scenery, just what a common pedestrian may see from a morning walk to work. That said, I must say it was pretty enjoyable.

We ended up getting some food at a native-looking Gasthaus. My friend and I was looking forward to trying Hamburg's traditional Labskaus, but we made a mistake reading the menu and thought they had a meat version of it (lol). In the end, I took it anyway, while my friend got something else. We were in the end pleasantly surprised that there was (a lot of) fish on the plate. Good dish, I must say. I liked the pickled beetroots too, despite the vegetable not being my favorite.

In the afternoon, the weather wasn't so great, so we decided to just stay in a café. With some drinks ordered, we passed the time, until our next trains onward to Copenhagen at around 5.40pm. I didn't pass on one last chance to get another Bratwurst, unfortunately I was too eager to munch the whole thing and didn't take any photos ;)

Train to Copenhagen

The “standard” way to go from Hamburg to Copenhagen is by an ICE directly connecting the two cities. But, being the broke student I am, we opted for a triplet of trains instead: a Deutsche Bahn train from Hamburg dropping us off at the border down of Flensburg; followed by two DSV (Denmark national train) trains towards Fredericia and Copenhagen respectively.

Nothing spectacular about this trip: the DB train was about 10 minutes late, but as all connections were ~25 minutes apart (a concsiderate decision from DSV, I assume!), there was no trouble. We arrived at Copenhagen at the exact expected arrival time, which was a bit over midnight.

My first impression of Denmark, which already started in Fredericia...

Everything, including toilets, are spotlessly clean! And...

7-Elevens?!

Copenhagen, first impressions

The main station gave me a Montreal station vibe, with a twist of red-and-white (and a lot of 7-Elevens)!

Our ho(s)tel is about three metro stations from Copenhagen main station, and so we found ourselves there in a few minutes. One note about the station: you can walk from the tracks to the underground metro passage, but the station signs insists on you getting out of the main station and head into the Metro tracks after crossing a road. Strange decision to be sure, but it could be a time-saving measure, as I expect it would take a bit less time than crossing the tunnels.

The metro was clean and spacious.

... and so are the streets. Look at that And there is a lot of 7-elevens.

We checked in quite late, but the reception was expecting us – and checking in can be done automatically, so there was no hassle. We got a small and cozy room, time to sleep!

Day 4: Copenhagen @ Christmas Eve

We woke up around the not-so-early 9am. It has crossed our minds that stores will close early on this day, so we were prepared. There is a Netto right close to our place, so we went there looking for some food and drinks.

Potatoes-in-a-jar

We walked down the main streets of Copenhagen, just acknowledging that museums are expected to be closed for both the 24th and 25th. I wanted a coffee (as a caffeine-holic), so we were walking towards some of the nearest cafés according to Google Maps. I was treated with a cozy atmosphere and lots of space on every street. This is a pedestrian haven, that's for sure.

Looking around, we found a lot of Japanese tourists coming to Copenhagen. We decided to also follow the Japanese tradition, and spent our first Danish lunch... in KFC ;)

Afterwards, more walking around. We visited the Kastellet and the streets surrounding, peeking at all 7-elevens we crossed along the route. Feeling slightly hungry, we ook an S-Bahn back to the main station, and turned our way back to the shopping street...

Extra photo dump

That Dinner Story

As you could've guessed from your own experience, most places close early on Christmas Eve. Including restaurants. This leaves a problem for tourists: what do we have for dinner? The answer is: restaurants run by foreigners themselves. Across the shopping street, we found the following options: – Mr. Pho, a Vietnamese-style restaurant serving Vietnamese popular dishes as the name suggests. Reviews do say they are run by Chinese residents though, and so our pride does not let us come in. – Burger King, I mean, yeah. – Wok Box, serving Chinese noodle soups and such. Was promising until we peeked inside and saw people slurping disgusting looking noodle for ~$20. – The two Shawarma places. – Instant Ramen from 7-Eleven.

I think we ended up pondering this question for hours, walking back and forth on the street (and between it and our hotel) about a dozen times before finally committing on one of the Shawarma places. And this is the result:

And it turned out, as always, ramen is better than anything.

And that's the end of the 4th day...

 
Read more...

from Zumi's Blog

Been a while since I was last here... ;D

It's also been a few months since I last took a vacation and went somewhere new for fun, so I decided to write some notes about it. Perhaps not the kind of thing you would post online, but eh, might as well share a bit of the fun (and the pain) with whoever that's following this blog (hehe)...

For context, I've been eyeing one of the more northern European country for a Christmas trip since I was here, but a plan has never materialized: I was in Korea, then in Japan, both times with the crew I had from Canada. This time around, the crew was not assembling, so I was back to eyeing the Scandanavians. Luckily, one of my other friends just started their Master's at the same place as mine, and they are also up for a trip. Let's go... to Copenhagen I thought. It's not too far, probably we can reach by train, I thought.

... and that's what we planned. Now, it was not initially this way: we thought of taking a cheap flight to Copenhagen before Christmas, then slowly taking our time through Germany with a few ICE trips afterwards. Looking at the air ticket prices though: – 97CHF for one way, no luggage (not even carry-on) tickets from Geneva to Copenhagen (~1.xx hours) – ~110CHF for the same trip but by trains and we stop at Hamburg as a middle point (~16 hours total)

At this point I think most people can see some reasons to take the trains, but would still think flight is the saner choice. Not me! I'm a bit of a train enthusiast, and I do enjoy traveling with my small luggage, so train it went. Our plan seems nice:

  1. train to Hamburg (~11 hours)
  2. train to Copenhagen (~6 hours)
  3. train to Berlin (~8 hours)
  4. train to home (~11 hours)

Fitting some dates in between every trip, and we got ourselves a multi-city vacation. Sweet! We booked all the train trips (~220CHF total, not bad!) as well as all the accomodations a week before. My friend was a bit of an Airbnb hater, so we went for some of the cheaper hotels, and to be frank, the price was about the same.

Eagerly waiting for the departure date...

Day 1: Depart for Hamburg!

We got on the first train at around 2pm, it was a Swiss (SBB) train. As expected, the train was on time to the minute. Despite not being able to book seats in advance on these regular cross-country trains (unless you are getting 1st class, which we students are never touching), there were enough vacancy for us to enjoy the ride all the way up until we arrived at Basel.

From then on, we changed to one of the German (DB) trains. Nothing too peculiar: I've been on DB ICE (high speed railway) before, and the trains they use are pretty similar. Our pre-booked seats seemed full, but that's alright, though it's a bit strange that it did not show the station we're getting off at (later on we found out from the announcements that they had to switch the actual train used on the route at the last minute and did not manage to move all the seat data over). Unlike the previous ride, this time we're sitting on the train for 4 hours straight, so we got comfortable and I dozed asleep for a little bit.

I prepared a lot of snacks for the trip: some grapes, a pack of Zweifel, a pack of chocolate waffles, apples, bananas, coffee and apfelschorle. It was a bit overkill, but who knows what your stomach will act during a 11 hour train trip? In the end, we managed to munch over all the fruits and drinks, leaving half of the waffles and never opened the Zweifel. During this first DB train, we were sitting with a German father and his kindergarten-aged daughter. Some thoughts of sharing snacks crossed my mind, but as soon as we saw them eating pre-cut fruits and vegetables together, we kept ourselves away from unhealthy snacks as well and just shared some grapes between us and the daughter. During the trip, she was also trying to talk to us, but her German is too much for my untrained ears, and so we could only smile awkwardly. Unfortunate!

Fulda

Now, no Deutsche Bahn trip would be complete without a bit of spätung. It was no simple crash either: the delay built up and up and up over time, adding a few minutes each station, before reaching a crescendo in Frankfurt. There, the delays seemed to have collapsed the train with another on the same track, causing it to be sitting behind for 15(!) minutes.

The train schedule we had

(this image was lying to you, the schedule itself was modified from Frankfurt on)

The DB app I had was very helpful to let us know that because of the delay we will be missing our connection, and offered a simple “choose an alternative” page. Well done DB app! I guess it happened often enough that having its own page is worth the hassle ;) With all that done, we were told to get off in Fulda, and spend about half an hour there. We will arrive around 40 minutes later than schedule, but that's alright: the hotel was already informed that we would be checking in quite late – another 40 minutes will make no difference.

Fulda is a small town, but has its own interesting Christmas market open. We didn't have time to actually buy something there, but the smell of glühwein was very inviting. Next time!

Bahnhofstrasse in Fulda Frog in Fulda Glühwein Place in Fulda

Arriving in Hamburg

The second train had some minor delays as well, but nothing painful: we arrived in Hamburg a bit past 0:40. Not too bad — the original schedule was 23:55~. Big city!

(I forgot to take a photo at the time, but it was dark and I was tired)

With little patience left for making HVV (the local transport system) accounts or dealing with ticket machines, we decided to just walk to the hotel — should take us around 12 minutes according to Google Maps. Of course, “misfortune never comes alone” (a Vietnamese idiom), we walked to the wrong branch of the hotel instead! And so another 20 minutes was spent...

We arrived at our correct hotel and got our room at around 1:30. At this time, all I could think of was to shower and sleep.

Day 2: Hamburg Day!

Did I tell you we booked a visit at freaking 9am in advance?

We passed some time on the train to Fulda looking at possible locations to visit. One of them was the Miniatur Wunderland, which (incorrectly, not sure if maliciously) informed us that all visits for Saturday from 10am to 5pm are fully booked. We had two choices: 9am, or night... And nights are for Christmas market, and so we took the 9am tickets, without knowing the incoming hit to our sleep budget...

... and so we were up 7:30am, totalling 5.5 hours in bed. The huge coffee bottle from home proved useful, as it kept me awake for the Miniatur Wunderland visit. We walked a few minutes from the hotel, getting a Day pass from the HVV app on the way. Interesting fact: HVV iOS app requires registration before getting a pass (yuck!) but works perfectly afterwards. The Android app, you can do it as a guest, but then it bought my friend the pass for the next day...

Miniatur Wunderland

Miniatur Wunderland is a permanent exhibition of miniature cities, built to scale with cars, buildings and people. And moving trains! They got various cities around the world: from Swiss cities, Italian places, Monaco race tracks, French farms, Hamburg itself... to the further Norwegian ports, the American landscape, Mexican desserts and even Antartica!

Did I tell you trains run through all of them?

I only took stills, so perhaps a YouTube clip would describe so much better...

Needless to say I was ecstatic about the models. They say it's a total of ~1.2 million man-hours to create the props, and I think it's so much worth it. For the price of 17EUR (student discount applied) I think you're going to have a great time. I highly recommend!

Also, not in the photos: you can see the operator room watching over the model simulation and the train network in real time! How cool is that!!

Another extra: one of the techniques that was used to “simulate” strong waves was to build a non-even surface, project a moving wave on top and synchronizing the ship's movement to the waves moving... Impressive!

Hamburg Kunsthalle

We went for the Kunsthalle (art museum) next, but since it's mostly artworks, I took no photos.

There were two buildings: one for the pre-modern art, and the other for modern exhibitions. Regardless, you get a ticket for both (8EUR on student discount) from the former building with the main entrance.

The first building organizes itself as a story moving from Realism art to Impressionistic, before touching a bit on Surrealism. I'm no art student, so definitely not here to comment. But I found the idea interesting: if Realism's motive was to capture the artist's rendering of nature in its full details, then an Impressionistic wants to do the same thing, but making the viewer filling in the details by drawing impressions instead of the details themselves. Of course, as viewers, we inject details drawing from our common experience, while keeping the “soul” of the image intact through the impressions. A bit of work makes you enjoy it more, I might say. Is Modernism the next step, where the “soul” is even more abstract, allowing us, the viewer, to inject our feelings and thoughts into our own personal rendering of the art? I pondered a little bit... and then moved to the next building.

The second building hosts a few unconnected exhibitions, each on its own floor. Confusing and abstract is the common theme, I would say, as the art ranges from gory pieces of flesh to... something resembling myself playing with MS Paint for the first time. Interestingly, each exhibition gives us a few paragraphs of context on the author and the collection: their upbringing, their interests and their message they hope to convey through the pieces. Still not so clear about whether I would “get” the exhibitions, but something dawned on me today: yes the pieces by themselves are confusing and abstract, but it seemed that none was meant to be viewed on its own. Taken together, one finds a bit of structure: a common through-line, reaching out from the first impressions of each piece, that hopefully one can take and feed back into our own rendering process. A bit like osu! mapping: one might find a common theme when you look at a piece both as individual patterns and as a whole, and use that as a recursive process...

Well, I felt a touch smarter leaving the museum than entering.

Food...

We stopped for food in-between. Highlight of the day was definitely the Currywurst I got in one of the Christmas markets:

Soft and tasteful bratwursts against the classic flavor of curry, just the perfect combination. Worth it every time.

Now, for the un-highlight. We decided to have a bit of Korean food for lunch, as our home(?)town lacks its presence. Settled on “Chingu”, we made our order... and found out immediately afterwards that it was not really authentic, and seems to be run by a group of Vietnamese cooks and staff.

I got myself a bowl of Kimchi Jjigae (one of my favorite Korean dishes), while my friend got themselves some Japchae with chicken, and we shared some fried chicken together.

The Jjigae had the ingredients, but lacked the complex taste of pork belly / kimchi rinse in its soup. The end product reminds you of its salt and MSG, which isn't bad by themselves, but they should be elevating the original flavors instead of replacing them... They just had some wacky sauce for the fried chicken though which turned us away from it as well. Not the best experience. But the yuzu tea was good.

There is another story involving ramen... but that's left for another time!

Internationales Maritimes Museum Hamburg

This one completely blew my mind, despite being a last-minute addition.

From the main station, we needed to take the S4 not-really-subway-but-is-much-underground line, and then walked a bit further. The sun already made most of its way by 4pm, so it was not the most positive walk we had. One interesting note: we walked from Tokiostrasse to Osakaallee, ending up with a bridge across a river (reminding myself a lot of Osaka) before getting to the museum itself.

We arrived just around 4.30pm, when the museum started its almost-closing-time ticket sale of 9EUR. Wonder why so expensive? You're about to find out. First thing we were instructed: please head to the 9th floor and make your way down as you visit the exhibitions. So we headed to the elevator. As the doors open up, we saw in front of our eyes...

Thousands of mini models of ships!!! What an opening. And it just keeps getting better:

And bigger...:

And even bigger...:

And more exotic:

Can you believe, even for captain hats, they manage to find hundreds???

At some point we jokingly said, “well, grab your Legos, you ain't gonna build any of these ships”. What do you see next?

(lego guy for scale in the 2nd photo, the ship is so big...)

But my most favorite thing is that this museum as well has a damn to-scale miniature model of the whole Hamburg port!?!?

(not sure if they have any connections to Miniatur Wunderland... also, damn, this is dream Cities Skylines material...)

My jaws were on the floor the whole time during this 1+ hour visit. I mean, this is no museum, this is just Hamburg flexing on all other harbor cities... To be frank, I think this will be the best maritime museum I will ever see in my life, and there is just zero competition.

Even the train station on the way home gets a model ship.

Home

... and that's all for the day. I'm very pleased! Hamburg's cultural side is perfect so far. We grabbed some Glühwein on our way home, and joined some remote friends for a gaming stream to spend the night. And then there's me spending hours writing this up. I'm happy to have had this experience, so it's probably worth it to jot it down while it's still fresh.

Until next time! Trains to Copenhagen tomorrow afternoon.

 
Read more...

from hung3a8

Peewee is a lightweight ORM for Python. Compared to SQLAlchemy, Peewee is much more simpler, and compared to Django ORM, it is much more explicit. Here are some Peewee tricks you can use to improve your code.

Ultilize DefferedForeignKeyField to avoid circular imports

Like any other ORM, Peewee has ForeignKeyField and ManyToManyField to work with relationships. Although these fields is really convenient, they are not very powerful in some cases.

The most common issue is circular imports. Take a look at the following code:

class User(Model):
    username = CharField()
    favorite_tweet = ForeignKeyField(Tweet, null=True)  # NameError!!

class Tweet(Model):
    message = TextField()
    user = ForeignKeyField(User, backref='tweets')

While it is possible to solve this issue by changing the order of the classes, it is not pratical and may cause further problems. To fix this, the simplest solution is to use a DeferredForeignKey field:

class User(Model):
    username = CharField()
    favorite_tweet = DeferredForeignKey('Tweet', null=True)  # OK

class Tweet(Model):
    message = TextField()
    user = ForeignKeyField(User, backref='tweets')

While DeferredForeignKey is powerful, it can't resolve itself if deferred field is defined after the target class, and need to be resolved manually, which is really annoying to do so:

class User(Model):
    username = CharField()
    favorite_tweet = DeferredForeignKey('Tweet', null=True)  # OK

class Tweet(Model):
    message = TextField()
    user = DeferredForeignKey(User, backref='tweets')

DeferredForeignKey.resolve(Tweet)  # Too annoying!

This can be solve easily by running this at the end of the file if every model in a single file, or after importing all the models into __init__.py :

from peewee import DeferredForeignKey, Model

from .model import User, Tweet

for model in Model.__subclasses__():
    DeferredForeignKey.resolve(model)

Model.__subclasses__() returns a list of all subclasses of Model, which is useful to get all the models that are imported before the call. By doing this, DeferredForeignKey will resolve all unresolved fields that are stored in DeferredForeignKey._unresolved. This is a bit hacky, but from now on, you can use DeferredForeignKey instead of ForeignKeyField to avoid circular imports.

Although ForeignKeyField circular import error can be fixed by using DeferredForeignKey, it is still challenging when it comes to ManyToManyField, as ManyToManyField can only pass the model class, and the only thing that can be declared deferred is the through model, not the target model. To fix this, I made a modification of ManyToManyField that works just like the DeferredForeignKey by storing unresolved fields in ManyToManyField._unresolved:

class ManyToManyField(peewee.MetaField):
    _unresolved = set()

    def __init__(self, rel_model_name, **kwargs):
        self.field_kwargs = kwargs
        self.rel_model_name = rel_model_name.lower()
        ManyToManyField._unresolved.add(self)

    __hash__ = object.__hash__

    def set_model(self, rel_model):
        field = peewee.ManyToManyField(rel_model, **self.field_kwargs)
        self.model._meta.add_field(self.name, field)

    @staticmethod
    def resolve(model_cls):
        unresolved = sorted(ManyToManyField._unresolved, key=operator.attrgetter('_order'))
        for dr in unresolved:
            if dr.rel_model_name == model_cls.__name__.lower():
                dr.set_model(model_cls)
                ManyToManyField._unresolved.discard(dr)

    def _create_through_model(self):
        """Use DeferredForeignKey to create the through model, as the rel_model is not resolved yet."""
        lhs = self.model.__name__.lower()
        rhs = self.rel_model_name.lower()
        tables = (lhs, rhs)

        class Meta:
            database = self.model._meta.database
            schema = self.model._meta.schema
            table_name = '%s_%s_through' % tables
            indexes = (
                ((lhs._meta.name, rhs._meta.name),
                 True),)

        params = {'on_delete': self._on_delete, 'on_update': self._on_update}
        attrs = {
            lhs._meta.name: DeferredForeignKey(lhs, **params),
            rhs._meta.name: DeferredForeignKey(rhs, **params),
            'Meta': Meta}

        klass_name = '%s%sThrough' % (lhs.__name__, rhs.__name__)
        return type(klass_name, (peewee.Model,), attrs)

The new ManyToManyField will store the relational model in _unresolved and resolve it when the target model is available by calling ManyToManyField.resolve(target_model). Furthermore, if the through model is not specified, it will be created automatically, but with DeferredForeignKey instead of ForeignKeyField like the original implementation from Peewee, as the relational model is not resolved yet.

ManyToManyField in autogenerated models from playhouse.reflection.generate_models

Peewee has a builtin extension called playhouse which is extremely handy with custom fields, model generator, etc. playhouse.reflection.generate_models is used to generate models from an existing database, and then return the models. For small projects and projects that have different codebases that using the same database but cannot use the same code for building database models (ie. my projects which has a bridge written in Python (using Peewee ORM) and the backend written in TypeScript (using TypeORM)). It also supports relationships, which is very useful as you now don't have to deal with handling the relationships by yourself.

But there are the way playhouse.reflection.generate_models handling Many-to-Many relationships. Currently, the only relationships that are supported in Peewee are OneToMany, ManyToOne, and OneToOne, which are only requires foreign keys to handle relations between objects. But when it comes to ManyToMany, the madness begins. As generate_models only generates ForeignKeyFIeld to handle relations between fields and tables, when it meets a Many-to-Many table, which, of course, only contains the two foreign keys that related to the two tables of the relationship. This can cause many problems, as you now have to work on Many-to-Many relationships in a much more complex way, perform manual querying, etc. Luckily, we can use this workarround to create ManyToManyField in those models, which can solve all of our problems above.

for name, model in models.items():
    attrs = model_attrs[name]
    if 'id' not in model._meta.fields:
        continue
    for fk in model._meta.backrefs.keys():
        if 'id' not in fk.model._meta.fields:
            right_model = [m for m in fk.model._meta.model_refs.keys() if m != model]
            if len(right_model) != 1:
                raise ValueError('the Many-to-many model %s contains more than 2 foreign keys' % fk.model)
            right_model = right_model[0]

            field = ManyToManyField(right_model, backref=pluralize(name), through_model=fk.model)

            attrs[pluralize(right_model._meta.name)] = field
        else:
            continue
    for attr, field in attrs.items():
        model._meta.add_field(attr, field)

This code is directly added to the end of playhouse.reflection.Introspector method generate_models, which is the one that we are actually using to generate models (playhouse.reflection.generate_models is a seperate function that is used to create a new Introspector object from the database, and then it generate the models for you). Let me explain the code a little bit:

  1. First, we exclude all the models without the primary key field in it, and we may imply its name to be id. After excluding those tables, we want to work on the rest of the models. We search for all back-references in the table, and then if a we encounter a table with no primary key field in it, we move to the next step. Do this all over again until we move through all models and all of its back-references.
  2. After you successfully found the table, it is now time to perform some magic. First, we have to search for the right-side model, which is the one related to the left-side table that we are working on. Most of auto-generated Many-to-Many tables from other ORMs contains only two foreign keys, one for the left-side and one for the right-side. We here imply that there should only exists only one model references that is different from our current left-side model.
  3. After having the right-side model, we create a new field with the same name as the right model, but in plural form (you can find and use a pluralize function on your choice, we will not discuss about it here). This new ManyToManyField should contains both backref and through_model options to ultilize its power, as we all have those data available. Once finish, go back to step 2 if there are more models available, or else move to step 4.
  4. After having all attributes stored, we add all the fields to the model Meta, using its method add_field(attribute_name, field). And there you go! A new model class with proper ManyToManyField for you to work with your precious projects!

The code above might not work with your codebases, and you might need to modify it a little bit. But still, the concept is pretty nice, and I hope that this can save you a bunch of time.

Ending

I hope these tricks will help you a lot with your code. If you have any questions, please feel free to contact me.

Happy coding!

 
Read more...

from dnx04

Typst is a brand-new typesetting system, the ultimate solution for document typesetting of the new age. A unified typesetting experience that combined the best of many worlds.

I believe that sooner or later, Typst will take over the reign of LaTeX in academia. Getting my hands dirty with Typst is a satisfying experience for me these days.

It works elegantly, and blazingly fast.

Everything works out of the box, you don't really have to learn much to start. Typst's document hierarchical system and scripting is so consistent and elegantly designed, being so easy to understand like Markdown.

95% of Typst is written using Rust. Typst's developers have done their job so well in performance optimization.

Incremental compilation system

While being powerful and feature-packed like LaTeX, Typst surprised me with a true WYSIWYG (what you see is what you get) experience. In the past, there are many LaTeX contenders tried to replicate Word's WYSIWYG experience such as LyX, BakomaTeX, but their implementations are so inferior.

In LaTeX, PDFs need to be recompiled after every changes, which is inefficient and resource intensive. With Typst, every change you make is placed on top of what exists before, thanks to comemo, Typst's solution for cached compilation. Each letter and object you type got reflected in your PDF instantly and fluently.

 
Read more...

from Zumi's Blog

#cooking #recipe #vietnamesefood

Just writing down what this video is doing for my own reference.

Preparing the meat

For 6 portions: ½ kg pork belly + ½ kg pork leg (for the extra softness and sweetness).

  • Cut them into bigger chunks, they'll be nice to bite anyway!
  • Marinate the meat:
    • Baby onions (4x): slice them thinly
    • Red chili pepper: cut two ways! Thinly slice one half, and slice the other half into big chunks.
    • Garlic: crushed.
    • Lime: squeeze half into the meat.
    • Take the chunked chili, garlic and salt and grind them. Put everything into the meat, mix them carefully.
  • Leave them in the fridge for half an hour.

For the eggs: boil for 12 minutes, then set aside.

Braising Juice

You need:

  • Coconut juice: 1 liter. Filter away the coconut: you only need the juice!
  • Water: add the same amount as the juice.

Wait for everything to boil!

Putting them together

  • Once the juice is boiled, add the meat in. Take out all the marinating ingredients from the boil!
  • Leave everything for two hours.
  • Add the eggs, a small amount (~1 teaspoon) of fish sauce and a bit of sugar for the eggs to golden up. Leave for another hour!

When braising, leave a big gap for the lid: it keeps the juice clear.

That's it, have fun :D

 
Read more...

from Zumi's Blog

... is the GTK's default gtk-font-name settings.

What is system-ui?

You can change them in .config/gtk-4.0/settings.ini (or .config/gtk-3.0/settings.ini) like this:

gtk-font-name=Font Name 10

Read more to see how I found it.

  1. Looking for "system-ui" firefox landed me on this Firefox issue tracking the feature.
  2. The issue has an implementation here that maps the system-ui family to the internal menu font-family.
  3. Looking at the gfx/thebes/gfxPlatformFontList.cpp file in the implementation I saw StyleSystemFont::Menu. How do we find it?
  4. ... Searchfox of course! That leads me to widget/gtk/nsLookAndFeel.cpp...
  5. And from there just look for mMenuFontName that got assigned...
  6. From here, we know it's something from GTK. A quick look at ArchWiki on GTK shows that you can change gtk-font-name... so we just try it and 🎉
 
Read more...