diff --git a/R/Analyse/Auswertung.R b/R/Analyse/Auswertung.R new file mode 100644 index 0000000000000000000000000000000000000000..a93516b051e22f816dbbaba4ea71e5fe9dc37057 --- /dev/null +++ b/R/Analyse/Auswertung.R @@ -0,0 +1,153 @@ +# Auswertung nach Stadtteilen vs. Kommunalwahlergebnis 2021 + + +# Parteienliste +parteien_df <- read.xlsx("index/obwahl_da_2023/parteien-kommunalwahl.xlsx") + +# Kommunaldaten: Stadtverordnetenwahl +kommunal_url <- "https://votemanager-da.ekom21cdn.de/2021-03-14/06411000/praesentation/Open-Data-06411000-Stadtverordnetenwahl-Wahlbezirk.csv?ts=1679256774922" +k_stimm_df <- lies_stimmbezirke(kommunal_url) %>% + # Die Spalten D1-D14 enthalten die Gesamtergebnisse der Parteien. + # Unzählige weitere Spalten enthalten die Ergebnisse für jeden Kandidaten + # auf den sehr, sehr langen Wahllisten. + select(zeitstempel, + nr, + name, + meldungen_anz, + meldungen_max, + wahlberechtigt, + waehler_regulaer, + waehler_wahlschein, + waehler_nv, + stimmen, + stimmen_wahlschein, + ungueltig, + gueltig, + matches("D[0-9]+$")) %>% + mutate(nr = as.integer(str_extract(nr,"[0-9]+"))) + + +k_stadtteile_df <- k_stimm_df %>% + left_join(stimmbezirke_df %>% select(nr,ortsteilnr,stadtteil), + by="nr") %>% + group_by(ortsteilnr) %>% + # Fasse alle Spalten von meldungen_anz bis Ende der Tabelle zusammen - + # mit der sum()-Funktion (NA wird wie null behandelt) + summarize(zeitstempel = last(zeitstempel), + nr = first(ortsteilnr), + meldungen_anz = sum(meldungen_anz,na.rm =T), + meldungen_max = sum(meldungen_max,na.rm = T), + wahlberechtigt = sum(wahlberechtigt, na.rm = T), + waehler_regulaer = sum(waehler_regulaer, na.rm = T), + waehler_wahlschein = sum(waehler_wahlschein, na.rm = T), + waehler_nv = sum(waehler_nv, na.rm = T), + stimmen = sum(stimmen, na.rm = T), + stimmen_wahlschein = sum(stimmen_wahlschein, na.rm = T), + ungueltig = sum(ungueltig, na.rm = T), + gueltig = sum(gueltig, na.rm = T), + across(starts_with("D"), ~ sum(.,na.rm = T))) %>% + mutate(across(where(is.numeric), ~ifelse(is.na(.), 0, .))) %>% + # Stadtteilnamen, Geokoordinaten dazuholen + left_join(stadtteile_df, by="nr") %>% + # Wichtige Daten für bessere Lesbarkeit nach vorn + relocate(zeitstempel,nr,name,lon,lat) + +# Sicherheitscheck: Warnen, wenn nicht alle Ortsteile zugeordnet +if (nrow(stadtteildaten_df) != nrow(stadtteile_df)) teams_warnung("Nicht alle Stadtteile zugeordnet") +if (nrow(stimmbezirke_df) != length(unique(stimmbezirke_df$nr))) teams_warnung("Nicht alle Stimmbezirke zugeordnet") + + tmp_long_df <- k_stadtteile_df %>% + pivot_longer(cols = starts_with("D"), names_to = "partei_nr", values_to = "partei_stimmen") %>% + mutate(partei_nr = as.integer(str_extract(partei_nr,"[0-9]+"))) %>% + # Ortsteil- bzw. Stimmbezirks-Gruppen, um dort nach Stimmen zu sortieren + group_by(nr,name) %>% + arrange(desc(partei_stimmen)) %>% + mutate(Platz = row_number()) %>% + left_join(parteien_df %>% select(partei_nr = Nummer, + partei = Parteikürzel, + farbe= Farbwert), by="partei_nr") %>% + mutate(prozent = if_else(gueltig != 0,partei_stimmen / gueltig * 100, 0)) + k_ergänzt_df <- tmp_long_df %>% + # Ist noch nach Stadtteil (name, nr) sortiert + arrange(partei_nr) %>% + # Alles weg, was verhindert, was individuell auf den Kand ist - außer + # kand und Prozentwert + select(-partei_stimmen, -partei_nr, -Platz, -farbe) %>% + # Kandidatennamen in die Spalten zurückverteilen + pivot_wider(names_from = partei, values_from = prozent) %>% + ungroup() %>% + # und die zweite Hälfte dazu: + left_join( + tst <- tmp_long_df %>% + # Brauchen nur die Kand-Ergebnisse - und den (Stadtteil-)name + select(name, Platz, partei,prozent,farbe) %>% + # Nur die ersten (top) Plätze + filter(Platz <= (3)) %>% + #The Big Pivot: Breite die ersten (3) aus. + pivot_wider(names_from = Platz, + values_from = c(partei,prozent,farbe), + names_glue = "{.value}{Platz}") %>% + ungroup() %>% + select(-nr), + by="name") %>% + # Jetzt auswählen und umbenennen + select(nr, # ortsteilnr + k_wahlberechtigt = wahlberechtigt, + k_stimmen = stimmen, + k_stimmen_wahlschein = stimmen_wahlschein, + k_gueltig = gueltig, + ungueltig:partei3) %>% + rename(k_ungueltig = ungueltig) + +ergänzt3_df <- berechne_ergänzt(stadtteildaten_df,3) + +vergleichstabelle_df <- ergänzt3_df %>% + left_join(k_ergänzt_df,by="nr") %>% + select(-zeitstempel, + -ortsteilnr, + -meldungen_anz, + -meldungen_max, + -waehler_regulaer, + -waehler_wahlschein, + -waehler_nv) + # Gesamt-Ergebnisse ergänzen + +write.xlsx(vergleichstabelle_df,"daten/obwahl_da_2023/vergleichstabelle2021.xlsx", overwrite=T) + + +# Briefwahlergebnis +urnenwahl_df <- stimmbezirksdaten_df %>% + # Achtung: Prüfen, ob die Benennung der Briefwahllokale hierzu passt. + filter(nr < 9999) %>% + summarize(gueltig = sum(gueltig), + across(starts_with("D"),~ sum(.))) %>% + pivot_longer(cols = starts_with("D"), names_to = "kand_nr", + values_to = "kand_stimmen") %>% + mutate(kand_nr = as.integer(str_extract(kand_nr,"[0-9]+"))) %>% + left_join(kandidaten_df %>% select(kand_nr=Nummer, + Parteikürzel, + kand_name=Name),by="kand_nr") %>% + mutate(`Kandidat/in` = paste0(kand_name," (",Parteikürzel,")")) %>% + mutate(Prozent = kand_stimmen / gueltig *100) %>% + select(`Kandidat/in`, Urnenwahl = Prozent) + +briefwahl_df <- stimmbezirksdaten_df %>% + filter(nr > 9999) %>% + summarize(gueltig = sum(gueltig), + across(starts_with("D"),~ sum(.))) %>% + pivot_longer(cols = starts_with("D"), names_to = "kand_nr", + values_to = "kand_stimmen") %>% + mutate(kand_nr = as.integer(str_extract(kand_nr,"[0-9]+"))) %>% + left_join(kandidaten_df %>% select(kand_nr=Nummer, + Parteikürzel, + kand_name=Name),by="kand_nr") %>% + mutate(`Kandidat/in` = paste0(kand_name," (",Parteikürzel,")")) %>% + mutate(Prozent = kand_stimmen / gueltig *100) %>% + select(`Kandidat/in`, Briefwahl = Prozent) %>% + left_join(urnenwahl_df,by = "Kandidat/in") + + + +write.xlsx(briefwahl_df,"daten/briefwahl_ergebnis.xlsx", overwrite = T) + + \ No newline at end of file diff --git a/R/aktualisiere_karten.R b/R/aktualisiere_karten.R index 9fa849149aebf0cc6df4c3d9445f381678a48ed7..c3e641f753ff9292348ec23ee08af70b5b6f6f5c 100644 --- a/R/aktualisiere_karten.R +++ b/R/aktualisiere_karten.R @@ -277,6 +277,11 @@ aktualisiere_top <- function(kand_tabelle_df,top=5) { head(top) # Daten pushen dw_data_to_chart(daten_df,chart_id = top_id) + # Daten aufs Google Bucket (für CORS-Aktualisierung) + if (SERVER) { + write.csv(daten_df,"daten/top.csv") + system('gsutil -h "Cache-Control:no-cache, max_age=0" cp daten/top.csv gs://d.data.gcp.cloud.hr.de/obwahl_top.csv') + } # Intro_Text nicht anpassen. # Balken reinrendern balken_text <- generiere_auszählungsbalken(gezaehlt,stimmbezirke_n,ts) @@ -297,6 +302,10 @@ aktualisiere_tabelle_alle <- function(kand_tabelle_df) { # Daten und Metadaten hochladen, für die Balkengrafik mit allen # Stimmen für alle Kandidaten dw_data_to_chart(kand_tabelle_df, chart_id = tabelle_alle_id) + if (SERVER) { + write.csv(kand_tabelle_df,"daten/kand_tabelle.csv") + system('gsutil -h "Cache-Control:no-cache, max_age=0" cp daten/kand_tabelle.csv gs://d.data.gcp.cloud.hr.de/obwahl_kand_tabelle.csv') + } balken_text <- generiere_auszählung_nurtext(gezaehlt,stimmbezirke_n,ts) # Metadaten anpassen: Farbcodes für Parteien metadata_chart <- dw_retrieve_chart_metadata(tabelle_alle_id) @@ -320,7 +329,12 @@ aktualisiere_karten <- function(ergänzt_df) { ergänzt_f_df <- ergänzt_df %>% filter(meldungen_anz > 0) balken_text = generiere_auszählungsbalken(gezaehlt,stimmbezirke_n,ts) dw_edit_chart(chart_id = karte_sieger_id, annotate = balken_text) + # Daten pushen dw_data_to_chart(ergänzt_f_df,chart_id = karte_sieger_id) + if (SERVER) { + write.csv(ergänzt_f_df,"daten/ergaenzt.csv") + system('gsutil -h "Cache-Control:no-cache, max_age=0" cp daten/ergaenzt.csv gs://d.data.gcp.cloud.hr.de/obwahl_ergaenzt.csv') + } dw <- dw_publish_chart(karte_sieger_id) # Jetzt die Choropleth-Karten für alle Kandidierenden for (i in 1:nrow(switcher_df)) { @@ -333,7 +347,12 @@ aktualisiere_karten <- function(ergänzt_df) { aktualisiere_hochburgen <- function(hochburgen_df) { # Das ist ziemlich geradeheraus. + # Pushe Daten. dw_data_to_chart(hochburgen_df, chart_id = hochburgen_id) + if (SERVER) { + write.csv(hochburgen_df,"daten/hochburgen.csv") + system('gsutil -h "Cache-Control:no-cache, max_age=0" cp daten/hochburgen.csv gs://d.data.gcp.cloud.hr.de/obwahl_hochburgen.csv') + } balken_text <- generiere_auszählung_nurtext(gezaehlt,stimmbezirke_n,ts) # Metadaten anpassen: Farbcodes für Parteien metadata_chart <- dw_retrieve_chart_metadata(hochburgen_id) @@ -430,7 +449,12 @@ aktualisiere_ergebnistabelle <- function(stadtteildaten_df) { ungroup() %>% arrange(sort) %>% select(-name,-sort) + # Daten pushen dw_data_to_chart(ergebnistabelle_df %>% select(-nr), chart_id = tabelle_stadtteile_id) + if (SERVER) { + write.csv(ergebnistabelle_df,"daten/stadtteile.csv") + system('gsutil -h "Cache-Control:no-cache, max_age=0" cp daten/stadtteile.csv gs://d.data.gcp.cloud.hr.de/obwahl_stadtteile.csv') + } # Trendergebnis? Schreibe "Trend" oder "Endergebnis" in den Titel gezählt <- e_tmp_df %>% pull(meldungen_anz) %>% sum(.) stimmbezirke_n <- e_tmp_df %>% pull(meldungen_max) %>% sum(.) diff --git a/R/lies_aktuellen_stand.R b/R/lies_aktuellen_stand.R index 2ffc5fcd4cadd952b90337bfabecd094bd80fb06..685c7c669ddc578d6ef72b689568a92cbd43bdd1 100644 --- a/R/lies_aktuellen_stand.R +++ b/R/lies_aktuellen_stand.R @@ -85,7 +85,7 @@ check_for_timestamp <- function(my_url) { # } else { # t <- tmp[stringr::str_detect(tmp,"last-modified")] %>% # stringr::str_replace("last-modified: ","") %>% - # parse_date_time("%a, %d %m %Y %H:%M:%S",tz = "CET") + hours(1) + # parse_date_time("%a, %d %m %Y %H:%M:%S",tz = "CET") # } } else { # lokale Datei t = file.info(my_url)$mtime %>% as_datetime @@ -102,14 +102,15 @@ lies_stimmbezirke <- function(stand_url = stimmbezirke_url) { #' Schreibt eine Meldung ins Logfile - zugleich ein Lesezeichen cat(as.character(now())," - Neue Daten lesen\n") # Touch logfile check = tryCatch( - { stand_df <- read_delim(stand_url, + { + stand_df <- read_delim(stand_url, delim = ";", escape_double = FALSE, locale = locale(date_names = "de", decimal_mark = ",", grouping_mark = "."), trim_ws = TRUE) %>% # Spalten umbenennen, Zeitstempel-Spalte einfügen - mutate(zeitstempel=ts) %>% + mutate(zeitstempel=ts) %>% # Sonderregel: wir haben einen Zeitstempel, die "datum"-Spalte macht # Probleme, weil: starts_with("D"). select(-datum) %>% @@ -129,7 +130,9 @@ lies_stimmbezirke <- function(stand_url = stimmbezirke_url) { ungueltig = C, gueltig = D, # neu: alle Zeilen mit Stimmen (D1..Dn) - starts_with("D")) + starts_with("D")) %>% + # Zusatz für Frankfurt, das die Stimmbezirksnummern als character überträgt + mutate(nr = as.integer(nr)) }, warning = function(w) {teams_warning(w,title="OB-Wahl: Datenakquise")}, @@ -166,8 +169,8 @@ aggregiere_stadtteildaten <- function(stimmbezirksdaten_df = stimmbezirksdaten_d relocate(zeitstempel,nr,name,lon,lat) # Sicherheitscheck: Warnen, wenn nicht alle Ortsteile zugeordnet - if (nrow(stadtteildaten_df) != nrow(stadtteile_df)) teams_warnung("Nicht alle Stadtteile zugeordnet") - if (nrow(stimmbezirke_df) != length(unique(stimmbezirke_df$nr))) teams_warnung("Nicht alle Stimmbezirke zugeordnet") + if (nrow(stadtteildaten_df) != nrow(stadtteile_df)) teams_warning("Nicht alle Stadtteile zugeordnet") + if (nrow(stimmbezirke_df) != length(unique(stimmbezirke_df$nr))) teams_warning("Nicht alle Stimmbezirke zugeordnet") cat("Stadtteildaten aggregiert.\n") return(stadtteildaten_df) } @@ -237,7 +240,9 @@ berechne_kand_tabelle <- function(stimmbezirksdaten_df = stimmbezirksdaten_df) { left_join(kandidaten_df %>% select(Nummer, Name, Parteikürzel, Farbwert), by="Nummer") %>% mutate(name = paste0(Name," (",Parteikürzel,")")) %>% - mutate(Prozent = Stimmen / gueltig * 100) %>% + mutate(Prozent = ifelse(gueltig > 0, + Stimmen / gueltig * 100, + 0)) %>% select(Nummer, `Kandidat/in` = name, Parteikürzel, Stimmen, Prozent) cat("Gesamttabelle alle Kandidaten berechnet.\n") return(kand_tabelle_df) @@ -355,8 +360,10 @@ hole_wahldaten <- function() { } # Stadtteil neu ausgezählt? } - meldung_s <- paste0(meldung_s,"<br><br>", + if (!exists("NO_SOCIAL")) { + meldung_s <- paste0(meldung_s,"<br><br>", generiere_socialmedia()) + } teams_meldung(meldung_s,title=wahl_name) check = tryCatch( diff --git a/R/lies_konfiguration.R b/R/lies_konfiguration.R index b36523afcf71e436a4663c4bce56ab973a57ac7a..4c4d629148e81b2f3cb2fc2bd64c447a48a5b779 100644 --- a/R/lies_konfiguration.R +++ b/R/lies_konfiguration.R @@ -25,10 +25,20 @@ # - wahlberechtigt - Zahl der Wahlberechtigen (kommt Sonntag) # - briefwahl - Zahl der Briefwahlstimmen (kommt Sonntag) +# Falls der Parameter wahl_name noch nicht definiert ist, +# setze ihn erst mal auf das derzeitige Verzeichnis. +if (exists("wahl_name")) { + index_pfad = paste0("index/",wahl_name,"/") +} else { + index_pfad = paste0("index/") +} + +# Lies die Indexdatei aus dem Verzeichnis wahl_name. +# Falls keines angegeben: aus dem aktuellen Verzeichnis if (TEST) { - config_df <- read_csv("index/config_test.csv") + config_df <- read_csv(paste0(index_pfad,"config_test.csv")) } else { - config_df <- read_csv("index/config.csv") + config_df <- read_csv(paste0(index_pfad,"config.csv")) } for (i in c(1:nrow(config_df))) { # Erzeuge neue Variablen mit den Namen und Werten aus der CSV diff --git a/R/main.R b/R/main.R index 695ed73c5ee9bbf9638d5de0d6cad4ec5701f021..a4cb93c50b14b68d58608fa24ea1ee0b6ec8b1f7 100644 --- a/R/main.R +++ b/R/main.R @@ -16,7 +16,7 @@ p_load(R.utils) rm(list=ls()) TEST = FALSE -DO_PREPARE_MAPS = TRUE +DO_PREPARE_MAPS = FALSE @@ -85,7 +85,7 @@ if (DO_PREPARE_MAPS) { while (gezaehlt < stimmbezirke_n) { check = tryCatch( { # Zeitstempel der Daten holen - ts_daten <- check_for_timestamp(stimmbezirke_url) + ts_daten <- check_for_timestamp(stimmbezirke_url) + hours(1) }, warning = function(w) {teams_warning(w,title=paste0(wahl_name,": CURL-Polling"))}, error = function(e) {teams_warning(e,title=paste0(wahl_name,": CURL-Polling"))} @@ -107,6 +107,7 @@ dw_publish_chart(top_id) # Logging beenden if (!TEST) { + cat("OK: FERTIG - alle Stimmbezirke ausgezählt: ",as.character(ts),"\n") sink() sink(type="message") file.rename("obwahl.log","obwahl_success.log") diff --git a/R/main_oneshot.R b/R/main_oneshot.R index d4914a895036cd0f446a0b0e3a48dfa802061d8a..b23dd48f9ce2fa37bb0acc0e0107d469fe5496e0 100644 --- a/R/main_oneshot.R +++ b/R/main_oneshot.R @@ -11,19 +11,41 @@ p_load(DatawRappr) p_load(curl) p_load(magick) p_load(openxlsx) +p_load(R.utils) rm(list=ls()) -TEST = TRUE -DO_PREPARE_MAPS = FALSE - - - # Aktuelles Verzeichnis als workdir setwd(this.path::this.dir()) # Aus dem R-Verzeichnis eine Ebene rauf setwd("..") +# Lies Kommandozeilen-Parameter: +# (Erweiterte Funktion aus dem R.utils-Paket) +args = R.utils::commandArgs(asValues = TRUE) +if (length(args)!=0) { + if (any(c("h","help","HELP") %in% names(args))) { + cat("Parameter: \n", + "--TEST schaltet Testbetrieb ein\n", + "--DO_PREPARE_MAPS schaltet Generierung der Switcher ein\n", + "wahl_name=<name> holt Index-Dateien aus dem Verzeichnis ./index/<name>\n\n") + } + TEST <- "TEST" %in% names(args) + DO_PREPARE_MAPS <- "DO_PREPARE_MAPS" %in% names(args) + if ("wahl_name" %in% names(args)) { + wahl_name <- args[["wahl_name"]] + if (!dir.exists(paste0("index/",wahl_name))) stop("Kein Index-Verzeichnis für ",wahl_name) + } +} + +# Defaults +if (!exists("wahl_name")) wahl_name = "obwahl_ffm_stichwahl_2023" +if (!exists("TEST")) TEST = FALSE +if (!exists("DO_PREPARE_MAPS")) DO_PREPARE_MAPS = FALSE +NO_SOCIAL = TRUE + + + # Logfile anlegen, wenn kein Test # if (!TEST) { # logfile = file("obwahl.log") @@ -92,4 +114,4 @@ ts <- ts_daten hole_wahldaten() -# EOF \ No newline at end of file +# EOF diff --git a/R/messaging.R b/R/messaging.R index 77807cabdd5cdf51c44314c1398d25a8c7c75a46..80d8784d33cc05369036d0306a07b4f1c2978094 100644 --- a/R/messaging.R +++ b/R/messaging.R @@ -22,6 +22,7 @@ if (Sys.getenv("WEBHOOK_OBWAHL") == "") { teams_meldung <- function(...,title="OB-Wahl-Update") { cc <- teamr::connector_card$new(hookurl = t_txt) + if (TEST) {title <- paste0("TEST: ",title) } cc$title(paste0(title," - ",lubridate::with_tz(lubridate::now(), "Europe/Berlin"))) alert_str <- paste0(...) @@ -32,13 +33,13 @@ teams_meldung <- function(...,title="OB-Wahl-Update") { teams_error <- function(...) { alert_str <- paste0(...) - teams_meldung(title="OB-Wahl: FEHLER: ", ...) + teams_meldung("***FEHLER: ",...) stop(alert_str) } teams_warning <- function(...) { alert_str <- paste0(...) - teams_meldung("OB-Wahl: WARNUNG: ",...) + teams_meldung("***WARNUNG: ",...) warning(alert_str) } diff --git a/README.md b/README.md index 32998ac136dcdba01a3fc7798046c8ff89016c03..aae3d03fe3f0b71f2236271c26d4454e8039cffd 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,18 @@ Grafiken: * Tabelle nach Kandidaten (3 beste, 3 schlechteste Stadtteile) * Tabelle alle Ergebnisse nach Stadtteil +### Aktualisierung via CORS/GBucket + +Der normale Weg, eine Datawrapper-Grafik anzuzeigen, ist: pushe die Daten auf den Datawrapper-Server - mit dw_data_to_chart() - und aktualisiere. + +Alternativ kann die Grafik aus live bereitgestellten Daten in einem Google Bucket bestückt werden. Die Adressen der Dateien, die an die Grafiken übergeben werden müssen, sind: + +- https://d.data.gcp.cloud.hr.de/obwahl_top.csv +- https://d.data.gcp.cloud.hr.de/obwahl_kand_tabelle.csv +- https://d.data.gcp.cloud.hr.de/obwahl_ergaenzt.csv +- https://d.data.gcp.cloud.hr.de/obwahl_hochburgen.csv +- https://d.data.gcp.cloud.hr.de/obwahl_stadtteile.csv + ### Konfiguration Das Programm holt sich seine Daten aus einer Konfigurationsdatei - entweder für den Live- oder den Testbetrieb, was über die Variable TEST im Progammcode umgestellt wird. Die Indizes für die jeweile Wahl - Kandidatinnen und Kandidaten, Stadtteile und Wahllokal-Zuordnungen - liegen in einem Unterordner mit dem Namen der Wahl, als CSV oder XLSX. @@ -86,7 +98,7 @@ Spalte | Wert ---- | ---- nr | ID des Stimmbezirks ortsteilnr | ID des Stadtteils -ortsteil Name des Stadtteils +ortsteil | Name des Stadtteils Nicht benötigte Spalten können in der Tabelle bleiben, sollten aber möglichst nicht "name" oder so heißen. @@ -107,11 +119,28 @@ Aggregation auf Stadtebene (siehe ["Sitemap"](./sitemap.md) für den Code) +## Vorbereitung einer Wahl + +- Shapefile für die Stadt besorgen; Stimmbezirksebene; Stadtteile +- Stadtteile aggregieren, GEOJSON generieren +- Ordner für die Wahl im Index-Ordner; Datei Kandidaten, Stadtteile (mit Geokoordinaten für die Zentrumspunkte), Stimmbezirke (mit Zuordnung Stadtteil) +- Kopien für die vier Grafiken anlegen: Top, alle Stimmen, Hochburgen, Stadtteile. Link zum Wahlamt nicht vergessen. +- Leerdatei Ergebnisse nach Stadtteil vorbereiten +- Kopie der Sieger-Karte mit GEOJSON anlegen; GEOJSON hochladen, Leerdatei hochladen. Link zum Wahlamt korrigieren. +- Eine erste Kopie der Choropleth-Karten nach Kandidat: Wahlamt-Link ändern und Karte und Leerdatei hochladen, dann kopieren. +- Kopien für alle Kandidierenden anlegen. Jeweils die Werte-Spalte des jeweiligen Kandidaten auswählen; benennen, um sie zuordnen zu können. (Farben und Namen werden automatisch nachgetragen.) +- Indexdatei vorbereiten: Wahlname, Anzahl TOP, Dateinahmen der Index-Dateien, Datawrapper-IDs für die Karten und Diagramme + +## TODO + +- Analyse: Weshalb hängt das Polling manchmal hinterher? +- Aufruf mit Parametern ermöglichen ("main.R obwahl_ffm_2023") +- Oneshot-Variante für Kassel + +- Auswertung Briefwahldaten ## Nice-To-Have -- Vergleich letzte Kommunalwahl - Zusatzfeature: Briefwahlprognostik - wieviele Stimmen fehlen vermutlich noch? - Shapefiles KS, DA verbessern -- Datensparsamere Alternativ-CURL-Poll-Datei (zB mit dem Gesamtergebnis) -- Mehr Licht in den Choropleth-Karten farbabhängig +- Vergleich letzte Kommunalwahl regulär \ No newline at end of file diff --git a/howto_shapefiles.md b/howto_shapefiles.md index c75fe0a9c11e08ba9a8f97e1262d823741b41b40..981477235c0847ce22de40831048abf1f51ea235 100644 --- a/howto_shapefiles.md +++ b/howto_shapefiles.md @@ -8,6 +8,8 @@ Dazu Rechtsklick auf den Layer; Koordinatensystem WGS84, exportieren 3. Stadtteile generieren Menü "Vektor", "Geometrieverarbeitungswerkzeuge", "Auflösen" - und dann in der Dialogbox auswählen "Felder auflösen [optional]", und dann die Attribute hinzufügen, nach denen zusammengeführt werden soll. +Nach den Attributen schauen - die Ortsteilnr. ist ein String, kein Integer! Rechtsklick auf den neuen Layer; Eigenschaften..., dann den "Felder..."-Editor, da oben auf das kleine Abakus-Symbol klicken, Namen für das neue Feld in Ausgabefeldname (z.B. "nr"), dann in Feld Ausdruck eintragen"to_int(Ortsbezirk)" - und OK klicken. Neues Feld wird angelegt. Dann das alte Feld löschen (auswählen, oben Klick auf Löschen-Feld). + - Rechtsklick auf den Layer; Exportieren als GEOJSON - nicht vergessen, das Bezugssystem auf WGS84 umzustellen! - Rechtsklick auf den Layer; Export als XLSX - ggf. Geo-Attribute abschalten @@ -22,4 +24,12 @@ Dann noch Geokoordinaten der Zentroidpunkte: Rechte Seite die Toolbox, dort "Vek 5. CSV-/XLSX-Dateien putzen - Brauchen eine Stadtteil-Datei mit nr,name,lon,lat (erzeugt aus den Zentroiden) -- Brauchen einen Wahlbezirks-Zuordnung \ No newline at end of file +- Brauchen einen Wahlbezirks-Zuordnung + + +6. Reparatur der Darmstadt-Karte + +- Laden (falsche Geometrie - das erst zum Schluss fixen!) +- Vereinfachen: Fläche +- Auflösen +- Löcher löschen \ No newline at end of file diff --git a/index/obwahl_ffm_2023/ffm_config.csv b/index/obwahl_ffm_2023/ffm_config.csv new file mode 100644 index 0000000000000000000000000000000000000000..2f97d66ece55a4ae2800db52ad5c9972853d3f90 --- /dev/null +++ b/index/obwahl_ffm_2023/ffm_config.csv @@ -0,0 +1,37 @@ +name,value,comment +wahl_name,obwahl_ffm_2023,Welche Wahl? +stimmbezirke_url,https://votemanager-ffm.ekom21cdn.de/2023-03-05/06412000/daten/opendata/Open-Data-06412000-OB-Wahl-Wahlbezirk.csv?ts=1677904123448,URL Daten-CSV Stimmbezirke +wahlberechtigt,508182,Anzahl Wahlberechtigte lt. Wahlamt (kommt Sonntag) +briefwahl,250000,Anzahl Briefwahlstimmen lt. Wahlamt (kommt Sonntag) +kandidaten_fname,kandidaten.xlsx,"XLSX oder CSV, wird im Ordner <wahl_name> erwartet" +datawrapper_fname,datawrapper.xlsx,"XLSX oder CSV, wird im Ordner <wahl_name> erwartet" +zuordnung_fname,zuordnung_wahllokale.csv,"XLSX oder CSV, wird im Ordner <wahl_name> erwartet" +stadtteile_fname,stadtteile.csv,"XLSX oder CSV, wird im Ordner <wahl_name> erwartet" +startdatum,2023-03-05 16:00:00,Beginn der Auszählung +top5_id,2DYBQ, +karte_sieger_id,ANKmx, +karte_kand1_id,RcvQp,Rottmann (Grüne) +karte_kand2_id,jrm2v,Becker (CDU) +karte_kand3_id,bKR8r,Josef (SPD) +karte_kand4_id,etN3J,Mehler-Würzbach (Linke) +karte_kand5_id,3mydT,Pürsün (FDP) +karte_kand6_id,K3aCw,Lobenstein (AfD) +karte_kand7_id,vtG4Y,Pfeiffer (BFF) +karte_kand8_id,tRHeI,Tanczos (PARTEI) +karte_kand9_id,v4Y5m,Schwichtenberg (Gartenpartei) +karte_kand10_id,g3iBN,Wirth (unabh.) +karte_kand11_id,4LxcN,Camara (FPF) +karte_kand12_id,RZDF7,Pauli (unabh.) +karte_kand13_id,F86gf,Junghans (unabh.) +karte_kand14_id,bLPXL,Xu (unabh.) +karte_kand15_id,Ktufa,Wolff (unabh.) +karte_kand16_id,MO41j,Akhtar (Todenhöfer) +karte_kand17_id,ccrfL,Großenbach (Basis) +karte_kand18_id,q2S6m,Pawelski (unabh.) +karte_kand19_id,697CL,Schulte (unabh.) +karte_kand20_id,3lMmu,Eulig (unabh.) +tabelle_alle_id,7kRPR, +hochburgen_id,oB3KH, +tabelle_stadtteile_id,LiXnz, +social1_id,2DYBQ,5 stärkste +social2_id,S9BbQ,Alle Stimmen angepasst diff --git a/index/obwahl_ffm_2023/ffm_config_test.csv b/index/obwahl_ffm_2023/ffm_config_test.csv new file mode 100644 index 0000000000000000000000000000000000000000..07d5d1ab78591ee0f2e7376fb6d28c043eb752fa --- /dev/null +++ b/index/obwahl_ffm_2023/ffm_config_test.csv @@ -0,0 +1,37 @@ +name,value,comment +wahl_name,obwahl_ffm_2023,Welche Wahl? +stimmbezirke_url,testdaten/dummy.csv,URL Daten-CSV Stimmbezirke +wahlberechtigt,508182,Anzahl Wahlberechtigte lt. Wahlamt (kommt Sonntag) +briefwahl,250000,Anzahl Briefwahlstimmen lt. Wahlamt (kommt Sonntag) +kandidaten_fname,kandidaten.xlsx,"XLSX oder CSV, wird im Ordner <wahl_name> erwartet" +datawrapper_fname,datawrapper.xlsx,"XLSX oder CSV, wird im Ordner <wahl_name> erwartet" +zuordnung_fname,zuordnung_wahllokale.csv,"XLSX oder CSV, wird im Ordner <wahl_name> erwartet" +stadtteile_fname,stadtteile.csv,"XLSX oder CSV, wird im Ordner <wahl_name> erwartet" +startdatum,2023-01-01 18:00:00 CET,Beginn der Auszählung +top5_id,028Fp, +karte_sieger_id,7gscI, +karte_kand1_id,hM9SE,Rottmann (Grüne) +karte_kand2_id,hM9SE,Becker (CDU) +karte_kand3_id,07CR4,Josef (SPD) +karte_kand4_id,07CR4,Mehler-Würzbach (Linke) +karte_kand5_id,07CR4,Pürsün (FDP) +karte_kand6_id,07CR4,Lobenstein (AfD) +karte_kand7_id,07CR4,Pfeiffer (BFF) +karte_kand8_id,07CR4,Tanczos (PARTEI) +karte_kand9_id,07CR4,Schwichtenberg (Gartenpartei) +karte_kand10_id,07CR4,Wirth (unabh.) +karte_kand11_id,07CR4,Camara (FPF) +karte_kand12_id,07CR4,Pauli (unabh.) +karte_kand13_id,07CR4,Junghans (unabh.) +karte_kand14_id,07CR4,Xu (unabh.) +karte_kand15_id,07CR4,Wolff (unabh.) +karte_kand16_id,07CR4,Akhtar (Todenhöfer) +karte_kand17_id,07CR4,Großenbach (Basis) +karte_kand18_id,07CR4,Pawelski (unabh.) +karte_kand19_id,07CR4,Schulte (unabh.) +karte_kand20_id,07CR4,Eulig (unabh.) +tabelle_alle_id,PLwHI, +hochburgen_id,Im2PX, +tabelle_stadtteile_id,BM8kD, +social1_id,028Fp,5 stärkste +social2_id,S9BbQ,Alle Stimmen angepasst diff --git a/index/obwahl_ks_2023/config.csv b/index/obwahl_ks_2023/config.csv new file mode 100644 index 0000000000000000000000000000000000000000..cf31ec139f7f2a771c626b571e5387861466f98e --- /dev/null +++ b/index/obwahl_ks_2023/config.csv @@ -0,0 +1,23 @@ +name,value,comment +wahl_name,obwahl_ks_2023,Welche Wahl? +stimmbezirke_url,https://votemanager-ks.ekom21cdn.de/2023-03-12/06611000/daten/opendata/Open-Data-06611000-Direktwahl-zur-Oberbuergermeisterin-zum-Oberbuergermeister-Wahlbezirk.csv?ts=1678486050153,URL Daten-CSV Stimmbezirke +wahlberechtigt,147463,Anzahl Wahlberechtigte lt. Wahlamt (kommt Sonntag) +briefwahl,39092,Anzahl Briefwahlstimmen lt. Wahlamt (kommt Sonntag) +top,6,Anzahl der Top-Kandidaten in den Darstellungen +kandidaten_fname,kandidaten.xlsx,"XLSX oder CSV, wird im Ordner <wahl_name> erwartet" +zuordnung_fname,wahlbezirke.xlsx,"XLSX oder CSV, wird im Ordner <wahl_name> erwartet" +stadtteile_fname,ks-stadtteile.csv,"XLSX oder CSV, wird im Ordner <wahl_name> erwartet" +startdatum,2023-03-12 16:00:00,Beginn der Auszählung +top_id,Ts1oS, +karte_sieger_id,O9wPT, +karte_kand1_id,hM9SE,Schöller +karte_kand2_id,07CR4,Carqueville +karte_kand3_id,whgzp,Kühne-Hörmann +karte_kand4_id,5CpYu,Bock +karte_kand5_id,pc6vH,Käufler +karte_kand6_id,sEJhl,Geselle +tabelle_alle_id,EQ4dd, +hochburgen_id,GMTSJ, +tabelle_stadtteile_id,gJNPD, +social1_id,Ts1oS,5 stärkste +social2_id,S9BbQ,Alle Stimmen angepasst diff --git a/index/obwahl_ks_2023/config_test.csv b/index/obwahl_ks_2023/config_test.csv new file mode 100644 index 0000000000000000000000000000000000000000..8c2e436731078e59a1d0619b113f72e81e26311d --- /dev/null +++ b/index/obwahl_ks_2023/config_test.csv @@ -0,0 +1,23 @@ +name,value,comment +wahl_name,obwahl_ks_2023,Welche Wahl? +stimmbezirke_url,https://www.eggers-elektronik.de/files/test.csv,URL Daten-CSV Stimmbezirke +wahlberechtigt,147463,Anzahl Wahlberechtigte lt. Wahlamt (kommt Sonntag) +briefwahl,39092,Anzahl Briefwahlstimmen lt. Wahlamt (kommt Sonntag) +kandidaten_fname,kandidaten.xlsx,"XLSX oder CSV, wird im Ordner <wahl_name> erwartet" +zuordnung_fname,wahlbezirke.xlsx,"XLSX oder CSV, wird im Ordner <wahl_name> erwartet" +stadtteile_fname,ks-stadtteile.csv,"XLSX oder CSV, wird im Ordner <wahl_name> erwartet" +startdatum,2023-01-01 18:00:00 CET,Beginn der Auszählung +top,6, +top_id,028Fp, +karte_sieger_id,7gscI, +karte_kand1_id,hM9SE,Schöller +karte_kand2_id,07CR4,Carqueville +karte_kand3_id,whgzp,Kühne-Hörmann +karte_kand4_id,5CpYu,Bock +karte_kand5_id,pc6vH,Käufler +karte_kand6_id,sEJhl,Geselle +tabelle_alle_id,PLwHI, +hochburgen_id,Im2PX, +tabelle_stadtteile_id,BM8kD, +social1_id,028Fp,5 stärkste +social2_id,S9BbQ,Alle Stimmen angepasst diff --git a/index/obwahl_ks_2023/kandidaten.xlsx b/index/obwahl_ks_2023/kandidaten.xlsx index e10b674797487ee43e2033972e604fe8f5e8c83b..f1c1eac6703306255f182e4ea891e49f72ddc201 100644 Binary files a/index/obwahl_ks_2023/kandidaten.xlsx and b/index/obwahl_ks_2023/kandidaten.xlsx differ diff --git a/index/obwahl_ks_2023/parteien-kommunalwahl.xlsx b/index/obwahl_ks_2023/parteien-kommunalwahl.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..5b382a6653d2514848c5bbbe8651c2b62bdb5d04 Binary files /dev/null and b/index/obwahl_ks_2023/parteien-kommunalwahl.xlsx differ