from __future__ import annotations
import re
from typing import TYPE_CHECKING
from ..boxes import ModDBList, Option, Thumbnail
from ..enums import ThumbnailType
from ..utils import concat_docs, get_date, get_list_stats, join
from .base import BaseMetaClass
if TYPE_CHECKING:
from bs4 import BeautifulSoup
[docs]
class ReviewList(ModDBList):
"""Represents a list of reviews
Attributes
-----------
current_page : int
The page of results this objects represents
total_pages : int
The total amount of result pages available
total_results : int
The total amount of results available
"""
def _parse_method(self, html: BeautifulSoup):
return parse_reviews(html)
def parse_reviews(html):
review_box = html.find("div", class_="normalbox browsebox")
try:
raw_ratings = (
review_box.find("div", class_="inner")
.find("div", class_="table")
.find_all("div", class_=["rowrating"], recursive=False)
)
except AttributeError:
return [], 1, 1, 0
reviews = []
for rating in raw_ratings:
next_sibling = rating.find_next_sibling("div", class_=["rowcontent"])
if next_sibling is not None and "rowcontentnext" in next_sibling["class"]:
reviews.append(Review(rating=rating, text=next_sibling))
else:
reviews.append(Review(rating=rating))
return reviews, *get_list_stats(review_box, 10)
[docs]
@concat_docs
class Review:
"""Represents a review.
Filtering
-----------
rating : int
A value from 1 to 10 denoting the rating number you're looking for
sitearea : Category
The type of model the rating is for (mod, engine, game)
Sorting
--------
* **ratingalt** - rating number, desc is biggest to lowest, asc is lowest to biggest
* **memberipid** - sort reviewer account age, asc is oldest reviewer first
* **positive** - how many people agree with it, desc is most to least people agreeing
* **negative** - how many people disagree with it, desc is most to least people disagreeing
* **id** - when it was added to moddb, asc is oldest, desc is most recent
Attributes
-----------
id : int
The review id
text : str
The contents of the review. Can be none if the member hasn't left any
rating : int
An int out of 10 representing the rating left with this review.
author : Thumbnail
A member like thumbnail of the member who left the review
date : datetime.datetime
Date and time of the review creation
agree : str
Link to agree with the review
disagree : str
Link to disagree with the review
"""
def __init__(self, **attrs):
text = attrs.get("text")
if text:
self.text = text.text
else:
self.text = None
review = attrs.get("rating")
self.rating = int(review.span.string)
# id and hash are none if the review doesn't have content
try:
strings = ("Agree", "Delete", "Disagree")
self.id = int(
re.findall(r"siteareaid=(\d*)", review.find("a", title=strings)["href"])[0]
)
except TypeError:
self.id = None
try:
self._hash = re.findall(r"hash=(.*)&", review.find("a", title="Delete")["href"])[0]
except TypeError:
self._hash = None
author = review.div.a
self.author = Thumbnail(
url=author["href"],
name=author.string.split(" ")[0],
type=ThumbnailType.member,
)
self.date = get_date(review.div.span.time["datetime"])
try:
self.agree = join(review.find("a", title="Agree")["href"])
self.disagree = join(review.find("a", title="Disagree")["href"])
except TypeError:
self.agree = None
self.disagree = None
def __repr__(self):
return f"<Review author={self.author.name} rating={self.rating}>"
[docs]
@concat_docs
class Poll(BaseMetaClass):
"""Represents a poll. Cannot be voted for due to restrictions implemented by the website.
Parameters
-----------
html : bs4.BeautifulSoup
The html to parse. Allows for finer control.
Filtering
----------
month : Month
The month the poll you're looking for should be from
year : int
A int representing a year between 2002 and now. Anything below or above 2002 will
always return zero results.
Sorting
--------
* **totalvotes** - how many people voted on the poll, desc is most to least
* **name** - sort the poll alphabetically by name, asc is a-z
* **date** - when it was added to moddb, asc is oldest, desc is most recent
Attributes
-----------
question : str
The question of the poll
author : Thumbnail
A member like thumbnail of the member who posted the poll, usually ModDB staff
total : int
The total number of votes that have been cast
options : List[Option]
The list of available options for the poll
"""
def __init__(self, html: BeautifulSoup):
poll = html.find("div", class_="poll")
self.question = (
poll.parent.parent.parent.find("div", class_="normalcorner")
.find("span", class_="heading")
.string
)
self.name = self.question
super().__init__(html)
author = poll.find("p", class_="question").find("a")
self.author = Thumbnail(name=author.string, url=author["href"], type=ThumbnailType.member)
self.total = int(
re.search(r"([\d,]*) votes", poll.find("p", class_="question").text)[1].replace(",", "")
)
percentage = poll.find_all("div", class_="barouter")
rest = poll.find_all("p")[1:]
self.options = []
for index, _ in enumerate(percentage):
raw = percentage[index].div.string.replace("%", "").replace("\xa0", "")
percent = float(f"0.{raw}")
text = re.sub(r"\([\d,]* vote(s)?\)", "", rest[index].text)
votes = int(
re.search(r"([\d,]*) vote(s)?", rest[index].span.string)[1].replace(",", "")
)
self.options.append(Option(text=text, votes=votes, percent=percent))
def __repr__(self):
return f"<Poll question={self.question}>"