Save JSON in DB
This commit is contained in:
@ -20,7 +20,7 @@ class Command(BaseCommand):
|
||||
*args: Variable length argument list.
|
||||
**kwargs: Arbitrary keyword arguments.
|
||||
"""
|
||||
dir_name = Path("json")
|
||||
dir_name = Path("json2")
|
||||
for num, file in enumerate(Path(dir_name).rglob("*.json")):
|
||||
logger.info("Processing %s", file)
|
||||
|
||||
|
@ -255,7 +255,7 @@ class Command(BaseCommand):
|
||||
await browser.close()
|
||||
|
||||
for num, campaign in enumerate(json_data, start=1):
|
||||
await process_json_data(num=num, campaign=campaign, local=True)
|
||||
await process_json_data(num=num, campaign=campaign, local=False)
|
||||
|
||||
return json_data
|
||||
|
||||
|
28
core/migrations/0008_dropcampaign_scraped_json_and_more.py
Normal file
28
core/migrations/0008_dropcampaign_scraped_json_and_more.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.1.1 on 2024-09-21 16:51
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from django.db.migrations.operations.base import Operation
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies: list[tuple[str, str]] = [
|
||||
("core", "0007_alter_game_options_alter_owner_options_and_more"),
|
||||
]
|
||||
|
||||
operations: list[Operation] = [
|
||||
migrations.AddField(
|
||||
model_name="dropcampaign",
|
||||
name="scraped_json",
|
||||
field=models.JSONField(help_text="The JSON data from the Twitch API.", null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="rewardcampaign",
|
||||
name="scraped_json",
|
||||
field=models.JSONField(help_text="The JSON data from the Twitch API.", null=True),
|
||||
),
|
||||
]
|
@ -0,0 +1,50 @@
|
||||
# Generated by Django 5.1.1 on 2024-09-21 23:56
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from django.db.migrations.operations.base import Operation
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies: list[tuple[str, str]] = [
|
||||
("core", "0008_dropcampaign_scraped_json_and_more"),
|
||||
]
|
||||
|
||||
operations: list[Operation] = [
|
||||
migrations.CreateModel(
|
||||
name="ScrapedJson",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("json_data", models.JSONField(help_text="The JSON data from the Twitch API.", unique=True)),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
],
|
||||
options={
|
||||
"ordering": ["-created_at"],
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="dropcampaign",
|
||||
name="scraped_json",
|
||||
field=models.ForeignKey(
|
||||
help_text="Reference to the JSON data from the Twitch API.",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="core.scrapedjson",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="rewardcampaign",
|
||||
name="scraped_json",
|
||||
field=models.ForeignKey(
|
||||
help_text="Reference to the JSON data from the Twitch API.",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="core.scrapedjson",
|
||||
),
|
||||
),
|
||||
]
|
@ -22,6 +22,26 @@ logger: logging.Logger = logging.getLogger(__name__)
|
||||
image_file_format: Literal["webp"] = "webp"
|
||||
|
||||
|
||||
async def update_scraped_json(model_instance: models.Model, new_json_data: dict) -> None:
|
||||
"""Update the JSON data for the drop campaign.
|
||||
|
||||
Args:
|
||||
model_instance (models.Model): The Django model instance which must have a 'scraped_json' attribute.
|
||||
new_json_data (dict): The new JSON data to be updated.
|
||||
"""
|
||||
if await sync_to_async(hasattr)(model_instance, "scraped_json"):
|
||||
if model_instance.scraped_json: # type: ignore[attr-defined]
|
||||
model_instance.scraped_json.json_data = new_json_data # type: ignore[attr-defined]
|
||||
await model_instance.scraped_json.asave() # type: ignore[attr-defined]
|
||||
else:
|
||||
scraped_json_instance: ScrapedJson = await ScrapedJson.objects.acreate(json_data=new_json_data)
|
||||
model_instance.scraped_json = scraped_json_instance # type: ignore[attr-defined]
|
||||
await model_instance.asave()
|
||||
else:
|
||||
logger.error("The model instance does not have a 'scraped_json' attribute.")
|
||||
model_instance.save()
|
||||
|
||||
|
||||
def wrong_typename(data: dict, expected: str) -> bool:
|
||||
"""Check if the data is the expected type.
|
||||
|
||||
@ -170,6 +190,17 @@ def fetch_image(image_url: str) -> bytes | None:
|
||||
class User(AbstractUser): ...
|
||||
|
||||
|
||||
class ScrapedJson(models.Model):
|
||||
json_data = models.JSONField(unique=True, help_text="The JSON data from the Twitch API.")
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
ordering: ClassVar[list[str]] = ["-created_at"]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.pk}"
|
||||
|
||||
|
||||
class Owner(models.Model):
|
||||
"""The company or person that owns the game.
|
||||
|
||||
@ -296,7 +327,7 @@ class Game(models.Model):
|
||||
logger.info("Updated %s fields for %s", updated, self)
|
||||
|
||||
# Handle the owner of the game.
|
||||
if owner:
|
||||
if owner and await sync_to_async(lambda: self not in owner.games.all())(): # type: ignore[attr-defined]
|
||||
await owner.games.aadd(self) # type: ignore # noqa: PGH003
|
||||
await self.asave()
|
||||
logger.info("Added game %s for %s", self, owner)
|
||||
@ -363,6 +394,13 @@ class DropCampaign(models.Model):
|
||||
|
||||
game = models.ForeignKey(Game, on_delete=models.CASCADE, related_name="drop_campaigns", null=True)
|
||||
|
||||
scraped_json = models.ForeignKey(
|
||||
ScrapedJson,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
help_text="Reference to the JSON data from the Twitch API.",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering: ClassVar[list[str]] = ["ends_at"]
|
||||
|
||||
@ -405,6 +443,9 @@ class DropCampaign(models.Model):
|
||||
if wrong_typename(data, "DropCampaign"):
|
||||
return self
|
||||
|
||||
# Save the JSON data for debugging purposes.
|
||||
await update_scraped_json(model_instance=self, new_json_data=data)
|
||||
|
||||
field_mapping: dict[str, str] = {
|
||||
"name": "name",
|
||||
"accountLinkURL": "account_link_url", # TODO(TheLovinator): Should archive site. # noqa: TD003
|
||||
@ -601,7 +642,7 @@ class Benefit(models.Model):
|
||||
if updated > 0:
|
||||
logger.info("Updated %s fields for %s", updated, self)
|
||||
|
||||
if time_based_drop:
|
||||
if time_based_drop and await sync_to_async(lambda: self not in time_based_drop.benefits.all())(): # type: ignore[attr-defined]
|
||||
await time_based_drop.benefits.aadd(self) # type: ignore # noqa: PGH003
|
||||
await time_based_drop.asave()
|
||||
logger.info("Added benefit %s for %s", self, time_based_drop)
|
||||
@ -685,6 +726,13 @@ class RewardCampaign(models.Model):
|
||||
|
||||
game = models.ForeignKey(Game, on_delete=models.CASCADE, related_name="reward_campaigns", null=True)
|
||||
|
||||
scraped_json = models.ForeignKey(
|
||||
ScrapedJson,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
help_text="Reference to the JSON data from the Twitch API.",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering: ClassVar[list[str]] = ["-starts_at"]
|
||||
|
||||
@ -719,6 +767,9 @@ class RewardCampaign(models.Model):
|
||||
if wrong_typename(data, "RewardCampaign"):
|
||||
return self
|
||||
|
||||
# Save the JSON data for debugging purposes.
|
||||
await update_scraped_json(model_instance=self, new_json_data=data)
|
||||
|
||||
field_mapping: dict[str, str] = {
|
||||
"name": "name",
|
||||
"brand": "brand",
|
||||
@ -910,18 +961,18 @@ class Reward(models.Model):
|
||||
logger.info("Updated %s fields for %s", updated, self)
|
||||
|
||||
banner_image_url = data.get("bannerImage", {}).get("image1xURL")
|
||||
if banner_image_url:
|
||||
await sync_to_async(self.download_banner_image)()
|
||||
if banner_image_url != self.banner_image_url:
|
||||
self.banner_image_url = banner_image_url
|
||||
await self.asave()
|
||||
if banner_image_url and banner_image_url != self.banner_image_url:
|
||||
# await sync_to_async(self.download_banner_image)()
|
||||
# TODO(TheLovinator): Download the image. # noqa: TD003
|
||||
self.banner_image_url = banner_image_url
|
||||
await self.asave()
|
||||
|
||||
thumbnail_image_url = data.get("thumbnailImage", {}).get("image1xURL")
|
||||
if thumbnail_image_url:
|
||||
await sync_to_async(self.download_thumbnail_image)()
|
||||
if thumbnail_image_url != self.thumbnail_image_url:
|
||||
self.thumbnail_image_url = thumbnail_image_url
|
||||
await self.asave()
|
||||
if thumbnail_image_url and thumbnail_image_url != self.thumbnail_image_url:
|
||||
# await sync_to_async(self.download_thumbnail_image)()
|
||||
# TODO(TheLovinator): Download the image. # noqa: TD003
|
||||
self.thumbnail_image_url = thumbnail_image_url
|
||||
await self.asave()
|
||||
|
||||
if reward_campaign and await sync_to_async(lambda: reward_campaign != self.campaign)():
|
||||
self.campaign = reward_campaign
|
||||
|
@ -46,6 +46,12 @@
|
||||
<tr>
|
||||
<td><img src="{{ drop_campaign.image_url }}" alt="{{ drop_campaign.name }} image"></td>
|
||||
<td>
|
||||
<p><strong>Created at:</strong>
|
||||
{{ drop_campaign.created_at }}
|
||||
</p>
|
||||
<p><strong>Modified at:</strong>
|
||||
{{ drop_campaign.modified_at }}
|
||||
</p>
|
||||
<p><strong>Status:</strong>
|
||||
{{ drop_campaign.status }}
|
||||
</p>
|
||||
|
Reference in New Issue
Block a user