Ich finde es Schwierig wenn Kostenlose (und Fanabenteuer) so bewertet werden. Wenn es von jemanden das Erstlingswerk ist, vielleicht auch noch wenig Erfahrung hat.Sind das nicht alles offizielle Uhrwerk-Produkte?
Ich finde es Schwierig wenn Kostenlose (und Fanabenteuer) so bewertet werden. Wenn es von jemanden das Erstlingswerk ist, vielleicht auch noch wenig Erfahrung hat.
Sollen die Abenteuer aus Kettenrasseln separat bewertet werden oder nur die Kampagne als Ganzes, oder beides?
Sollen Tickleisten-Set und Zustandskarten auch bewertet werden?
"Diener der Götter" fehlt wohl auch noch.Erledigt
Wo sollten denn eurer Meinung nach die Links zu dem "errechneten Durchschnitt der Einzelabenteuer eines Anthologie-Bandes" hin führen? Zum Wiki? Einen eigenen Bewertungsthread zu den Anthologien gibt es verständlicherweise nicht.
Jagdfieber fehlt in der Liste der AbenteuerDanke für den Hinweis. Hier ist der neue Thread. http://forum.splittermond.de/index.php?topic=5373.0
Nachträglich wird es wahrscheinlich nicht gehen, ohne Stimmen zu verlieren, aber könnte es vielleicht in Zukunft bei allen Produktbewertungsthreads auch die Abstimmungsoption "noch nicht gelesen" oder "Enthaltung" geben?Nein, das würde das Bewertungsskript durcheinander bringen.
Das wäre ja kein großer Aufwand und würde dafür sorgen, dass man nicht immer wieder separat auf "Ergebnisse anzeigen" klicken muss, wenn man ein Abenteuer aus irgendwelchen Gründen nicht selbst bewerten kann oder will.
Würde nur der Durchschnittswert der Bewertungen genannt und vielleicht auch die Platzierung, würde ich Jeong Jeong zustimmen. Da aber auch die Stimmenanzahl direkt dahinter steht, kann man selber das für dich doch auch einordnen und relativieren.
Die Frage ist, wie die Liste dann aussehen sollte. Bisher ist es ja nach der Platzierung sortiert, die sich auf Basis der normalen durchschnittlichen Bewertung ergibt.
Sollen die Listen dupliziert werden mit Platzierungsreihenfolge nach "geek rating"? Soll das einfach nur dazu gemacht werden (und eventuell auch der Platz nach "geek rating") aber mit der alten Reihenfolge?
Für Splittermondprodukte, bei denen ja auch nach einer ersten Welle nur noch relativ wenige Bewertungen nach und nach dazukommen und sich fast alle irgendwo im Bereich bis ungefähr 70, 80 Bewertungen einpendeln (Abenteuer meist noch deutlich weniger), halte ich a) den Aufwand (auch langfristig) für unnötig und b) wie Olibino die Diskrepanz für zu groß.
Die BGG-Bewertung verstehe ich zwar, aber hätte ich diese aktuelle Diskussion und die Erklärung im Wiki nicht mitverfolgt, d.h. wäre ich relativ spontan irgendwann mal in die Übersicht gestolpert, hätte ich mich wahrscheinlich über die deutlich unterschiedlichen Werte gewundert.
Aber so ist es doch gut gelöst: wir haben die Spezial Wertung in der Splitterwiki, wo sie sich jeder, der daran interessiert ist, anschauen kann. Schön wäre es noch, den Splitterwiki-Artikel hier im Eröffnungsbeitrag zu verlinken. Neben der Spezial Wertung kann dieser ja auch noch mit nach der Stimmenzahl sortierbaren Tabellen punkten. :)
Selbst bei amazon und anderen Plattformen, auf denen man Produkte bewerten kann und auf denen es zwischen Null und tausenden Bewertungen alles gibt, muss ich selbst die Note in Relation zu den abgegebenen Stimmen setzen.Lustigerweise wird gerade Amazon häufig als Negativ-Beispiel gebracht wenn es um Rating-Systeme gibt. Und wie immer gilt auch, dass "andere machen es auch nur so" kein guter Grund dafür ist, etwas zu tun bzw. etwas nicht zu tun.
Bei den Spielhilfen z.B. haben wir (abgerundet) durchschnittlich 66 Stimmen pro Produkt. Insofern sind wir mit den 50 schon recht gut da.Insofern werde ich da morgen vermutlich nochmal etwas Arbeit reinstecken und das Skript so umschreiben, dass ich pro Kategorie das berechne und ich am besten auch noch die zusätzlichen Stimmen für die Spezielle Bewertung dynamisch berechne.
Bei den Kaufabenteuern hingegen kommen wir auf (abgerundet) durchschnittlich 19 Stimmen pro Produkt. Da könnte es echt sein, dass die 50 zu hoch gesetzt ist. Wir haben hier vor allem ein starkes Gefälle. Es geht von 52 zu 44 zu 37 zu 28, wo wir dann einige 20er haben und dann wieder einen starken Fall von 20 auf 14 haben wo es dann quasi konstant runter geht mit ganz vielen im Bereich 2-5.
Insofern stimme ich zu, dass im Ergebnis, wenn man die Werte absolut betrachtet, die 50 Stimmen bei den Kaufabenteuern etwas zu viel ist. Hier würde ich eher runter auf 20 senken.
Das hat ja sehr drastische Auswirkungen.
Zum einen ist eine Bewertung von 3,5 zwar theoretisch Durchschnitt, in der Praxis ist der Durchschnitt der Produktbewertungen aber viel besser. Da werden durch die 3,5 alle nach unten gezogen. Wäre es nicht besser den Durchschnitt der normalen Bewertungen zu nehmen?
Da bei den meisten Produkten ohnehin nicht so viele Leute abstimmen, heißt 50x Durchschnittsnote dass die Bewertung dann bei den meisten Produkten sehr nah an der Durchschnittsnote liegt. Ich würde eher 10x Durchschnittsnote vorschlagen.
Übrigens ebenfalls bedenkenswert (wobei ich nicht weiß, wie das mit euren technischen Mitteln umsetzbar ist): Zusatzstimmen nur bis zu einer bestimmten Stimmenzahl aufschlagen. Also wenn ein Produkt nur 5 Stimmen hat, wird bis zur Mindeststimmenzahl die festgelegt wird (50 erscheint mir auch viel, aber rechnen wir einfach mal damit) 45 Durchschnittsstimmen aufgeschlagen. Bei 35 Bewertungen nur noch 15. Bei 60 Stimmen wird gar nichts aufgeschlagen. So ähnlich funktionieren manche anderen gemittelten Bewertungssysteme (Gunmarks bei World of Tanks z.B. ;) ).Gibt es dafür irgend eine mathematische Grundlage, die man sich dafür ansehen könnte?
AnthologienDamit wäre das dann halt wirklich nurnoch eine Realtion / Platzierungsangabe zwischen den Produkten und man hätte nicht mehr eine "Bewertung".
Platz Bewertung Stimmen Bayes Platzierung Produkt 1 1.8 5 1 Verwunschene Mauern (http://Verwunschene Mauern) 2 1.87 96 2 An den Küsten der Kristallsee (http://An den Küsten der Kristallsee) 3 1.98 57 3 Unter Wölfen (http://Unter Wölfen) 4 2.31 13 4 Zwischen den Welten (http://Zwischen den Welten) 5 2.94 15 5 Alter Friede, neuer Streit (http://Alter Friede, neuer Streit)
Gibt es dafür irgend eine mathematische Grundlage, die man sich dafür ansehen könnte?
Also ich kenne das gewichtete Bewertungssystem von Bayes auch anders - ohne das Auffüllen mit Phantom-Stimmen. Ich meine, wir hätten damals einfach (durchschnittliche Stimmen * durchschnittliche Bewertung) + (Artikelstimmen * Artikelbewertung) / (durchschnittliche Stimmen + Artikelstimmen) gerechnet.Das ist das Gleiche.
Einen Fall von Platz 1 auf Platz 10 finde ich schon signifikant, vor allem wenn es nur 40 betrachtete Abenteuer gibt. Das ist schon ein harter Sturz.mMn aber ein berechtigter Sturz. Schließlich hat der 1. Platz nur 3 Stimmen, was dann viele andere übertrifft, die 20+ Stimmen haben, also knapp 7 mal so viele Stimmen.
Also letztendlich ein zweites Skript, dass etwas entsprechendes für die Splitterwiki ausgibt.ich denke mit etwas Geduld kann man parser und formatter in zwei module unterteilen und dann einen wiki formatter einbinden. Gibts sicher schon einen.
Weil ich glaube nicht, dass die Splitterwiki bbcode hat wie hier im Forum...
Ja, ich kann bei Gelegenheit mal meine PY-Datei wieder nach GitHub hochladen.Ja, das wäre super. Offener Quellcode ist immer der beste Weg um Hobbyprojekte vor dem Einschlafen zu bewahren. :)
Das Skript anzupassen sollte nicht so schwierig sein. Habe mir gerade mal n Fork erstellt und es mir angesehen und den letzten Fall kann man da ziemlich einfach mit einbauen (wären einfach nur 2 weitere Attribute die berechnet werden und bei der Ausgabe für den BBCode muss dann nurnoch die beiden Felder mit angegeben werden). Dann kann ich n Pull-Request machen und wenn 4 Port USB Hub das dann annimmt, ist es auch im Master-Branch drin.gerne, deswegen steht es ja auf github. :)
#!/usr/bin/python
# coding=utf-8
import urllib.request
from statistics import StatisticsError, mean
from bs4 import BeautifulSoup
from collections import namedtuple, OrderedDict
from operator import attrgetter
from multiprocessing.dummy import Pool as ThreadPool
# Collection of Thread IDs in several categories
Produktthreads = OrderedDict([
('Spielhilfen', [
1676, 1418, 2653, 3340, 3341, 3510, 4023, 4241, 4389, 4682, 4681, 5170, 4868, 5089, 5414, 5668, 5785]),
('Zubehör', [
2361, 3345, 3158, 3344, 5115, 5550]),
('Kaufabenteuer', [
2003, 2097, 2360, 2752, 3006, 3343, 3342, 3523, 3524, 3525, 2652, 2651, 3817, 4098, 4244, 4245, 4252, 4302, 4690, 4744, 4745, 5171, 5175, 5174, 5172, 5173, 5373, 5538, 5549, 5537, 5536, 5535, 5696, 5787, 5786]),
('Kostenlos verfügbare Abenteuer',
[2097, 2098, 2099, 2100, 2101, 2652, 2651, 4253])
])
# maintain anthologies separately
Anthologien = OrderedDict([
('Unter Wölfen',[
3523, 3524, 3525]),
('Zwischen den Welten',[
5009, 5010, 5011]),
('An den Küsten der Kristallsee',[
3828, 3827, 3817, 3826]),
('Alter Friede, neuer Streit',[
5173, 5174, 5175]),
('Verwunschene Mauern',[
5537, 5536, 5535])
])
# Add anthologies to collection to avoid duplicates
for Anthologie in Anthologien:
for threadid in Anthologien[Anthologie]:
if threadid not in Produktthreads['Kaufabenteuer']:
Produktthreads['Kaufabenteuer'].append(threadid)
# URL of a thread (%d will be thread_id)
baseurl = "http://forum.splittermond.de/index.php?topic=%d.0"
# Number of parallel threads (should be equal to number of CPU cores)
concurrent_parses = 4
def bbcode(tag, string, value=None):
"""Return a text(string) enclosed by the bbcode tags"""
if value:
return'[' + tag + '=' + value + ']' + string + '[/' + tag + ']'
else:
return'[' + tag + ']' + string + '[/' + tag + ']'
def bbcodeurl(urlstring, urlname):
"""Return an bbcode url format for given url and description"""
return bbcode('url', urlname, urlstring)
def bbbold(text):
"""Return the text with a bbcode bold tag"""
return bbcode(tag='b', string=text)
def bbtt(text):
"""Return the text with a bbcode tt tag"""
return bbcode(tag='tt', string=text)
class bbtable():
"""creates the frame of a bbcode table"""
def __init__(self, rows):
"""needs the rows as input for this table"""
self.elements = rows
def tablify(self, rows):
"""adds start and end tags for tables"""
return str('[table]\r\n' + rows + '[/table]')
def __str__(self):
"""prints table in bbcode format"""
return(self.tablify(''.join(str(row) for row in self.elements)))
class tablerow(bbtable):
"""creates a bbcode table row with correct tags"""
def cellify(self, rowfield):
"""encloses cells with correct tags"""
return str('[td]' + str(rowfield) + '[/td]')
def rowify(self, cells):
"""encloses rows with the correct tags"""
return str('[tr]' + str(cells) + '[/tr]\r\n')
def __str__(self):
"""adds cell and row tags to elements"""
return(self.rowify(''.join(self.cellify(field) for field in self.elements)))
class tableheaderrow(tablerow):
"""adds a header row"""
def cellify(self, rowfield):
return str('[td]' + bbbold(rowfield) + bbtt(' ') + '[/td]')
class ProduktParser():
def __init__(self, Produktthreads, Produkt = namedtuple('Produkt', 'name id url Stimmen Durchschnitt'), Produkte = [], Anthologien = [], baseurl = baseurl):
"""set base properties: URLs, thread ids, format"""
self.Produkt = Produkt
self.Produkte = Produkte
self.baseurl = baseurl
self.Produktthreads = Produktthreads
self.Anthologien = Anthologien
self.bewertungen = set(
[item for sublist in self.Produktthreads.values() for item in sublist])
self.pool = ThreadPool(concurrent_parses)
self.pool.map(self.getProdukt, self.bewertungen)
self.getAnthologie()
def getProdukt(self, threadid):
"""collect information for selected thread id"""
url = self.baseurl % threadid
page = urllib.request.urlopen(url)
soup = BeautifulSoup(page.read(), "html.parser")
Produktname = soup.find('title').string.split('/')[0].strip()
polls = soup.find('dl', {'class': 'options'})
options = polls.findAll('dt', {'class': 'middletext'})
votes = polls.findAll('span', {'class': 'percentage'})
ergebnis = dict(zip([[int(s) for s in option.string.split() if s.isdigit()][
0] for option in options], [int(vote.string.split(' ')[0]) for vote in votes]))
einzelvotes = [
item for sublist in [[k] * v for k, v in ergebnis.items()] for item in sublist]
try:
durchschnitt = str(round(mean(einzelvotes), 2))
stimmen = len(einzelvotes)
except (ZeroDivisionError, StatisticsError) as e:
durchschnitt = 'No votes yet'
stimmen = 0
self.Produkte.append(
self.Produkt(Produktname, threadid, url, stimmen, durchschnitt))
def getAnthologie(self):
for Anthologie in self.Anthologien:
Anthologiedurchschnittagg = 0
Anthologiestimmen = 0
for Spielhilfe in self.Produkte:
if Spielhilfe.id in self.Anthologien[Anthologie]:
if Spielhilfe.Durchschnitt != 'No votes yet':
Anthologiestimmen += Spielhilfe.Stimmen
Anthologiedurchschnittagg += Spielhilfe.Stimmen * float(Spielhilfe.Durchschnitt)
if Anthologiestimmen == 0:
Anthologiedurchschnitt = 'No votes yet'
else:
Anthologiedurchschnitt = str(round(Anthologiedurchschnittagg/Anthologiestimmen, 2))
self.Produkte.append(
self.Produkt(Anthologie, 0, 0, Anthologiestimmen, Anthologiedurchschnitt))
def generateTable(self, bewertungsthreads):
""""generate a table for the threads"""
return bbtable([tableheaderrow(['Platz', 'Bewertung', 'Stimmen', 'Produkt'])]
+ [tablerow([index + 1, element.Durchschnitt, element.Stimmen, bbcodeurl(element.url, element.name)])
for index, element in enumerate(sorted(bewertungsthreads, key=attrgetter('Durchschnitt')))])
def printProdukte(self):
""""print the table"""
for key, value in self.Produktthreads.items():
print('\r\n' + bbbold(key))
print(self.generateTable(
[Spielhilfe for Spielhilfe in self.Produkte if Spielhilfe.id in value]))
print('\r\n' + bbbold("Anthologien"))
print(self.generateTable(
[Spielhilfe for Spielhilfe in self.Produkte if Spielhilfe.name in [Anthologie for Anthologie in Anthologien]]))
if __name__ == '__main__':
SplittermondParser = ProduktParser(Produktthreads=Produktthreads, Anthologien=Anthologien)
print(
'Hier die Sammlung aller Produktbewertungsthreads, inklusive Durchschnittsbewertung und Ranking.')
print(
'Das script ist verfügbar unter https://github.com/zaboron/Splittermond/blob/master/parsebewertungen.py')
SplittermondParser.printProdukte()
Hallo Community,
wolln ma das kleine Weihnachtsszenario "Die Kunst zu backen" aufnehmen?
Ich habs noch nicht gefunden (vielleicht bin ich auch blind :o )
wolln ma das kleine Weihnachtsszenario "Die Kunst zu backen" aufnehmen?
Es ist ein inoffizielle Mini-Szenario und damit kommt es nicht mit in den Pot soweit ich weiß, genausowenig wie die Fanabenteuer (obwohl sie als extra Kategorie vielleicht doch interessant sind, ich glaube das viele gar nicht Wissen das es diese gibt)
Diskussion um Fanabenteuer hatten wir hier in diesem Thread schonmal, auf Seite eins. :) Ich war urspruenglich auch dafuer, fand aber letztendlich folgendes Argument doch recht stichhaltig;
N+1 Stimme für die Aufnahme von Fan-Abenteuern! Es kann sie zum einen ein wenig mehr in den Fokus rücken und zum anderen fände ich persönliche Bewertungen bei solchen Abenteuern besonders spannend.
Huldvoll winkend
---Jan van Leyden
Ich finde auch das es nicht wirklich motivierend ist, Fanwerk zu machen, wenn es anschließend Schulnoten bekommt. Das schreck doch eher ab als zu motivieren. Schöner fände ich es wenn jedes Abenteuer ein eigenes Thema bekommt in dem man über das Abenteuer diskutieren kann und fertig.
(C*m + sum(B)) / (C + S)Hier das Ergebnis vom Parser nach o.a. methode:
Wie gesagt, ich werde es vermutlich umändern in "durchschnittliche Anzahl Stimmen * durchschnittliche mögliche Bewertung" und das jeweils für die Kategorie (also die Spielhelfen werden da eine andere Stimmzahl bekommen als die Abenteuer etc.).
Wobei ich auch da ein Maximum reinsetzen werde (bei den Abenteuern vermutlich so um die 30, bei den Spielhilfen 50 etc.).
Platz | Bewertung | Stimmen | Produkt |
1 | 1.53 | 70 | Jenseits der Grenzen (http://forum.splittermond.de/index.php?topic=4023.0) |
2 | 1.61 | 63 | Selenia - Kaiserreich unter den Monden (http://forum.splittermond.de/index.php?topic=4389.0) |
3 | 1.62 | 86 | Bestien & Ungeheuer (http://forum.splittermond.de/index.php?topic=3510.0) |
4 | 1.64 | 46 | Splittermond Einsteigerbox (http://forum.splittermond.de/index.php?topic=3341.0) |
5 | 1.65 | 31 | Esmoda: Die Zitadelle der Unsterblichkeit (http://forum.splittermond.de/index.php?topic=5668.0) |
6 | 1.66 | 172 | Splittermond: Die Regeln (http://forum.splittermond.de/index.php?topic=1676.0) |
7 | 1.71 | 28 | Zhoujiang: Der Phönix im Schatten des Drachen (http://forum.splittermond.de/index.php?topic=5414.0) |
8 | 1.72 | 169 | Splittermond: Die Welt (http://forum.splittermond.de/index.php?topic=1418.0) |
9 | 1.75 | 9 | Farukan: Unter dem Pfauenthron (http://forum.splittermond.de/index.php?topic=5667.0) |
10 | 1.79 | 36 | Diener der Götter (http://forum.splittermond.de/index.php?topic=5089.0) |
11 | 1.8 | 9 | Die Flammensenke: Land der tausend Gefahren (http://forum.splittermond.de/index.php?topic=5785.0) |
12 | 1.8 | 26 | Die Surmakar - Unter Gleißender Sonne (http://forum.splittermond.de/index.php?topic=4682.0) |
13 | 1.84 | 32 | Dakardsmyr (http://forum.splittermond.de/index.php?topic=4241.0) |
14 | 1.87 | 108 | Mondstahlklingen (http://forum.splittermond.de/index.php?topic=3340.0) |
15 | 1.89 | 25 | Das Unreich (http://forum.splittermond.de/index.php?topic=5170.0) |
16 | 1.91 | 79 | Die Arwinger Mark (http://forum.splittermond.de/index.php?topic=2653.0) |
17 | 1.99 | 46 | Bestienmeister (http://forum.splittermond.de/index.php?topic=4868.0) |
18 | 2.07 | 65 | Splittermond: Die Götter (http://forum.splittermond.de/index.php?topic=4681.0) |
Platz | Bewertung | Stimmen | Produkt |
1 | 1.83 | 66 | NSC-Heft mit SL-Schirm (http://forum.splittermond.de/index.php?topic=2361.0) |
2 | 2.19 | 15 | Splittermond Tickleisten-Set Deluxe (http://forum.splittermond.de/index.php?topic=5550.0) |
3 | 2.21 | 34 | Splittermond CD (http://forum.splittermond.de/index.php?topic=3158.0) |
4 | 2.26 | 43 | Splittermond Tickleisten-Set (http://forum.splittermond.de/index.php?topic=3344.0) |
5 | 2.27 | 21 | Splittermond Würfelset (http://forum.splittermond.de/index.php?topic=5115.0) |
6 | 2.33 | 41 | Splittermond Zustandskarten (http://forum.splittermond.de/index.php?topic=3345.0) |
Platz | Bewertung | Stimmen | Produkt |
1 | 1.95 | 27 | Die Seidene Stadt (http://forum.splittermond.de/index.php?topic=4098.0) |
2 | 1.97 | 80 | Der Fluch der Hexenkönigin (http://forum.splittermond.de/index.php?topic=2003.0) |
3 | 2.02 | 20 | Bis zum Hals (Dakardsmyr) (http://forum.splittermond.de/index.php?topic=4244.0) |
4 | 2.02 | 24 | Ein Funke Mut (Unter Wölfen) (http://forum.splittermond.de/index.php?topic=3524.0) |
5 | 2.02 | 26 | Das Geschenk der Seealben (Kristallsee-Anthologie) (http://forum.splittermond.de/index.php?topic=3817.0) |
6 | 2.04 | 28 | Mord im Schwimmende Zirkus (An den Küsten der Kristallsee) (http://forum.splittermond.de/index.php?topic=3827.0) |
7 | 2.06 | 26 | Pashtarische Päckchen (An den Küsten der Kristallsee) (http://forum.splittermond.de/index.php?topic=3828.0) |
8 | 2.07 | 59 | Das Geheimnis des Krähenwassers (http://forum.splittermond.de/index.php?topic=2360.0) |
9 | 2.09 | 37 | Kettenrasseln (Einsteigerbox) (http://forum.splittermond.de/index.php?topic=3343.0) |
10 | 2.1 | 13 | Das Heulen des Windes (Unter Wölfen) (http://forum.splittermond.de/index.php?topic=3523.0) |
11 | 2.12 | 4 | Im Auge des Sturms (Die Surmakar) (http://forum.splittermond.de/index.php?topic=4745.0) |
12 | 2.13 | 3 | Zügelloser Zorn (Die Surmakar) (http://forum.splittermond.de/index.php?topic=4744.0) |
13 | 2.14 | 13 | Feuer und Flamme (http://forum.splittermond.de/index.php?topic=3006.0) |
14 | 2.14 | 14 | Sommersonnenwende (http://forum.splittermond.de/index.php?topic=4690.0) |
15 | 2.16 | 76 | Zwist der Geschwister (Arwinger Mark) (http://forum.splittermond.de/index.php?topic=2652.0) |
16 | 2.18 | 11 | Die Totengräber von Dakardsmyr (Dakardsmyr) (http://forum.splittermond.de/index.php?topic=4245.0) |
17 | 2.18 | 4 | Tempel der tausend Tore (Verwunschene Mauern) (http://forum.splittermond.de/index.php?topic=5536.0) |
18 | 2.19 | 2 | Der Vulkan des Todes (Die Flammensenke) (http://forum.splittermond.de/index.php?topic=5786.0) |
19 | 2.19 | 2 | Das schwarze Labyrinth (Die Flammensenke) (http://forum.splittermond.de/index.php?topic=5787.0) |
20 | 2.19 | 2 | Wenn die Wasser verstummen (Verwunschene Mauern) (http://forum.splittermond.de/index.php?topic=5537.0) |
21 | 2.19 | 3 | Palast der tausend Zimmer (Verwunschene Mauern) (http://forum.splittermond.de/index.php?topic=5535.0) |
22 | 2.19 | 14 | Zorn der Natur (http://forum.splittermond.de/index.php?topic=2752.0) |
23 | 2.2 | 6 | Das Wandernde Warenhaus (Mephisto #66) (http://forum.splittermond.de/index.php?topic=5549.0) |
24 | 2.2 | 6 | Letzter Wille (Esmoda) (http://forum.splittermond.de/index.php?topic=5822.0) |
25 | 2.2 | 7 | Drachenpakt (http://forum.splittermond.de/index.php?topic=5696.0) |
26 | 2.21 | 24 | Des Seekönigs Zorn (An den Küsten der Kristallsee) (http://forum.splittermond.de/index.php?topic=3826.0) |
27 | 2.22 | 20 | Das Mädchen mit den grünen Augen (Das Unreich) (http://forum.splittermond.de/index.php?topic=5171.0) |
28 | 2.24 | 4 | Der Schatten von Palitan (Zwischen den Welten) (http://forum.splittermond.de/index.php?topic=5009.0) |
29 | 2.24 | 26 | Gejagt! (Unter Wölfen) (http://forum.splittermond.de/index.php?topic=3525.0) |
30 | 2.25 | 7 | Jagdfieber (http://forum.splittermond.de/index.php?topic=5373.0) |
31 | 2.26 | 9 | Wolfsjagd (Zwischen den Welten) (http://forum.splittermond.de/index.php?topic=5010.0) |
32 | 2.26 | 5 | Der Quell des Lebens (Alter Friede, neuer Streit) (http://forum.splittermond.de/index.php?topic=5175.0) |
33 | 2.27 | 4 | In Feindschaft verbunden (Mephisto Nr. 61) (http://forum.splittermond.de/index.php?topic=4252.0) |
34 | 2.28 | 6 | Der Abgrund (Alter Friede, neuer Streit) (http://forum.splittermond.de/index.php?topic=5174.0) |
35 | 2.29 | 15 | Der Schimmerturm (http://forum.splittermond.de/index.php?topic=5538.0) |
36 | 2.31 | 3 | In Luft aufgelöst (Zwischen den Welten) (http://forum.splittermond.de/index.php?topic=5011.0) |
37 | 2.31 | 23 | Der Pfad durchs Seelenmoor (Einsteigerbox) (http://forum.splittermond.de/index.php?topic=3342.0) |
38 | 2.31 | 45 | Türme im Eis (http://forum.splittermond.de/index.php?topic=2097.0) |
39 | 2.32 | 11 | Die lange Wacht (Das Unreich) (http://forum.splittermond.de/index.php?topic=5172.0) |
40 | 2.34 | 52 | Seelenqualen (Arwinger Mark) (http://forum.splittermond.de/index.php?topic=2651.0) |
41 | 2.37 | 22 | Im Zeichen der Schlange (http://forum.splittermond.de/index.php?topic=4302.0) |
42 | 2.43 | 8 | Auf Abwegen (Alter Friede, neuer Streit) (http://forum.splittermond.de/index.php?topic=5173.0) |
Platz | Bewertung | Stimmen | Produkt |
1 | 2.04 | 51 | Die Nacht der Toten (http://forum.splittermond.de/index.php?topic=2101.0) |
2 | 2.21 | 76 | Zwist der Geschwister (Arwinger Mark) (http://forum.splittermond.de/index.php?topic=2652.0) |
3 | 2.38 | 45 | Türme im Eis (http://forum.splittermond.de/index.php?topic=2097.0) |
4 | 2.38 | 16 | Ein Licht in dunkler Nacht (http://forum.splittermond.de/index.php?topic=2099.0) |
5 | 2.41 | 52 | Seelenqualen (Arwinger Mark) (http://forum.splittermond.de/index.php?topic=2651.0) |
6 | 2.42 | 47 | Die Bestie von Krahorst (http://forum.splittermond.de/index.php?topic=2100.0) |
7 | 2.52 | 10 | Die Federn des Feiglings (Schnellstarter GRT 2016) (http://forum.splittermond.de/index.php?topic=4253.0) |
8 | 2.69 | 30 | Nacht über Tannhag (http://forum.splittermond.de/index.php?topic=2098.0) |
Platz | Bewertung | Stimmen | Produkt |
1 | 2.04 | 104 | An den Küsten der Kristallsee (http://0) |
2 | 2.1 | 63 | Unter Wölfen (http://0) |
3 | 2.2 | 9 | Verwunschene Mauern (http://0) |
4 | 2.33 | 16 | Zwischen den Welten (http://0) |
5 | 2.41 | 19 | Alter Friede, neuer Streit (http://0) |
@4-USB-HUB: Meinst du du kannst das Script so ändern, dass eine Ausgabe nach Excel möglich ist?hab das projekt mal aktualisiert.
Dann könnte ich nämlich noch ganz andere Auswertungen, zB nach Heldengrad, nach Ort etc. machen.
So, wie ich das auch für's DSA-Forum mache:
Beispiel Auswertung nach Region (http://www.dsaforum.de/viewtopic.php?f=81&t=45052&start=30#p1744200)
Vielen Dank an 4 Port USB Hub für die Möglichkeit nun die Auswertung in Excel zu machen.
Ich die Auswertung mal nach DSAnews hochgeladen, inklusive Zusatzinfos wie Genre, Heldengrad, Ort, Zeit etc.
Hoffe es gefällt euch:
https://dsanews.de/abenteuerbewertungen-splittermond/
#!/usr/bin/python
# coding=utf-8
import urllib.request
import xlsxwriter
from statistics import StatisticsError, mean
from bs4 import BeautifulSoup
from collections import namedtuple, OrderedDict
from operator import attrgetter
from multiprocessing.dummy import Pool as ThreadPool
# Collection of Thread IDs in several categories
Produktthreads = OrderedDict([
('Spielhilfen', [
100244 ,
99039 ,
98315 ,
103886
]),
('Abenteuer', [
97188 ,
98757 ,
100657 ,
103971
])
])
# maintain anthologies separately
Anthologien = OrderedDict([
('Cthulhu - Ars Mathematica',[
102159, 102160, 102158]),
('Cthulhu - Dreissig',[
101501, 101503, 101504, 101502]),
('Cthulhu - The Final Revelation',[
97284, 97285, 97286, 97287]),
('Cthulhu - Die Goldenen Hände Suc´naaths',[
98758, 98757, 98759]),
('Shadowrun - Licht aus der Asche',[
96028, 96027, 96026])
])
# Add anthologies to collection to avoid duplicates
for Anthologie in Anthologien:
for threadid in Anthologien[Anthologie]:
if threadid not in Produktthreads['Abenteuer']:
Produktthreads['Abenteuer'].append(threadid)
# URL of a thread (%d will be thread_id)
baseurl = "https://www.tanelorn.net/index.php?topic=%d.0"
# Number of parallel threads (should be equal to number of CPU cores)
concurrent_parses = 4
def bbcode(tag, string, value=None):
"""Return a text(string) enclosed by the bbcode tags"""
if value:
return'[' + tag + '=' + value + ']' + string + '[/' + tag + ']'
else:
return'[' + tag + ']' + string + '[/' + tag + ']'
def bbcodeurl(urlstring, urlname):
"""Return an bbcode url format for given url and description"""
return bbcode('url', urlname, urlstring)
def bbbold(text):
"""Return the text with a bbcode bold tag"""
return bbcode(tag='b', string=text)
def bbtt(text):
"""Return the text with a bbcode tt tag"""
return bbcode(tag='tt', string=text)
def generate_xlsx(bewertungs_threads):
# Create a workbook and add a worksheet.
workbook = xlsxwriter.Workbook('Bewertungen.xlsx')
worksheet = workbook.add_worksheet()
# Start from the first cell. Rows and columns are zero indexed.
row = 0
col = 0
# Iterate over the data and write it out row by row.
for index, element in bewertungsthreads:
worksheet.write(row, col, index + 1)
worksheet.write(row, col + 1, element.Durchschnitt)
worksheet.write(row, col + 2, element.Stimmen)
worksheet.write(row, col + 3, element.url)
worksheet.write(row, col + 4, element.name)
row += 1
workbook.close()
class bbtable():
"""creates the frame of a bbcode table"""
def __init__(self, rows):
"""needs the rows as input for this table"""
self.elements = rows
def tablify(self, rows):
"""adds start and end tags for tables"""
return str('[table]\r\n' + rows + '[/table]')
def __str__(self):
"""prints table in bbcode format"""
return(self.tablify(''.join(str(row) for row in self.elements)))
class tablerow(bbtable):
"""creates a bbcode table row with correct tags"""
def cellify(self, rowfield):
"""encloses cells with correct tags"""
return str('[td]' + str(rowfield) + '[/td]')
def rowify(self, cells):
"""encloses rows with the correct tags"""
return str('[tr]' + str(cells) + '[/tr]\r\n')
def __str__(self):
"""adds cell and row tags to elements"""
return(self.rowify(''.join(self.cellify(field) for field in self.elements)))
class tableheaderrow(tablerow):
"""adds a header row"""
def cellify(self, rowfield):
return str('[td]' + bbbold(rowfield) + bbtt(' ') + '[/td]')
class ProduktParser():
def __init__(self, Produktthreads, Produkt = namedtuple('Produkt', 'name id url Stimmen Durchschnitt'), Produkte = [], Anthologien = [], baseurl = baseurl):
"""set base properties: URLs, thread ids, format"""
self.Produkt = Produkt
self.Produkte = Produkte
self.baseurl = baseurl
self.Produktthreads = Produktthreads
self.Anthologien = Anthologien
self.bewertungen = set(
[item for sublist in self.Produktthreads.values() for item in sublist])
self.pool = ThreadPool(concurrent_parses)
self.pool.map(self.getProdukt, self.bewertungen)
self.getAnthologie()
def getProdukt(self, threadid):
"""collect information for selected thread id"""
url = self.baseurl % threadid
page = urllib.request.urlopen(url)
soup = BeautifulSoup(page.read(), "html.parser")
Produktname = soup.find('title').string.split('/')[0].strip()
polls = soup.find('dl', {'class': 'options'})
options = polls.findAll('dt', {'class': 'middletext'})
votes = polls.findAll('span', {'class': 'percentage'})
ergebnis = dict(zip([[int(s) for s in option.string.replace("(","").split() if s.isdigit()][0] for option in options], [int(vote.string.split(' ')[0]) for vote in votes]))
einzelvotes = [
item for sublist in [[k] * v for k, v in ergebnis.items()] for item in sublist]
try:
durchschnitt = str(round(mean(einzelvotes), 2))
stimmen = len(einzelvotes)
except (ZeroDivisionError, StatisticsError) as e:
durchschnitt = '0 / No votes yet'
stimmen = 0
self.Produkte.append(
self.Produkt(Produktname, threadid, url, stimmen, durchschnitt))
def getAnthologie(self):
for Anthologie in self.Anthologien:
Anthologiedurchschnittagg = 0
Anthologiestimmen = 0
for Spielhilfe in self.Produkte:
if Spielhilfe.id in self.Anthologien[Anthologie]:
if Spielhilfe.Durchschnitt != '0 / No votes yet':
Anthologiestimmen += Spielhilfe.Stimmen
Anthologiedurchschnittagg += Spielhilfe.Stimmen * float(Spielhilfe.Durchschnitt)
if Anthologiestimmen == 0:
Anthologiedurchschnitt = '0 / No votes yet'
else:
Anthologiedurchschnitt = str(round(Anthologiedurchschnittagg/Anthologiestimmen, 2))
self.Produkte.append(
self.Produkt(Anthologie, 0, 0, Anthologiestimmen, Anthologiedurchschnitt))
def generateTable(self, bewertungsthreads):
""""generate a table for the threads"""
return bbtable([tableheaderrow(['Platz', 'Bewertung', 'Stimmen', 'Produkt'])]
+ [tablerow([index + 1, element.Durchschnitt, element.Stimmen, bbcodeurl(element.url, element.name)])
for index, element in enumerate(sorted(bewertungsthreads, key=attrgetter('Durchschnitt'), reverse=True))])
def printProdukte(self):
""""print the table"""
for key, value in self.Produktthreads.items():
print('\r\n' + bbbold(key))
print(self.generateTable(
[Spielhilfe for Spielhilfe in self.Produkte if Spielhilfe.id in value]))
print('\r\n' + bbbold("Anthologien"))
print(self.generateTable(
[Spielhilfe for Spielhilfe in self.Produkte if Spielhilfe.name in [Anthologie for Anthologie in Anthologien]]))
if __name__ == '__main__':
TanelornParser = ProduktParser(Produktthreads=Produktthreads, Anthologien=Anthologien)
generate_xlsx(TanelornParser.get_all())
Traceback (most recent call last):
File "D:/RPG/tanelornbewertungen-xls.py", line 219, in <module>
generate_xlsx(TanelornParser.get_all())
AttributeError: 'ProduktParser' object has no attribute 'get_all'
#!/usr/bin/python
# coding=utf-8
import urllib.request
import xlsxwriter
from statistics import StatisticsError, mean
from bs4 import BeautifulSoup
from collections import namedtuple, OrderedDict
from operator import attrgetter
from multiprocessing.dummy import Pool as ThreadPool
# Collection of Thread IDs in several categories
Produktthreads = OrderedDict([
('Spielhilfen', [
100244 ,
99039 ,
98315 ,
103886
]),
('Abenteuer', [
97188 ,
98757 ,
100657 ,
103971
])
])
# maintain anthologies separately
Anthologien = OrderedDict([
('Cthulhu - Ars Mathematica',[
102159, 102160, 102158]),
('Cthulhu - Dreissig',[
101501, 101503, 101504, 101502]),
('Cthulhu - The Final Revelation',[
97284, 97285, 97286, 97287]),
('Cthulhu - Die Goldenen Hände Suc´naaths',[
98758, 98757, 98759]),
('Shadowrun - Licht aus der Asche',[
96028, 96027, 96026])
])
# Add anthologies to collection to avoid duplicates
for Anthologie in Anthologien:
for threadid in Anthologien[Anthologie]:
if threadid not in Produktthreads['Abenteuer']:
Produktthreads['Abenteuer'].append(threadid)
# URL of a thread (%d will be thread_id)
baseurl = "https://www.tanelorn.net/index.php?topic=%d.0"
# Number of parallel threads (should be equal to number of CPU cores)
concurrent_parses = 4
def bbcode(tag, string, value=None):
"""Return a text(string) enclosed by the bbcode tags"""
if value:
return'[' + tag + '=' + value + ']' + string + '[/' + tag + ']'
else:
return'[' + tag + ']' + string + '[/' + tag + ']'
def bbcodeurl(urlstring, urlname):
"""Return an bbcode url format for given url and description"""
return bbcode('url', urlname, urlstring)
def bbbold(text):
"""Return the text with a bbcode bold tag"""
return bbcode(tag='b', string=text)
def bbtt(text):
"""Return the text with a bbcode tt tag"""
return bbcode(tag='tt', string=text)
class bbtable():
"""creates the frame of a bbcode table"""
def __init__(self, rows):
"""needs the rows as input for this table"""
self.elements = rows
def tablify(self, rows):
"""adds start and end tags for tables"""
return str('[table]\r\n' + rows + '[/table]')
def __str__(self):
"""prints table in bbcode format"""
return(self.tablify(''.join(str(row) for row in self.elements)))
class tablerow(bbtable):
"""creates a bbcode table row with correct tags"""
def cellify(self, rowfield):
"""encloses cells with correct tags"""
return str('[td]' + str(rowfield) + '[/td]')
def rowify(self, cells):
"""encloses rows with the correct tags"""
return str('[tr]' + str(cells) + '[/tr]\r\n')
def __str__(self):
"""adds cell and row tags to elements"""
return(self.rowify(''.join(self.cellify(field) for field in self.elements)))
class tableheaderrow(tablerow):
"""adds a header row"""
def cellify(self, rowfield):
return str('[td]' + bbbold(rowfield) + bbtt(' ') + '[/td]')
class ProduktParser():
def __init__(self, Produktthreads, Produkt = namedtuple('Produkt', 'name id url Stimmen Durchschnitt'), Produkte = [], Anthologien = [], baseurl = baseurl):
"""set base properties: URLs, thread ids, format"""
self.produkt_ergebnisse = []
self.anthologie_ergebnisse = []
self.Produkt = Produkt
self.Produkte = Produkte
self.baseurl = baseurl
self.Produktthreads = Produktthreads
self.Anthologien = Anthologien
self.bewertungen = set(
[item for sublist in self.Produktthreads.values() for item in sublist])
self.pool = ThreadPool(concurrent_parses)
self.pool.map(self.getProdukt, self.bewertungen)
self.getAnthologie()
self.calculate_average()
def calculate_average(self):
for produkt in self.produkt_ergebnisse:
produkt.calculate_mean()
def getProdukt(self, threadid):
"""collect information for selected thread id"""
url = self.baseurl % threadid
page = urllib.request.urlopen(url)
soup = BeautifulSoup(page.read(), "html.parser")
Produktname = soup.find('title').string.split('/')[0].strip()
polls = soup.find('dl', {'class': 'options'})
options = polls.findAll('dt', {'class': 'middletext'})
votes = polls.findAll('span', {'class': 'percentage'})
ergebnis = dict(zip([[int(s) for s in option.string.replace("(","").split() if s.isdigit()][0] for option in options], [int(vote.string.split(' ')[0]) for vote in votes]))
einzelvotes = [
item for sublist in [[k] * v for k, v in ergebnis.items()] for item in sublist]
try:
durchschnitt = str(round(mean(einzelvotes), 2))
stimmen = len(einzelvotes)
except (ZeroDivisionError, StatisticsError) as e:
durchschnitt = '0 / No votes yet'
stimmen = 0
self.Produkte.append(
self.Produkt(Produktname, threadid, url, stimmen, durchschnitt))
def getAnthologie(self):
for Anthologie in self.Anthologien:
Anthologiedurchschnittagg = 0
Anthologiestimmen = 0
for Spielhilfe in self.Produkte:
if Spielhilfe.id in self.Anthologien[Anthologie]:
if Spielhilfe.Durchschnitt != '0 / No votes yet':
Anthologiestimmen += Spielhilfe.Stimmen
Anthologiedurchschnittagg += Spielhilfe.Stimmen * float(Spielhilfe.Durchschnitt)
if Anthologiestimmen == 0:
Anthologiedurchschnitt = '0 / No votes yet'
else:
Anthologiedurchschnitt = str(round(Anthologiedurchschnittagg/Anthologiestimmen, 2))
self.Produkte.append(
self.Produkt(Anthologie, 0, 0, Anthologiestimmen, Anthologiedurchschnitt))
def get_produkt_ergebnisse(self, produkt_typ):
result = [produkt for produkt in self.produkt_ergebnisse
if produkt.thread_id in self.Produktthreads[produkt_typ]]
return enumerate(sorted(result))
def get_anthologie_ergebnisse(self):
return enumerate(sorted(self.anthologie_ergebnisse))
def get_all(self):
return enumerate(sorted(self.produkt_ergebnisse))
def generateTable(self, bewertungsthreads):
""""generate a table for the threads"""
return bbtable([tableheaderrow(['Platz', 'Bewertung', 'Stimmen', 'Produkt'])]
+ [tablerow([index + 1, element.Durchschnitt, element.Stimmen, bbcodeurl(element.url, element.name)])
for index, element in enumerate(sorted(bewertungsthreads, key=attrgetter('Durchschnitt'), reverse=True))])
def printProdukte(self):
""""print the table"""
for key, value in self.Produktthreads.items():
print('\r\n' + bbbold(key))
print(self.generateTable(
[Spielhilfe for Spielhilfe in self.Produkte if Spielhilfe.id in value]))
print('\r\n' + bbbold("Anthologien"))
print(self.generateTable(
[Spielhilfe for Spielhilfe in self.Produkte if Spielhilfe.name in [Anthologie for Anthologie in Anthologien]]))
def generate_xlsx(bewertungs_threads):
# Create a workbook and add a worksheet.
workbook = xlsxwriter.Workbook('Bewertungen.xlsx')
worksheet = workbook.add_worksheet()
# Start from the first cell. Rows and columns are zero indexed.
row = 0
col = 0
# Iterate over the data and write it out row by row.
for index, element in bewertungs_threads:
worksheet.write(row, col, index + 1)
worksheet.write(row, col + 1, element.Durchschnitt)
worksheet.write(row, col + 2, element.Stimmen)
worksheet.write(row, col + 3, element.url)
worksheet.write(row, col + 4, element.name)
row += 1
workbook.close()
if __name__ == '__main__':
TanelornParser = ProduktParser(Produktthreads=Produktthreads, Anthologien=Anthologien)
output = 'xlsx'
generate_xlsx(TanelornParser.get_all())
#!/usr/bin/python
# coding=utf-8
import urllib.request
import xlsxwriter
from statistics import StatisticsError, mean
from bs4 import BeautifulSoup
from collections import namedtuple, OrderedDict
from operator import attrgetter
from multiprocessing.dummy import Pool as ThreadPool
# Collection of Thread IDs in several categories
Produktthreads = OrderedDict([
('Spielhilfen', [
100244 ,
99039 ,
98315 ,
103886
]),
('Abenteuer', [
97188 ,
98757 ,
100657 ,
103971
])
])
# maintain anthologies separately
Anthologien = OrderedDict([
('Cthulhu - Ars Mathematica',[
102159, 102160, 102158]),
('Cthulhu - Dreissig',[
101501, 101503, 101504, 101502]),
('Cthulhu - The Final Revelation',[
97284, 97285, 97286, 97287]),
('Cthulhu - Die Goldenen Hände Suc´naaths',[
98758, 98757, 98759]),
('Shadowrun - Licht aus der Asche',[
96028, 96027, 96026])
])
# Add anthologies to collection to avoid duplicates
for Anthologie in Anthologien:
for threadid in Anthologien[Anthologie]:
if threadid not in Produktthreads['Abenteuer']:
Produktthreads['Abenteuer'].append(threadid)
# URL of a thread (%d will be thread_id)
baseurl = "https://www.tanelorn.net/index.php?topic=%d.0"
# Number of parallel threads (should be equal to number of CPU cores)
concurrent_parses = 4
def bbcode(tag, string, value=None):
"""Return a text(string) enclosed by the bbcode tags"""
if value:
return'[' + tag + '=' + value + ']' + string + '[/' + tag + ']'
else:
return'[' + tag + ']' + string + '[/' + tag + ']'
def bbcodeurl(urlstring, urlname):
"""Return an bbcode url format for given url and description"""
return bbcode('url', urlname, urlstring)
def bbbold(text):
"""Return the text with a bbcode bold tag"""
return bbcode(tag='b', string=text)
def bbtt(text):
"""Return the text with a bbcode tt tag"""
return bbcode(tag='tt', string=text)
class bbtable():
"""creates the frame of a bbcode table"""
def __init__(self, rows):
"""needs the rows as input for this table"""
self.elements = rows
def tablify(self, rows):
"""adds start and end tags for tables"""
return str('[table]\r\n' + rows + '[/table]')
def __str__(self):
"""prints table in bbcode format"""
return(self.tablify(''.join(str(row) for row in self.elements)))
class tablerow(bbtable):
"""creates a bbcode table row with correct tags"""
def cellify(self, rowfield):
"""encloses cells with correct tags"""
return str('[td]' + str(rowfield) + '[/td]')
def rowify(self, cells):
"""encloses rows with the correct tags"""
return str('[tr]' + str(cells) + '[/tr]\r\n')
def __str__(self):
"""adds cell and row tags to elements"""
return(self.rowify(''.join(self.cellify(field) for field in self.elements)))
class tableheaderrow(tablerow):
"""adds a header row"""
def cellify(self, rowfield):
return str('[td]' + bbbold(rowfield) + bbtt(' ') + '[/td]')
class ProduktParser():
def __init__(self, Produktthreads, Produkt = namedtuple('Produkt', 'name id url Stimmen Durchschnitt'), Produkte = [], Anthologien = [], baseurl = baseurl):
"""set base properties: URLs, thread ids, format"""
self.produkt_ergebnisse = []
self.anthologie_ergebnisse = []
self.Produkt = Produkt
self.Produkte = Produkte
self.baseurl = baseurl
self.Produktthreads = Produktthreads
self.Anthologien = Anthologien
self.bewertungen = set(
[item for sublist in self.Produktthreads.values() for item in sublist])
self.pool = ThreadPool(concurrent_parses)
self.pool.map(self.getProdukt, self.bewertungen)
self.getAnthologie()
self.calculate_average()
def calculate_average(self):
for produkt in self.produkt_ergebnisse:
produkt.calculate_mean()
def getProdukt(self, threadid):
"""collect information for selected thread id"""
url = self.baseurl % threadid
page = urllib.request.urlopen(url)
soup = BeautifulSoup(page.read(), "html.parser")
Produktname = soup.find('title').string.split('/')[0].strip()
polls = soup.find('dl', {'class': 'options'})
options = polls.findAll('dt', {'class': 'middletext'})
votes = polls.findAll('span', {'class': 'percentage'})
ergebnis = dict(zip([[int(s) for s in option.string.replace("(","").split() if s.isdigit()][0] for option in options], [int(vote.string.split(' ')[0]) for vote in votes]))
einzelvotes = [
item for sublist in [[k] * v for k, v in ergebnis.items()] for item in sublist]
try:
durchschnitt = str(round(mean(einzelvotes), 2))
stimmen = len(einzelvotes)
except (ZeroDivisionError, StatisticsError) as e:
durchschnitt = '0 / No votes yet'
stimmen = 0
self.Produkte.append(
self.Produkt(Produktname, threadid, url, stimmen, durchschnitt))
def getAnthologie(self):
for Anthologie in self.Anthologien:
Anthologiedurchschnittagg = 0
Anthologiestimmen = 0
for Spielhilfe in self.Produkte:
if Spielhilfe.id in self.Anthologien[Anthologie]:
if Spielhilfe.Durchschnitt != '0 / No votes yet':
Anthologiestimmen += Spielhilfe.Stimmen
Anthologiedurchschnittagg += Spielhilfe.Stimmen * float(Spielhilfe.Durchschnitt)
if Anthologiestimmen == 0:
Anthologiedurchschnitt = '0 / No votes yet'
else:
Anthologiedurchschnitt = str(round(Anthologiedurchschnittagg/Anthologiestimmen, 2))
self.Produkte.append(
self.Produkt(Anthologie, 0, 0, Anthologiestimmen, Anthologiedurchschnitt))
def get_produkt_ergebnisse(self, produkt_typ):
result = [produkt for produkt in self.produkt_ergebnisse
if produkt.thread_id in self.Produktthreads[produkt_typ]]
return enumerate(sorted(result))
def get_anthologie_ergebnisse(self):
return enumerate(sorted(self.anthologie_ergebnisse))
def get_all(self):
return enumerate(sorted(self.Produkte))
def generateTable(self, bewertungsthreads):
""""generate a table for the threads"""
return bbtable([tableheaderrow(['Platz', 'Bewertung', 'Stimmen', 'Produkt'])]
+ [tablerow([index + 1, element.Durchschnitt, element.Stimmen, bbcodeurl(element.url, element.name)])
for index, element in enumerate(sorted(bewertungsthreads, key=attrgetter('Durchschnitt'), reverse=True))])
def printProdukte(self):
""""print the table"""
for key, value in self.Produktthreads.items():
print('\r\n' + bbbold(key))
print(self.generateTable(
[Spielhilfe for Spielhilfe in self.Produkte if Spielhilfe.id in value]))
print('\r\n' + bbbold("Anthologien"))
print(self.generateTable(
[Spielhilfe for Spielhilfe in self.Produkte if Spielhilfe.name in [Anthologie for Anthologie in Anthologien]]))
def generate_xlsx(produkte):
# Create a workbook and add a worksheet.
workbook = xlsxwriter.Workbook('Bewertungen.xlsx')
worksheet = workbook.add_worksheet()
# Start from the first cell. Rows and columns are zero indexed.
row = 0
col = 0
# Iterate over the data and write it out row by row.
for index, element in produkte:
worksheet.write(row, col, index + 1)
worksheet.write(row, col + 1, element.Durchschnitt)
worksheet.write(row, col + 2, element.Stimmen)
worksheet.write(row, col + 3, element.url)
worksheet.write(row, col + 4, element.name)
row += 1
workbook.close()
if __name__ == '__main__':
TanelornParser = ProduktParser(Produktthreads=Produktthreads, Anthologien=Anthologien)
generate_xlsx(TanelornParser.get_all())
Ein FAQ wäre noch super, wo vor allem die Bayes-Bewertung erklärt werden könnte.Kann mir denn jemand eine allgemein verständliche Erklärung dafür schreiben?
Im Zeitraum vom 08.06 – 14.09 sind 43 Stimmen bei den Abenteuer-Bewertungen, 32 Stimmen bei den Spielhilfe-Bewertungen und 24 Stimmen bei den Roman-Bewertungen hinzu gekommen.
Nimmst du noch Ruinen und Paläste auf?Ruinen und Paläste ist doch drin!?
Mir ist gerade ein Fehler aufgefallen. Das Abenteuer "Das schwarze Labyrinth" steht hier mit 9 Stimmen und Durchschnittsnote 2,67. Es hat aber 12 Stimmen und wenn man nachrechnet einen viel schlechteren Durchschnitt.
Macht es so langsam Sinn die Abenteuer, Spielhilfen und Romane in ein jeweils eigenes Unterforum zu verschieben? Wollt ihr das überhaupt?Ich fände das übertrieben. Da gibt es anderen Foren mit weit mehr Beiträgen die auch nicht weiter aufgeschlüsselt werden.
Im Zeitraum vom 15.09 – 20.11 sind 29 Stimmen bei den Abenteuer-Bewertungen, 83 Stimmen bei den Spielhilfe-Bewertungen und 24 Stimmen bei den Roman-Bewertungen hinzu gekommen.
Im Zeitraum vom 21.11 - 29.01 sind 42 Stimmen bei den Abenteuer-Bewertungen, 21 Stimmen bei den Spielhilfe-Bewertungen und 19 Stimmen bei den Roman-Bewertungen hinzu gekommen.
Im Zeitraum vom 24.02 – 20.08 sind 119 Stimmen bei den Abenteuer-Bewertungen, 92 Stimmen bei den Spielhilfe-Bewertungen und 33 Stimmen bei den Roman-Bewertungen hinzu gekommen.