Repository navigation
Phase 4: the site the archive deserves - #220
Conversation
bin/new-meetup writes the page of one meetup from its Meetup URL. The public page of an event carries the whole event as JSON in its __NEXT_DATA__ script: the title, the day, the venue, the group, and the number of people who said yes. No API key, no login. --discover names the meetups of both groups that have no page yet and prints the command for each; --dry-run prints the page without writing it; --description prints the announcement, which is what a model reads to extract the talks. bin/import-meetups records where a page came from, so a page written by hand and a page written from the export say so in the same words. The output of the importer is unchanged: verified byte for byte on all 266 files it writes. The links job of a pull request checks the internal links only. The site holds 139 distinct external URLs outside the frozen snapshots, and checking them took 25 minutes on the pull request that added the person pages: every pull request asked every host we have ever linked to whether it is still there, and reported rot that is not ours to fix. An internal link that does not resolve is a typo, is ours, and is found in seconds. The external ones move to .github/workflows/links.yml, which runs on request with a cache of a week and does not fail the build. The data of the association: the glyph of each role, the emoji of each event type, and the assemblies with their minutes.
The site holds 607 talks, 371 people, and 281 events, and the only way to find one was to know which listing it was on. /search/ searches all of them. Pagefind reads the pages Hugo wrote, after the build, in CI and locally, and ships its index with them: no account, no third party, and no request that leaves this site. The binary is pinned the way Hugo is, with the version in .pagefind-version and the SHA-256 of the release checked by bin/install-pagefind. What is indexed is the record of each page and not the whole of it: the person pages repeat the title of every talk the person gave and the event pages repeat the programme, so a name or a technology would match three pages per talk. The h1 of an indexed page carries the kind, which is what a result is labelled by and what the kind filter passes. The box is in the bar of every page, and the page that runs the search is the only one that ships the script. A missing address searches the words of its own path, less the section names: an old link to /talks/2019/keep-your-repo-clean/ asks for "2019 keep your repo clean" and the first result is that talk on its new address. Measured on a production build: 1,258 pages indexed in about a second, 903 KB on disk under /pagefind/, and 209 KB downloaded by a first search, with the 30 fragments of the results drawn. The second query loads only the chunk of its word.
/sponsors/ carried its own list of 46 ids by hand, and it had drifted: 25 sponsors were on the page of their edition and nowhere else, among them 8 of the 12 of PyDay BCN 2025, 15 of 2024, and 2 of 2023. The page reads content/sponsors/ now, so a sponsor reaches it by existing. The count in the heading comes from the same walk the grid does, so the two cannot disagree. Two pages use that layout, and the second one is why the directory is read only when the page declares no levels of its own: /pyladies_bcn/sponsors/ lists the 18 of PyLadies BCN and was given all 71 for one build. The sponsors of PyDay BCN 2018 and 2019 come back. The two pages carried them as the HTML of the frozen site of that year pasted into their body: six img tags in 2018 and nineteen in 2019, each pointing at /archives/ and each with its own size in a style attribute, from 350px to 70px. They are levels now, with the levels the site of each year published, so they take the same card as every other edition. Ten of those sponsors had no record at all, and three of the ten have no web address because the company is gone: the record says so rather than guessing a URL. The logo of Fundación Caja de Ingenieros was labelled "Fundación Cibervoluntarios" in the alt text of that pasted HTML, which is a different organisation. The picture says Caja de Ingenieros. Every card is one size and every logo sits in the same box, because with 81 of them the eye reads a difference in size as a difference in what the sponsor gave. Six to a line at 1,200px and above, two on a phone; the card is 180x110 and the logo box 150x90. The hover is the one a person card has. Each logo is stored at the size the page serves at most: 28 of them were larger than anything the site draws, and go from 1,167 KiB to 249 KiB, with the worst palette conversion at 3.8 RMSE against the full-colour resize.
The name of the event and the name of the workshop were the same words twice, on 42 of the 72 PyLadies BCN days, because Meetup announced each day with the title of the one workshop it held: the page said "Selenium for WebScraping" in its heading and again in its programme, and the row of /events/ and the row of /talks/ said it twice as well. Those 42 are "PyLadies BCN Workshop, <month> <year>" now, and the workshop keeps its own title on its own page. The year is in the name because the month alone collides: six Januaries, six Mays, five Junes. canonical_photo is the one picture of a day, named in the front matter of its event. No rule can pick it: the gallery of PyDay BCN 2016 holds four pictures and three of them are a projector. heroBackground cannot stand in for it either, because thirteen events share seven of those images. Eleven events have a photo thread and all eleven now name one, nine of them the group photo of the day. The two oldest take a picture that lives loose in images/photos/, where the old site kept the few somebody chose by hand, and the one of 2016 is the full lecture hall picked out of a post of @PyLadiesBCN. An edition says how many people came when a source says it in words. PyDay BCN 2025 is the one page with a figure, 140, from the post that closes the thread of the day. It is not a registration: AGENTS.md keeps the two words apart, and an edition sells its tickets elsewhere. The minutes of the eight general assemblies have a section of their own on the page of the association, with the photograph of the day beside each. Five of the eight have one. The minutes are the record the law asks for; the photograph is the only thing that says what the day looked like, because an assembly has no event page and no gallery. Stored as WebP at 600px: 674 KiB of JPEG become 123 KiB. "Hands-on Django with PyLadiesBCN (2022)" carries its group, so it stops taking the "Other event" glyph on every listing.
/talks/ and /people/ hold 607 and 371 rows, and the only thing above them was a legend that counted what was there. The legend is a filter now, one sub-page per type and per role, with no JavaScript: the pages come from a content adapter, the rows from the one cached walk the listings already do, and the tab of the page you are on says so with aria-current. 398 talks, 172 workshops, 27 lightning talks, 7 keynotes, and 3 panels; the board, the committee, 23 organizers, 356 speakers, 10 mentors, 8 collaborators, and 1 volunteer. The role tabs read biggest first, because the bar is a way into the list and not a hierarchy of the association. /people/role/assistants/ answers the question nobody had asked out loud: the association never recorded who came to an event, and the page says so. The archive holds the people a programme, a board page, or a credit names, and an attendee is on no such list. /organizers/ keeps its grid of faces, with the photograph in a circle and in colour, and the modal behind each face is gone: it repeated the page of the person, which is the one place that holds the detail. That was an open defect since 2026-10-06. Every role carries its glyph on /people/, as it already did on the page of a person. The event cell of /talks/ carries the emoji of its event, the one /events/ draws. The trail of a listing names its parent inside the h1, as in "People:" and then the name of the person, with the colon and no underline until the pointer is on it. A filter page prints no trail, so "Talks: Talks" cannot happen, and a title that ends in a parenthesis takes no second one. The venue of a row has no hover: it carried the string of the source whenever it said more than the short name, and what it said was the noise the short name exists to remove. The caption under each listing is gone as well, on every one of them: it said the order and the grouping, which the first columns say by themselves. The schedule of an event reads on a phone, which it did not: the grid was written into the page body by the template and could not reflow. The gallery stays inside the screen between 992px and 1,200px. The reading column follows the window instead of stepping out at every Bootstrap breakpoint.
One of five proposals, with its header and its footer taken for the whole site: a menu that changes between the home page and the rest is worse than either of them. The home page says what this is in one screen and where the next event is: the name and the year it started, the four counts of the archive as the way in, the video and the paragraph that say what PyBCN is, the two Meetup groups, the ten most recent events, the photograph of each event, the faces of the board and the committee, the four ways to take part, and the communities next door. Every number is computed, and the photographs are the canonical_photo of each event. The bar is four groups in title case, The Association, PyLadies BCN, Meetups, and Archive, with the search box and without Contact, which is in the footer and says itself once. It replaces the menu of config.toml, which read in the order of the file and shouted every item in capitals. The footer is four columns, the pages that Take part also links nested under the page they belong to, the seven accounts on one line, and the licence of the code and of the content. The hero is gone. It was a band a third of the screen tall with a photograph behind it, and the photograph said nothing about the page it stood over: thirteen events shared seven of them, and the association pages borrowed one from a PyDay. What is left is the h1 itself, in the column, with everything it carried: the search attributes, the trail of its listing, the total of a listing, and the lang of a title that is not in English. The home page draws its own, and says so with no_page_heading. Verified on a build with drafts: no page carries the old hero, every kind of page has exactly one h1, the footer draws its seven accounts, bin/check-rendered passes, and bin/check-content is unchanged at 0 errors and 81 warnings. The five proposals that led here, /option-1/ to /option-5/, are not in this branch. They were the way to decide, not pages to publish, and the site of a non-profit does not ship the sketches: the five layouts, the five stylesheets, their bars and their footers, and the five entries each needed in the allowlist of bin/check-rendered all go. The one thing of theirs that stays is what the home page uses: the marks of the communities under Our friends, and partials/friends.html, which holds the five of them.
The title of an event is printed in the row of /events/, in the row of /talks/, in the heading of its own page, in the search result, and in the card a shared link draws. Two of them carried an emoji of their own: a globe and a rocket around the Global Day of Coderetreat, and a snake and a crab after the March meetup of 2024. The row of a listing already marks every event with the glyph of its type, so the title emoji is a second mark that says something else, and the heading of the page reads as a post rather than as the name of a day. Two titles carried one, the coderetreat and the code katas, and both go. The announcements on Meetup keep their emoji: this is the name the archive gives the day, as with the 42 PyLadies workshops.
The page of a meetup, and of an edition with no grid, printed the speakers of each talk as plain text. It was the one listing that credits a talk to a person and did not take a reader to them: /talks/ links every name, the page of a person links every talk, and the day itself was a dead end. Each name is now the link of partials/person_page_link.html, which is the one place that decides where a name goes, and the line opens with the glyph of the Speaker role, the one /people/ prints before the same word. A credit that is not a person, an id with no record behind it, stays text, as it does everywhere else. 161 pages of the build gain the link.
The band is gone, and with it the partial that cropped a photograph to its shape (partials/image_hero.html), the partial that drew it (partials/hero-image-setheight.html, which nothing called any more), and the stylesheet of the two (hero.scss, 54 lines) with its import. Verified on a build: no page carries hero-image. Four photographs go with them, 455 KiB: canodrom_header.jpg was the first slide of the old carousel, meetups.jpg and practice-sessions.jpg were the background of the meetup blocks of the old home page, and news_bkg.jpg was behind its news band. Nothing reads them now, in the content, the templates, the data, or the stylesheets. heroBackground stays in the front matter of 38 pages, and so do the photographs it names: partials/social_image.html reads it for the card a shared link draws when the event has no gallery photo of its own. The field no longer draws anything on the page.
The home page was rebuilt this week and became the best looking page of the site, which left every other page reading as the one before it. The pages of the archive now share its vocabulary, and the vocabulary lives in main.scss where any page can reach it. What is shared: the colours the home page introduced ($rule-grey, $text-grey, $ink, $paper, $hover-blue, $blue-deep), one card radius, one gap between blocks of 3rem, one gap under a title of 1.5rem; a section title, the h2 at 27px with its rule under it; a sub title, the h3 at 1.2rem; a card, with its border, its hover, and its focus; and a frame for the boxes that are not links, the facts box and the figures. What the pages gained and lost. The edition page loses six blue h2 bands, three runs of <br/>, the rule over the gallery, the grey band behind the sponsors, and a padding trick that scroll-margin does better; it gains the section titles and the rhythm. The sponsor card and the person card are the card of the home page. The letters of /people/ are section titles. /search/ takes the greys and the blocks. The headings a markdown body writes take the scale of the site, which is what the page of the association reads by. The listings keep their table and gain the shared pills and the muted voice. The muted grey is one value everywhere, #51616e, which is 6.39:1 on white: the intros of the four listings were body black and the search status was #555. A long person name no longer pushes /people/ past the edge of a 390px screen, which it did before this pass. Three things the pass had changed and this commit keeps as they were asked for: the organizers page is its grid of faces and not the horizontal card, a level says how many people sit on its line, and a sponsor card lifts under the pointer as well as taking the shared border. On a phone the organizer cards go two to a line, because one gave a 360px face and a board four screens long. bin/check-rendered passes, bin/check-content is unchanged at 0 errors and 81 warnings, the build holds its 1,377 pages, and none of the ten pages checked overflows at 390, 768, or 1440.
The archive held 235 photographs, all of them from the threads of the eleven large editions. The association has posted far more, and the rest were sitting in the X export with nobody reading them: the importer swept the day of the event, so a thread that ran past midnight or a post of the morning after was out. What places a photograph is the text of its thread first and the day second. Reading it that way took several passes, and each failure is worth writing down: The account replies to its own posts of years before, so following the chain of replies built threads that ran from 2014 to 2021. The chain is cut at four days from the photograph. A talk title of two or three generic words matches any post that uses them, so a title has to be at least twenty characters before it can attribute a photograph. Five days of 2014 and 2015 have programmes written in one word (Ansible, Fabric, OpenStack, Spark + ipython, Api By Example, Python for porn); those are safe when the day decides as well, which is eight photographs more and the DevOps meetup of 2014 had none at all. A title that repeats across years says what the event is and not which one, so the day decides between them. Asking the text to name a talk is right for a post that names one, and blind to the photograph of a full room whose post says "About to start today's meetup!". There are a lot of those, so the day carries them: a post of the association, with a photograph, published on the day of an event, is a photograph of that event. Two posts of 2019-07-22 name the two talks of the meetup of the 18th and the thread went out four days later, so a reading places what no day could. The last group the day got wrong needed a person: 21 posts of 2019-11-21 came out under the November meetup, and 20 of them are the thank-you thread of PyDay BCN 2019, held on the 16th, one logo card per sponsor. A logo card is not a photograph of a day, and the sponsors of that edition are already on its page, so none of the twenty is imported. The thread does confirm the sponsor list: the 19 accounts it thanks are the 19 sponsors the page carries, at the same five levels, and that list came off the frozen site, so the thread is a second source for it. What stays out: 30 posts that thank a sponsor one logo card at a time, 44 with no event to attach to (a call for proposals, a change of date, a milestone, a meme, the announcement of a new logo, the events of other associations), and 4 that fall on the day of an event and say nothing about it (a box of stickers, a screenshot of a chat, a call to a practice session of the week after, and the sign with the wifi credentials of PyDay BCN 2019). The galleries go from 235 photographs on 11 pages to 530 on 84, and 257 of them name the talk they show, because the text of the post is "Ansible, by Ignasi Fosch" and the page of that day holds a talk by that name. One photograph per post, which is the rule the galleries already followed. Two carry a description instead of a quotation, because their post had no text at all and a caption is the alt text of its image: they are the only two captions on the site that the association did not write. They are stored as WebP: 23.2 MiB of JPEG from the export become 13.6 MiB, 41.5 per cent less. bin/check-content gains one rule: an edition with a gallery and no canonical photo is still worth saying, because the carousel of the home page shows the editions; a monthly meetup with a gallery is not, and 64 of them have one now. bin/import-x-photos takes --ids, a file of post ids, and the day stops deciding when it is given. It reads one account, the one of the association: a member who posts a photograph of a meetup from their own account keeps it, so a personal archive is never opened, not even for the day of an event. A t.co link that the export expands to http://pybcn.org is written as https, because the site publishes no insecure link; that is the only thing the importer changes about a quotation. AGENTS.md records the rules, so the next import does not learn them again. What is left for a person to read: the 44 posts above, which name no event at all.
The repository carried 8,460,730 bytes of PNG. Most of it was not a choice: a photograph in a lossless container, and three files no page has ever asked for. Deleted, 207,500 bytes, because nothing names them: a template, a stylesheet, the front matter of a page, nor the built site. backgrounds/zen_of_python.png is what the hero band left behind. static/favicon.png is byte for byte the favicon of the theme, and head.html asks for favicon.ico by name and builds the 32 and the 180 pixel icons from the asset, so nothing links the copy at the root and the site has no web manifest that could. backgrounds/logo_mini_white.png has no reference anywhere. Two more go the same way, sponsors/apsl.png and sponsors/verse.png, whose records point at apsl.svg and verse.jpg. Sixteen person photographs were PNG, which is the wrong container for a photograph: 4,669,887 bytes for images mostly under 600 pixels, and 773,727 of them for one 800 by 800 portrait. At quality 90 the same sixteen are 695,224, which is 85.1 per cent less. Quality 90 and not the 82 the galleries use, because the browser is served a resize of this file and not the file, and a lower quality would stack two lossy passes on one image. Twelve of the sixteen are RGB with no alpha at all; of the three that carry one, two have real transparency, which WebP keeps. The 65 sponsor logos, the four marks of the friends of the association, the placeholder of a person with no photograph, and one photograph of PyDay BCN 2021 that was a PNG among 441 WebP: 1,201,435 bytes become 702,638. The logos go to lossless WebP and not to a lossy one, because a logo is flat colour with sharp edges, which is what lossy compression damages first, and the saving over lossless is four points. None of that changes a page. The site never served those files: the templates resize them and the pipeline already wrote WebP, so /images/people/ holds the same 507 WebP and 124 JPEG as before, and /images/sponsors/ the same 136 WebP and 5 SVG. One change does reach the browser. The marks of the two Meetup groups sit under static/, so the file goes out as it is on disk: 134,314 bytes of PNG for two marks the stylesheet draws 88 pixels wide. As WebP at quality 82 they are 10,348, and the downscale swallows the compression. Rendered side by side in Chromium at 1440 pixels, the mark of PyBCN differs from the PNG by 0.25 of 255 per channel on average and 3 at its worst pixel, and the mark of PyLadies BCN by 0.64 and 5. The PNG of the repository is 2,150,520 bytes now, and 1,855,481 of those are the frozen snapshots under static/archives/, which keep their files as they were served.
Comments across the templates, the data, and one prompt file named who asked for a decision and on which day. The reason is the half worth reading: a square box drawn around a circle is the worst of the two shapes, whoever said so first. At that density a name stops being attribution and starts reading as a warning not to touch, which is the opposite of what the repository of an association run by volunteers wants. The reasons stay, word for word; the names and the dates of who asked go. bin/prompts/extract-talks.md pointed at a note in a private notebook for the measurement behind the model it names. It points at tests/extraction/ instead, which is in this repository and is what bin/check-extraction --selftest reads. The names that stay are the ones that are data and not attribution: the speakers the archive credits, the person who sits in two groups of the organizers page, and the names the comments of the importer use as their examples of an accent, a hyphen, or a pair of initials.
| </div> | ||
| </div> | ||
| {{ else }} | ||
| {{- /* The search. On a page the site search indexes, the h1 is part of |
There was a problem hiding this comment.
Count hidden when title of the section ends in ) (For example Former Management Board (2018 - 2025))
There was a problem hiding this comment.
That one is on purpose. The count is dropped when the title already ends in a parenthesis, so that "Former Management Board (2018 - 2025)" does not render as "Former Management Board (2018 - 2025) (7)", and so a filter page cannot read "Talks: Talks (398)". The cost is the one you spotted: those two board pages are the only role pages with no count.
If you would rather have the number everywhere, the alternative is to take the year range out of the title and let the heading print it, which changes the name of the group on the page. Happy to do that instead if you prefer it.
| if not path.is_file(): | ||
| return None | ||
| names = set() | ||
| for quoted, bare in re.findall(r'^(?:"([^"]+)"|([^"\s#][^:]*?))\s*:', path.read_text(encoding="utf-8"), re.M): |
There was a problem hiding this comment.
perhaps here is better to use yaml.safe_load as in other places
There was a problem hiding this comment.
Fixed in 244f482. It gives the same 87 keys today, so nothing changes in the output, but you are right that it is the wrong tool: a key with a colon in it, or a quoted value over two lines, would defeat the expression silently, and this function only ever reports a venue it does not recognise.
| {{- $alt = $social.alt | default site.Title -}} | ||
| {{- else -}} | ||
| {{- with resources.Get "images/logo.png" -}} | ||
| {{- $image = (.Fill "1200x630 png Center").Permalink -}} |
There was a problem hiding this comment.
og:image crops logo. Fill "1200x630 Center" on 920x273 source cuts ~43% width. Fallback card reads "python barcelo". Hits every page without own picture (search, most person pages). Use Fit on canvas or prepared card asset.
There was a problem hiding this comment.
Fixed in accaa39, and you were right that it reaches almost everything: 715 of the 1,307 pages shared that card, which is /search/, every listing, the association pages, and every person with no photograph.
It was worse than the crop as well. The source is transparent, so what was left of the logo sat on whatever colour the client painted behind it, black in Chromium.
The fix is a file, images/social-card.png, and not a Fit on a canvas: no crop or fit of a 3.4:1 logo fills a 1.9:1 frame without either losing something or leaving the frame empty, and a card is artwork, so it is reviewed by looking at it. The logo is scaled to 960x285 and centred on white with a 120px margin. A page that names its own picture is untouched: 468 event pages still share a photograph of the day and 124 person pages the portrait.
known_venues() found the keys of a YAML file with a regular expression, which every other reader of that file does with yaml.safe_load. The expression gets the same 87 keys today, and it is the wrong tool: a key that carries a colon, a quoted value over two lines, or a nested map would each defeat it in a different way, and silently, because the function only reports a venue it does not know.
A page with no picture of its own shared its logo through .Fill "1200x630", and Fill crops to the frame: the logo is 920x273, which is 3.4 times wider than it is tall, and the frame is 1.9, so 43 per cent of the width went. The card read "python barcelo", cut at both ends, on the transparent background of the source, which each client painted its own colour, black in Chromium. 715 of the 1,307 pages of the site shared that: /search/, every listing, the association pages, and every person with no photograph. images/social-card.png is the whole logo, scaled to 960x285 and centred on white with a 120 pixel margin, at the 1200x630 the card wants. It is a file and not a crop because no crop of a 3.4:1 logo fits a 1.9:1 frame without losing something, and because a card is artwork: it is reviewed by looking at it, which a generated crop is not. A page that names its own picture is unchanged: 468 event pages share a photograph of the day, and 124 person pages share the portrait.
AUTHORS names the eighteen people who have written or reviewed here, in the order they arrived, with the years, the account they worked from, and their page on the site. Reviewing counts: two of the eighteen have done nothing else. bin/authors writes it from the commits, the pull request reviews, and the person records, with no email anywhere. bin/authors --check writes it again and compares, and runs in CI. Four people with a page on the site now carry the GitHub account they worked from. humans.txt counts from the archive and not from the Meetup group: 281 events, 607 talks and workshops, 370 people, 81 sponsors, 87 venues, and 530 photographs. llms.txt gains the talks and the search. The two headers that document the card of a shared link say what the code does again.
e4bcdd2 to
baa051c
Compare
Phase 4 of the site: the archive becomes findable, the home page is rebuilt around it, and what the archive showed about the rest of the site is fixed.
Thirteen commits on top of
edition, each one a theme with its reasons in its message. The tree is the one verified:bin/check-content0 errors, a clean build with drafts, andbin/check-renderedandbin/check-html-safetyboth green.The search.
/search/searches the whole archive with no account, no third party, and no request that leaves this site: Pagefind runs over the built pages in CI and the index ships with them. The box is in the header of every page, and a missing address searches the words of its own path, so an old link to a talk lands on that talk. 1,258 pages indexed; opening/search/costs 176 KB of engine and filters, and a first search 36 KB more.The home page, the bar, and the footer. The home page says what PyBCN is and where the next event is, in one screen: the year it started, the four counts of the archive, the video, the two Meetup groups, the recent events, the photographs, the board and the committee, the four ways to take part, and the communities next door. The bar and the footer of the whole site follow it. The hero band is gone from every page: a third of the screen with a photograph that said nothing about the page it stood over, and thirteen events shared seven of them.
Finding a person, a talk, a day.
/talks/filters by type (398 talks, 172 workshops, 27 lightning talks, 7 keynotes, 3 panels) and/people/by role (the board, the committee, 23 organizers, 356 speakers, 10 mentors, 8 collaborators, 1 volunteer), each on its own page, with no JavaScript./people/role/assistants/says that the association never recorded who came, which is the honest answer to the question that tab asks.The photographs. 530 of them on 84 event pages, where the archive had 235 on 11, and 257 name the talk they show. They were in the X export with nobody reading them. What places one is the text of its thread first and the day second; what thanks a sponsor, announces something, or says nothing about the day it was posted on stays out. They are stored as WebP: 23.2 MiB of JPEG become 13.6.
What the archive showed about the rest of the site.
/sponsors/is read from the records and not from a list by hand, 81 and not 46, with 25 that were on an edition page and nowhere else. The sponsors of PyDay BCN 2018 and 2019 come back from the frozen site. 42 PyLadies BCN workshops are named after their day and not after their one talk. The minutes of the eight general assemblies have a section of their own.Accessibility, privacy, and weight. The schedule of an event reads on a phone. A shared link gets a card with the right title and a picture. No page sends a request to another site to draw itself. The site declares itself light, so a phone does not darken it and leave 81 sponsor logos unreadable. The images of the repository are WebP where they were the wrong format: 8,460,730 bytes of PNG become 2,150,520, and 1,855,481 of those are the frozen snapshots, which keep their files as they were served.
What is worth a look: the new photographs, in case one of them should not be published; the page of each role of
/people/, in case a name does not belong there; and the minutes of the assemblies.Closes #219
Closes #212
Closes #172
Closes #171