Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d194d227e | |||
| ed217cbad2 | |||
| 3cca30dc70 | |||
| d3a15d5e7b | |||
| deeaa0af4b | |||
| dd71bdd38c | |||
| d066a98282 | |||
| 73bc4a1cd1 |
@ -1,6 +1,4 @@
|
||||
import json
|
||||
import logging
|
||||
import urllib
|
||||
from typing import Optional
|
||||
|
||||
import requests
|
||||
@ -87,7 +85,9 @@ def get_ibu_from_soup(soup) -> Optional[int]:
|
||||
def get_rating_from_soup(soup) -> str:
|
||||
rating = ""
|
||||
try:
|
||||
rating = float(soup.find(class_="num").get_text().strip("(").strip(")"))
|
||||
rating = float(
|
||||
soup.find(class_="num").get_text().strip("(").strip(")")
|
||||
)
|
||||
except AttributeError:
|
||||
rating = None
|
||||
except ValueError:
|
||||
@ -130,7 +130,6 @@ def get_beer_from_untappd_id(untappd_id: str) -> dict:
|
||||
return beer_dict
|
||||
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
beer_dict["untappd_id"] = untappd_id
|
||||
beer_dict["title"] = get_title_from_soup(soup)
|
||||
beer_dict["description"] = get_description_from_soup(soup)
|
||||
beer_dict["styles"] = get_styles_from_soup(soup)
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
# Generated by Django 4.2.19 on 2025-05-11 03:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("puzzles", "0002_alter_puzzle_dimensions_in_inches"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name="puzzle",
|
||||
old_name="igdb_id",
|
||||
new_name="ipdb_id",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="puzzle",
|
||||
name="igdb_image",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="puzzle",
|
||||
name="ipdb_image",
|
||||
field=models.ImageField(
|
||||
blank=True, null=True, upload_to="puzzles/ipdb/"
|
||||
),
|
||||
),
|
||||
]
|
||||
@ -1,12 +1,14 @@
|
||||
from uuid import uuid4
|
||||
|
||||
from puzzles.sources import ipdb
|
||||
import requests
|
||||
from django.apps import apps
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django_extensions.db.models import TimeStampedModel
|
||||
from imagekit.models import ImageSpecField
|
||||
from imagekit.processors import ResizeToFit
|
||||
from puzzles.sources import ipdb
|
||||
from scrobbles.dataclasses import PuzzleLogData
|
||||
from scrobbles.mixins import ScrobblableConstants, ScrobblableMixin
|
||||
|
||||
@ -31,17 +33,17 @@ class Puzzle(ScrobblableMixin):
|
||||
material = models.CharField(max_length=50, **BNULL)
|
||||
cut_style = models.CharField(max_length=100, **BNULL)
|
||||
pieces_count = models.IntegerField(**BNULL)
|
||||
igdb_id = models.CharField(max_length=255, **BNULL)
|
||||
ipdb_id = models.CharField(max_length=255, **BNULL)
|
||||
barcode = models.CharField(max_length=13, **BNULL)
|
||||
igdb_image = models.ImageField(upload_to="puzzles/igdb/", **BNULL)
|
||||
igdb_image_small = ImageSpecField(
|
||||
source="igdb_image",
|
||||
ipdb_image = models.ImageField(upload_to="puzzles/ipdb/", **BNULL)
|
||||
ipdb_image_small = ImageSpecField(
|
||||
source="ipdb_image",
|
||||
processors=[ResizeToFit(100, 100)],
|
||||
format="JPEG",
|
||||
options={"quality": 60},
|
||||
)
|
||||
igdb_image_medium = ImageSpecField(
|
||||
source="igdb_image",
|
||||
ipdb_image_medium = ImageSpecField(
|
||||
source="ipdb_image",
|
||||
processors=[ResizeToFit(300, 300)],
|
||||
format="JPEG",
|
||||
options={"quality": 75},
|
||||
@ -65,10 +67,10 @@ class Puzzle(ScrobblableMixin):
|
||||
return ScrobblableConstants(verb="Solving", tags="puzzle")
|
||||
|
||||
@property
|
||||
def igdb_link(self) -> str:
|
||||
def ipdb_link(self) -> str:
|
||||
link = ""
|
||||
if self.igdb_id:
|
||||
link = f"https://www.ipdb.plus/IPDb/puzzle.php?id={self.igdb_id}"
|
||||
if self.ipdb_id:
|
||||
link = f"https://www.ipdb.plus/IPDb/puzzle.php?id={self.ipdb_id}"
|
||||
return link
|
||||
|
||||
@property
|
||||
@ -88,17 +90,31 @@ class Puzzle(ScrobblableMixin):
|
||||
|
||||
if not puzzle:
|
||||
puzzle_dict = ipdb.get_puzzle_from_ipdb_id(ipdb_id)
|
||||
manufacturer_name = puzzle_dict.pop("manufacturer")
|
||||
manufacturer, _created = PuzzleManufacturer.objects.get_or_create(
|
||||
name=manufacturer_name
|
||||
)
|
||||
ipdb_dict["manufacturer_id"] = manufacturer.id
|
||||
manufacturer_name = puzzle_dict.pop("manufacturer", None)
|
||||
if manufacturer_name:
|
||||
(
|
||||
manufacturer,
|
||||
_created,
|
||||
) = PuzzleManufacturer.objects.get_or_create(
|
||||
name=manufacturer_name
|
||||
)
|
||||
puzzle_dict["manufacturer_id"] = manufacturer.id
|
||||
|
||||
genres = puzzle_dict.pop("genres")
|
||||
genres = puzzle_dict.pop("genres", None)
|
||||
cover_url = puzzle_dict.pop("ipdb_image_url", None)
|
||||
puzzle = Puzzle.objects.create(**puzzle_dict)
|
||||
|
||||
if genres:
|
||||
puzzle.genre.add(*genres)
|
||||
|
||||
if cover_url:
|
||||
r = requests.get(cover_url)
|
||||
if r.status_code == 200:
|
||||
fname = f"{puzzle.title}_{puzzle.uuid}.jpg"
|
||||
puzzle.ipdb_image.save(
|
||||
fname, ContentFile(r.content), save=True
|
||||
)
|
||||
|
||||
return puzzle
|
||||
|
||||
def scrobbles(self, user_id):
|
||||
|
||||
@ -1,5 +1,246 @@
|
||||
#!/usr/bin/env python3
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
IPDB_URL = "https://www.ipdb.plus/IPDb/puzzle.php?id={id}"
|
||||
|
||||
|
||||
def get_first(key: str, result: dict) -> str:
|
||||
obj = ""
|
||||
if obj_list := result.get(key):
|
||||
obj = obj_list[0]
|
||||
return obj
|
||||
|
||||
|
||||
def get_title_from_soup(soup) -> str:
|
||||
title = ""
|
||||
try:
|
||||
title = soup.find("h1").get_text()
|
||||
except AttributeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
return title
|
||||
|
||||
|
||||
def get_manufacturer_from_soup(soup) -> str:
|
||||
manufacturer = ""
|
||||
try:
|
||||
manufacturer = (
|
||||
soup.find(class_="infobox").div.contents[0].split("|")[0].strip()
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
return manufacturer
|
||||
|
||||
|
||||
def get_pieces_count_from_soup(soup) -> int:
|
||||
pieces = 0
|
||||
try:
|
||||
pieces = int(
|
||||
soup.find(class_="infobox")
|
||||
.div.contents[0]
|
||||
.split("|")[1]
|
||||
.split(" ")[1]
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
return pieces
|
||||
|
||||
|
||||
def get_publish_year_from_soup(soup) -> int:
|
||||
year = 1900
|
||||
try:
|
||||
year = int(
|
||||
soup.find(class_="infobox").div.contents[0].split("|")[2].strip()
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
return year
|
||||
|
||||
|
||||
def get_material_from_soup(soup) -> str:
|
||||
material = ""
|
||||
try:
|
||||
material = (
|
||||
soup.findAll(class_="infobox")[1]
|
||||
.findAll("div")[1]
|
||||
.contents[2]
|
||||
.split("|")[-1]
|
||||
.strip()
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
return material
|
||||
|
||||
|
||||
def get_cut_style_from_soup(soup) -> str:
|
||||
cut_style = ""
|
||||
try:
|
||||
cut_style = (
|
||||
soup.findAll(class_="infobox")[1]
|
||||
.findAll("div")[1]
|
||||
.contents[2]
|
||||
.split("|")[-2]
|
||||
.strip()
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
return cut_style
|
||||
|
||||
|
||||
def get_barcode_from_soup(soup) -> str:
|
||||
barcode = ""
|
||||
try:
|
||||
barcode = (
|
||||
soup.findAll(class_="infobox")[2]
|
||||
.findAll("div")[0]
|
||||
.contents[0]
|
||||
.split("|")[-1]
|
||||
.split(":")[-1]
|
||||
.strip()
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
if not barcode:
|
||||
try:
|
||||
barcode = (
|
||||
soup.findAll(class_="infobox")[3]
|
||||
.findAll("div")[0]
|
||||
.contents[0]
|
||||
.split("|")[-1]
|
||||
.split(":")[-1]
|
||||
.strip()
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
return barcode
|
||||
|
||||
|
||||
def get_image_url_from_soup(soup) -> str:
|
||||
url = ""
|
||||
try:
|
||||
url = (
|
||||
soup.find(class_="image-container").contents[0].contents[0]["src"]
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
return url
|
||||
|
||||
|
||||
def get_orientation_from_soup(soup) -> str:
|
||||
orientation = ""
|
||||
try:
|
||||
orientation = (
|
||||
soup.findAll(class_="infobox")[1]
|
||||
.findAll("div")[1]
|
||||
.contents[0]
|
||||
.split("|")[0]
|
||||
.strip()
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
return orientation
|
||||
|
||||
|
||||
def get_dimensions_from_soup(soup) -> str:
|
||||
dimensions = ""
|
||||
try:
|
||||
height = (
|
||||
soup.findAll(class_="infobox")[1]
|
||||
.findAll("div")[1]
|
||||
.contents[1]["data-bs-height"]
|
||||
)
|
||||
width = (
|
||||
soup.findAll(class_="infobox")[1]
|
||||
.findAll("div")[1]
|
||||
.contents[1]["data-bs-width"]
|
||||
)
|
||||
|
||||
dimensions = "x".join([height, width])
|
||||
except AttributeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
return dimensions
|
||||
|
||||
|
||||
def get_rating_from_soup(soup) -> str:
|
||||
rating = ""
|
||||
try:
|
||||
rating = float(
|
||||
soup.find(class_="num").get_text().strip("(").strip(")")
|
||||
)
|
||||
except AttributeError:
|
||||
rating = None
|
||||
except ValueError:
|
||||
rating = None
|
||||
return rating
|
||||
|
||||
|
||||
def get_puzzle_from_ipdb_id(ipdb_id: str) -> dict:
|
||||
return {}
|
||||
puzzle_url = IPDB_URL.format(id=ipdb_id)
|
||||
headers = {"User-Agent": "Vrobbler 0.11.12"}
|
||||
response = requests.get(puzzle_url, headers=headers)
|
||||
puzzle_dict = {"ipdb_id": ipdb_id}
|
||||
|
||||
if response.status_code != 200:
|
||||
logger.warn(
|
||||
"Bad response from untappd.com", extra={"response": response}
|
||||
)
|
||||
return puzzle_dict
|
||||
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
puzzle_dict["title"] = get_title_from_soup(soup)
|
||||
puzzle_dict["orientation"] = get_orientation_from_soup(soup)
|
||||
puzzle_dict["manufacturer"] = get_manufacturer_from_soup(soup)
|
||||
puzzle_dict["pieces_count"] = get_pieces_count_from_soup(soup)
|
||||
puzzle_dict["ipdb_image_url"] = get_image_url_from_soup(soup)
|
||||
puzzle_dict["dimensions_in_inches"] = get_dimensions_from_soup(soup)
|
||||
puzzle_dict["publish_year"] = get_publish_year_from_soup(soup)
|
||||
puzzle_dict["material"] = get_material_from_soup(soup)
|
||||
puzzle_dict["cut_style"] = get_cut_style_from_soup(soup)
|
||||
puzzle_dict["barcode"] = get_barcode_from_soup(soup)
|
||||
return puzzle_dict
|
||||
|
||||
Reference in New Issue
Block a user