Compare commits

...

4 commits

5 changed files with 166 additions and 14 deletions

25
app.py
View file

@ -3,6 +3,12 @@ from typing import List
from shiny import App, ui, Inputs, Outputs, Session
from shiny.types import NavSetArg
from src import mod_welcome
from src.util import load_html_str_from_file
import os
footer_html: str = load_html_str_from_file(os.path.join("www", "footer.html"))
def nav_controls() -> List[NavSetArg]:
@ -21,6 +27,21 @@ def nav_controls() -> List[NavSetArg]:
app_ui = ui.page_navbar(
*nav_controls(),
# create gap ----
ui.nav_spacer(),
# right hand side ----
ui.nav_menu(
"Mehr Infos",
ui.nav_control(
ui.a(
"No G20 Studie",
href="https://g20.protestinstitut.eu/",
target="_blank",
),
),
align="right",
),
selected="intro",
fluid=False,
title=ui.div(ui.img(src="favicon.ico", width="75dpi", height="75dpi"),
@ -31,12 +52,12 @@ app_ui = ui.page_navbar(
bg="#ba289f",
inverse=True,
id="Intro",
footer=ui.div(ui.HTML(footer_html), inline=False),
)
def server(input: Inputs, output: Outputs, session: Session):
# mod_welcome.welcome_server()
pass
mod_welcome.welcome_server("Intro")
static_dir = Path(__file__).parent / "www"

View file

@ -0,0 +1 @@
{"hashtags": 267255, "mention": 71142, "url": 141594, "tweet_count": 151690, "num_police_accounts": 163, "date_first_tweet": "2020-10-27 09:29:13", "date_last_tweet": "2023-03-16 11:42:58", "day_diff": 870, "avg_post_hour": 11.156780275562001, "num_text_tokens": 3764759}

View file

@ -1,26 +1,83 @@
from shiny import module, ui
# UI ----
# Note that we made conter_ui a function, and decorated it
from shiny import module, ui, render
import json
from datetime import datetime
@module.ui
def welcome_ui():
return ui.div(
ui.h2("Projekt Copbird: Eine Zusammenfassung"),
ui.h3("Allgemeines"),
ui.markdown("""
Copbird ist ein Projekt der [AG-Link][0].
Im Rahmen dieses Projektes entstand ein Stück Software, dass es uns ermöglicht hat viele tausend Tweets von verschiedenen Titter Accounts der Polizei abzurufen und zu speichern.
Im Rahmen dieses Projektes entstand ein Stück Software, dass es uns ermöglicht hat viele tausend Tweets von fast allen Twitter Accounts der Polizei abzurufen und zu speichern.
Die Idee hinter diesem Projekt war es, der exekutiven Gewalt auch im digitalen Raum genauer auf die Finger zu schauen.
Denn Twitter und andere Social Media Kanäle sind mittlerweile weit mehr als einfache Informationskanäle.
Es wird Werbung betrieben, gezielt Lügen oder sogenannte Fake News verbreitet, politische Ideologien verfochten und meistens auch einfach nur Memes ausgetauscht.
Doch welche Rolle nimmt dabei die Polizei genau ein und was macht sie auf Twitter?
[0]: https://ag-link.xyz
""")
"""),
# ui.output_text("dataset_infos"),
ui.output_ui("dataset_infos"),
ui.h3("Ursprung der Idee"),
ui.markdown("""
Die Idee für dieses Projekt überkam uns bei einem Vortrag der KEW (Kritische Einführungswochen) an der Universität Leipzig im Herbst 2020. Im Zuge eines Vortrages von Copwatch Leipzig wurde dort die Rolle der Polizei auf Twitter zu den Ausschreitungen zum G20 2017 in Hamburg erläutert.
Dieser Vortrag stellte heraus, dass die Polizeikräfte im Vorfeld der Demonstration sich ordentlich mit ihrem Inventar & Fuhrpark gebrüstet haben.
Es war eine Demonstration der Macht und ein klares Signal an alle Demonstrierenden: Wir sind auf alles vorbereitet vor allem auf Eskalation.
Dieses zur Schau stellen von Macht, war eine deutliche Kampfansage an alle, die sich in Hamburg versammelt haben, um gegen den G20 Gipfel zu demonstrieren.
Und die Fronten waren von Anfang an klar.
So hat die Polizei in ihrer theoretisch neutralen Rolle die Bevölkerung zu informieren versagt und von Anfang an zur eskalierenden Stimmungsmache, die zur Welcome to Hell Demonstration einen Höhepunkt erreicht hat, beigetragen.
Doch nicht nur das, laut einer [Studie][0] wurden während des Geschehens von Medienakteuren, den schnell abgesetzten Tweets der Polizei zur aktuellen Lage von Blockaden und Ausschreitungen schnell vertraut, obwohl die Polizei ein Teil der Konfliktpartei.
Einige der abgesetzten Tweets zur Dynamik der Veranstaltung und den Ausschreitungen haben sich schlussendlich sogar als falsch herausgestellt, wodurch bewusst oder unbewusst Falschinformationen verbreitet wurden.
Der Vortrag von Copwatch Leipzig hat genau solche Tweets analysiert und Folgen davon vorgestellt. Dies geschah aber von Hand, also die Tweets wurden nicht maschinell herausgesucht, verarbeitet und analysiert, sondern in mühseliger Handarbeit selektiert und analysiert.
Als Gruppe von Technik-Interessierten Menschen dachten wir uns darauf hin, warum nicht einfach ALLE Tweets der Polizei sammeln und automatisiert analysieren, um herauszufinden ob wir solche Untersuchungen, wie von Copwatch Leipzig oder der G20 Studie, nicht auch automatisieren könnten.
[0]: https://g20.protestinstitut.eu/
"""),
ui.h3("Vortragswoche"),
ui.markdown("""Infos zur Vortragswoche"""),
ui.h3("Hackathon"),
ui.markdown(
"""Link zu netzpolitik Artikel: https://netzpolitik.org/2021/copbird-hackathon-auf-twitter-macht-jede-polizei-ihr-eigenes-ding/"""),
ui.h3("Das Ende der Twitter API"),
ui.markdown(""":(""")
)
# Server ----
# Note that we just added the @module.server decorator
@module.server
with open("data/general_analysis_results.json", "r") as f:
general_analysis_dict = json.load(f)
@ module.server
def welcome_server(input, output, session, starting_value=0):
pass
@output
@render.ui
def dataset_infos():
date_format_str = "%Y-%m-%d %H:%M:%S"
tweet_count = general_analysis_dict["tweet_count"]
hashtags = general_analysis_dict["hashtags"]
mentions = general_analysis_dict["mention"]
url = general_analysis_dict["url"]
num_police_accounts = general_analysis_dict["num_police_accounts"]
date_first_tweet = datetime.strptime(general_analysis_dict["date_first_tweet"], date_format_str).strftime("%d.%m.%Y")
date_last_tweet = datetime.strptime(general_analysis_dict["date_last_tweet"], date_format_str).strftime("%d.%m.%Y")
day_diff = general_analysis_dict["day_diff"]
num_text_tokens = general_analysis_dict["num_text_tokens"]
return ui.markdown(f"""
**Datensatz Übersicht:**
Anzahl Tweets: **{tweet_count}**<br>
Anzahl Wörter/ Satzzeichen/ Emojis: **{num_text_tokens}**<br>
Anzahl Hashtahs in Tweets: **{hashtags}**<br>
Anzahl Erwähnungen in Tweets: **{mentions}**<br>
Anzahl Links in Tweets: **{url}**<br>
Anzahl Polizei Accounts: **{num_police_accounts}** (incl. Zoll, Bundespolizei, BKA, etc.)<br>
Datum des ersten Tweets: **{date_first_tweet}**<br>
Datum des letzter Tweets: **{date_last_tweet}**<br>
Anzahl Tage mit gesammelten Tweets: **{day_diff}**
""")

11
src/util.py Normal file
View file

@ -0,0 +1,11 @@
import os
import sys
def load_html_str_from_file(file_name: str) -> str:
if not os.path.isfile(file_name):
print(f"HTML File {file_name} does not exist!")
sys.exit(1)
with open(file_name, "r") as f:
return str(f.read())

62
www/footer.html Normal file
View file

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html>
<head>
<style>
.tab-content {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.footer {
background-color: #ba289f;
color: #fff;
padding: 5px;
text-align: center;
font-size: 14px;
left: 0;
width: 100%;
clear: both;
position: absolute;
}
.footer p {
margin: 0;
}
.footer table {
margin: 0;
margin-left: auto;
margin-right: auto;
}
.footer th {
padding-right: 3em;
margin-left: auto;
margin-right: auto;
}
.footer a {
color: #fff;
}
</style>
</head>
<body>
<footer class="footer">
<table>
<tr>
<th><a href="https://ag-link.xyz/impressum">Impressum</a> </th>
<th><a href="https://ag-link.xyz/feed.xml">RSS</a> </th>
<th><a href="https://lediver.se/@link">Mastodon</a> </th>
<th><a href="https://git.ag-link.xyz/">Git</a> </th>
</tr>
</table>
<p>2023 AG Link. Die von uns verfassten Inhalte stehen, soweit nicht anders vermerkt, unter der Lizenz <a
href="https://creativecommons.org/licenses/by-nc-sa/4.0/"> Creative Commons BY-NC-SA 4.0</a>. </p>
</footer>
</body>
</html>