Files
beantownbabeandClaude Sonnet 5 e60eb034ce Audit 22/10 post
Likes A3/T1 (Katrin Schmidt, Namrata Sharma, Alice Autuori audience;
Mckinley Black team; TXproducts Page like excluded entirely),
comments 0/0 (only comment was the Page's own), reposts A0/T2 (Peter
Gaal, Mckinley Black, both internal). Real Eng % 8.1%. Confirmed the
20/10 audit against the fresh export that now includes it (Likes 8,
Comments 0, Reposts 3) - matches exactly what was recorded from
screenshots.

Alice Autuori's status conflict (flagged last commit) is resolved -
the Demographics session confirmed directly with Mckinley,
independent of this session, that she does follow the Page, verified
against her LinkedIn profile, and already restored her Directory
record and removed the outreach_contacts.py duplicate.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-26 09:26:22 +01:00

226 lines
16 KiB
Python

"""Audience-vs-Team engagement audit and Real Eng % calculation, shared by app.py
(the live Dash pages) and report.py (the PDF generator) — pulled out of app.py so
report.py can use it too without a circular import (report.py is imported BY app.py,
so it can't import back from it; see report.py's own module docstring).
LinkedIn's export has no field for who performed a reaction/comment/repost, so this
is hand-mined from each post's own Reactions/Comments/Reposts lists on LinkedIn (same
method used to build the Follower Directory). Team counts are kept for transparency
(the diagonal box's "T" half on All Posts) but never feed Real Eng % or any Read —
only Audience numbers are ever read as real engagement. Some counts here differ
slightly from the export (e.g. 28/07 and 30/07 Reposts) — LinkedIn's own UI and its
export don't always agree; the hand-audited UI counts are treated as truth.
Add a new "DD/MM" entry here as each post gets audited — every table/chart/KPI that
reads through real_eng_pct() picks it up automatically, no other code changes needed.
"""
AUDIENCE_TEAM_AUDIT = {
"02/07": {
"likes": {"audience": 15, "team": 2}, # Page's own self-reaction excluded entirely
"comments": {"audience": 1, "team": 4},
"reposts": {"audience": 2, "team": 1},
},
"09/07": {
"likes": {"audience": 6, "team": 4}, # Page's own self-reaction excluded entirely
"comments": {"audience": 1, "team": 3}, # Page's own link-drop comment excluded entirely
"reposts": {"audience": 1, "team": 4},
},
"14/07": {
"likes": {"audience": 8, "team": 2}, # Page's own self-reaction excluded entirely
"comments": {"audience": 1, "team": 2}, # Page's own link-drop comment excluded entirely
"reposts": {"audience": 0, "team": 0},
},
"16/07": {
"likes": {"audience": 7, "team": 3}, # Page's own self-reaction excluded entirely
"comments": {"audience": 0, "team": 0}, # only comment was the Page's own link-drop
"reposts": {"audience": 0, "team": 3},
},
"21/07": {
"likes": {"audience": 1, "team": 1}, # Page's own self-reaction excluded entirely
"comments": {"audience": 0, "team": 0}, # only comment was the Page's own link-drop
"reposts": {"audience": 0, "team": 1},
},
"23/07": {
"likes": {"audience": 4, "team": 3}, # Page's own self-reaction excluded entirely
"comments": {"audience": 0, "team": 0}, # only comment was the Page's own link-drop
"reposts": {"audience": 0, "team": 0}, # no reposts on this post
},
"28/07": {
"likes": {"audience": 5, "team": 3}, # Page's own self-reaction excluded entirely
"comments": {"audience": 0, "team": 0}, # only comment was the Page's own link-drop
"reposts": {"audience": 0, "team": 3},
},
"30/07": {
"likes": {"audience": 7, "team": 1}, # Page's own self-reaction excluded entirely
"comments": {"audience": 0, "team": 0}, # no comments at all on this post
"reposts": {"audience": 2, "team": 2},
},
"04/08": {
"likes": {"audience": 4, "team": 3}, # Daniel Schmidt, Aleksandra Kotenko, Mckinley Black are Team
"comments": {"audience": 1, "team": 0}, # Page's own link-drop comment excluded entirely
"reposts": {"audience": 0, "team": 2}, # Peter Gaal (CEO/co-founder) + Mckinley Black are Team;
# TXproducts Page's own admin-panel repost (by Daniel
# Schmidt) excluded entirely. Corrected 2026-08-27: Mihail
# Petev did NOT repost this post — he liked the separate
# company-page reshare surface (already excluded from the
# audit for a different reason, mismatched Impressions
# denominator), which was mistakenly also counted here as
# an audience repost of the original post.
},
"06/08": {
"likes": {"audience": 0, "team": 0}, # only like was the Page's own self-reaction, excluded entirely
"comments": {"audience": 0, "team": 0}, # only comment was the Page's own, excluded entirely
"reposts": {"audience": 0, "team": 0}, # no reposts
},
"11/08": {
"likes": {"audience": 3, "team": 2}, # Johannes Blum, Sabine Brock, Raphael Buck are Audience;
# Mckinley Black, Aleksandra Kotenko are Team; TXproducts and
# Hi-Acts Page likes excluded entirely
"comments": {"audience": 0, "team": 0}, # only comment was the Page's own link-drop, excluded entirely
"reposts": {"audience": 0, "team": 1}, # Peter Gaal (CEO/co-founder) is Team; TXproducts Page's own
# repost excluded entirely
},
"13/08": {
"likes": {"audience": 1, "team": 3}, # Maike Schroeder is Audience; Aleksandra Kotenko, Daniel
# Schmidt, Mckinley Black are Team; TXproducts Page like
# excluded entirely
"comments": {"audience": 0, "team": 0}, # only comment was the Page's own link-drop, excluded entirely
"reposts": {"audience": 0, "team": 4}, # Daniel Schmidt, Peter Gaal, Mckinley Black, Aleksandra
# Kotenko are Team
},
"18/08": {
"likes": {"audience": 1, "team": 2}, # Anastasiia Panchenko is Audience; Aleksandra Kotenko,
# Mckinley Black are Team; TXproducts Page like excluded
# entirely
"comments": {"audience": 0, "team": 0}, # only comment was the Page's own link-drop, excluded entirely
"reposts": {"audience": 0, "team": 3}, # Aleksandra Kotenko, Peter Gaal, Mckinley Black are Team
},
"20/08": {
"likes": {"audience": 4, "team": 2}, # Katrin Schmidt, Sabine Brock, Dr. Palvan Seyidov, Maike
# Schroeder are Audience; Aleksandra Kotenko, Mckinley Black
# are Team; TXproducts Page like excluded entirely
"comments": {"audience": 1, "team": 0}, # Sabine Brock's substantive comment is Audience; TXproducts's
# own link-drop comment excluded entirely
"reposts": {"audience": 0, "team": 3}, # Peter Gaal, Aleksandra Kotenko, Mckinley Black are Team
},
"25/08": {
"likes": {"audience": 2, "team": 3}, # Daniel Hernandez Duran, Maike Schroeder are Audience;
# Aleksandra Kotenko, Daniel Schmidt, Mckinley Black are
# Team; TXproducts Page like excluded entirely
"comments": {"audience": 0, "team": 0}, # only comment was the Page's own link-drop, excluded entirely
"reposts": {"audience": 0, "team": 3}, # Daniel Schmidt, Aleksandra Kotenko, Mckinley Black are Team
},
"27/08": {
"likes": {"audience": 1, "team": 3}, # Sabine Brock is Audience; Daniel Schmidt, Aleksandra
# Kotenko, Mckinley Black are Team; TXproducts Page like
# excluded entirely
"comments": {"audience": 0, "team": 0}, # only comment was the Page's own link-drop, excluded entirely
"reposts": {"audience": 0, "team": 3}, # Daniel Schmidt, Aleksandra Kotenko, Mckinley Black are
# Team (all internal)
},
"03/09": {
"likes": {"audience": 5, "team": 3}, # Johannes Blum, Marc Thiry, Katrin Schmidt, Dr. Palvan
# Seyidov, Denny Drossmann are Audience; Daniel Schmidt,
# Aleksandra Kotenko, Mckinley Black are Team; TXproducts
# Page like excluded entirely
"comments": {"audience": 0, "team": 0}, # only comment was the Page's own link-drop, excluded entirely
"reposts": {"audience": 0, "team": 4}, # Peter Gaal (CEO/co-founder), Daniel Schmidt, Aleksandra
# Kotenko, Mckinley Black are Team
},
"08/09": {
"likes": {"audience": 3, "team": 3}, # Johannes Tenten, Ali A., Sabine Brock are Audience;
# Daniel Schmidt, Aleksandra Kotenko, Mckinley Black are
# Team; TXproducts Page like excluded entirely
"comments": {"audience": 2, "team": 1}, # Arunangshu Debnath's two replies are Audience; Peter
# Gaal's reply is Team; TXproducts' own reply-in-thread
# and separate link-drop comment both excluded entirely
# (Page actions, not a person)
"reposts": {"audience": 0, "team": 3}, # Daniel Schmidt, Aleksandra Kotenko, Mckinley Black are Team
},
"15/09": {
"likes": {"audience": 9, "team": 3}, # Johannes Tenten, Benedikt Kubicki, Simon Sebold, Martin
# Uhlig, Chen Shen, Philipp Brandt, Marc Thiry, Dr. Palvan
# Seyidov, Katrin Schmidt are Audience; Mckinley Black,
# Aleksandra Kotenko, Daniel Schmidt are Team
"comments": {"audience": 0, "team": 0}, # no comments on this post
"reposts": {"audience": 0, "team": 4}, # Peter Gaal, Daniel Schmidt, Aleksandra Kotenko, Mckinley
# Black are Team (all internal)
},
"17/09": {
"likes": {"audience": 1, "team": 2}, # Johannes Blum (shown as "Johannes Tenten" on this post,
# same Hi-Acts/DESY Innovation Manager as every other post)
# is Audience; Aleksandra Kotenko, Mckinley Black are Team;
# TXproducts Page like excluded entirely
"comments": {"audience": 0, "team": 0}, # no comments on this post
"reposts": {"audience": 0, "team": 3}, # Peter Gaal, Aleksandra Kotenko, Mckinley Black are Team
# (all internal)
},
"22/09": {
"likes": {"audience": 20, "team": 3}, # vaishakh vasudevan, Dr. Sonia Utermann, Dr. Jefferson do
# Rosario Stelzer, Johannes Blum (shown as "Johannes Tenten"),
# Kai-Wei Lin, Sani Harouna-Mayer, Shrouk Ehab, Harish
# Purushothaman, Mallika K., Mihail Petev, Michael Peiniger,
# Sebastian Sell, Katrin Schmidt, Azat Khadiev, Ali A.,
# Maximilian Harr, Chirag Maniya, Antonina Berkivska, Cecilia
# Farias Marchant, Lars Klemeyer are Audience; Mckinley
# Black, Aleksandra Kotenko, Daniel Schmidt are Team;
# TXproducts Page like excluded entirely
"comments": {"audience": 0, "team": 0}, # no comments on this post
"reposts": {"audience": 0, "team": 3}, # Mckinley Black, Peter Gaal, Aleksandra Kotenko are Team
# (all internal)
},
"29/09": {
"likes": {"audience": 1, "team": 2}, # Sabine Brock is Audience; Aleksandra Kotenko, Mckinley
# Black are Team; TXproducts Page like excluded entirely
"comments": {"audience": 0, "team": 1}, # Mckinley Black's own comment is Team, excluded from audience
"reposts": {"audience": 0, "team": 3}, # Aleksandra Kotenko, Peter Gaal, Mckinley Black are Team
# (all internal, "the usual suspects")
},
"01/10": {
"likes": {"audience": 2, "team": 3}, # vaishakh vasudevan, Yosra Gmati are Audience; Mckinley
# Black, Daniel Schmidt, Aleksandra Kotenko are Team;
# TXproducts Page like excluded entirely
"comments": {"audience": 0, "team": 0}, # only comment was the Page's own, excluded entirely
"reposts": {"audience": 0, "team": 4}, # Peter Gaal, Daniel Schmidt, Aleksandra Kotenko, Mckinley
# Black are Team (all internal)
},
"20/10": {
"likes": {"audience": 5, "team": 2}, # Dr. Jefferson do Rosario Stelzer, Philipp Brandt, Alice
# Autuori, Sabine Brock, Katrin Schmidt are Audience;
# Mckinley Black, Aleksandra Kotenko are Team; 8th like
# assumed to be TXproducts Page (excluded entirely,
# consistent with every other audited post) - only 7
# names were visible in the screenshots
"comments": {"audience": 0, "team": 0}, # no comments on this post
"reposts": {"audience": 0, "team": 3}, # Aleksandra Kotenko, Peter Gaal, Mckinley Black are Team
# (all internal)
},
"22/10": {
"likes": {"audience": 3, "team": 1}, # Katrin Schmidt, Namrata Sharma, Alice Autuori are
# Audience; Mckinley Black is Team; TXproducts Page like
# excluded entirely
"comments": {"audience": 0, "team": 0}, # only comment was the Page's own, excluded entirely
"reposts": {"audience": 0, "team": 2}, # Peter Gaal, Mckinley Black are Team (all internal)
},
}
def is_audited(date_ddmm):
return date_ddmm in AUDIENCE_TEAM_AUDIT
def real_eng_pct(date_ddmm, impressions, clicks=0):
"""Real Engagement Rate: Audience-only Likes+Comments+Reposts, plus Clicks, over
Impressions. Clicks are included even though unaudited — LinkedIn never reveals
who clicked (no list exists, unlike Reactions/Comments/Reposts), but there's no
team practice of deliberately clicking through the way there is for Likes/
Comments/Reposts, so the team's share of Clicks is assumed small. Returns None
for a date with no audit entry."""
audit = AUDIENCE_TEAM_AUDIT.get(date_ddmm)
if not audit or not impressions:
return None
aud_total = (audit["likes"]["audience"] + audit["comments"]["audience"]
+ audit["reposts"]["audience"] + clicks)
return aud_total / impressions * 100