Compare commits
	
		
			9 Commits
		
	
	
		
			843eed64d6
			...
			37bcab73f0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 37bcab73f0 | |||
| 1a8338c0f8 | |||
| e0dfc0fc3e | |||
| 8cb67ca002 | |||
| be2a01840c | |||
| 612c42ebb7 | |||
| e2255a1c85 | |||
| 0b274b4403 | |||
| ddd75f22b0 | 
| @ -173,6 +173,7 @@ class GameForm(forms.ModelForm): | |||||||
|             "platform", |             "platform", | ||||||
|             "year_released", |             "year_released", | ||||||
|             "status", |             "status", | ||||||
|  |             "mastered", | ||||||
|             "wikidata", |             "wikidata", | ||||||
|         ] |         ] | ||||||
|         widgets = {"name": autofocus_input_widget} |         widgets = {"name": autofocus_input_widget} | ||||||
|  | |||||||
| @ -0,0 +1,59 @@ | |||||||
|  | # Generated by Django 5.1.5 on 2025-03-01 12:52 | ||||||
|  |  | ||||||
|  | import django.db.models.deletion | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ('games', '0005_game_mastered_game_status'), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='game', | ||||||
|  |             name='sort_name', | ||||||
|  |             field=models.CharField(blank=True, default='', max_length=255), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='game', | ||||||
|  |             name='wikidata', | ||||||
|  |             field=models.CharField(blank=True, default='', max_length=50), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='platform', | ||||||
|  |             name='group', | ||||||
|  |             field=models.CharField(blank=True, default='', max_length=255), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='purchase', | ||||||
|  |             name='converted_currency', | ||||||
|  |             field=models.CharField(blank=True, default='', max_length=3), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='purchase', | ||||||
|  |             name='games', | ||||||
|  |             field=models.ManyToManyField(related_name='purchases', to='games.game'), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='purchase', | ||||||
|  |             name='name', | ||||||
|  |             field=models.CharField(blank=True, default='', max_length=255), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='purchase', | ||||||
|  |             name='related_purchase', | ||||||
|  |             field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='related_purchases', to='games.purchase'), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='session', | ||||||
|  |             name='game', | ||||||
|  |             field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sessions', to='games.game'), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='session', | ||||||
|  |             name='note', | ||||||
|  |             field=models.TextField(blank=True, default=''), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										18
									
								
								games/migrations/0007_game_updated_at.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								games/migrations/0007_game_updated_at.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | # Generated by Django 5.1.5 on 2025-03-17 07:36 | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ('games', '0006_alter_game_sort_name_alter_game_wikidata_and_more'), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name='game', | ||||||
|  |             name='updated_at', | ||||||
|  |             field=models.DateTimeField(auto_now=True), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
| @ -14,14 +14,15 @@ class Game(models.Model): | |||||||
|         unique_together = [["name", "platform", "year_released"]] |         unique_together = [["name", "platform", "year_released"]] | ||||||
|  |  | ||||||
|     name = models.CharField(max_length=255) |     name = models.CharField(max_length=255) | ||||||
|     sort_name = models.CharField(max_length=255, null=True, blank=True, default=None) |     sort_name = models.CharField(max_length=255, blank=True, default="") | ||||||
|     year_released = models.IntegerField(null=True, blank=True, default=None) |     year_released = models.IntegerField(null=True, blank=True, default=None) | ||||||
|     wikidata = models.CharField(max_length=50, null=True, blank=True, default=None) |     wikidata = models.CharField(max_length=50, blank=True, default="") | ||||||
|     platform = models.ForeignKey( |     platform = models.ForeignKey( | ||||||
|         "Platform", on_delete=models.SET_DEFAULT, null=True, blank=True, default=None |         "Platform", on_delete=models.SET_DEFAULT, null=True, blank=True, default=None | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     created_at = models.DateTimeField(auto_now_add=True) |     created_at = models.DateTimeField(auto_now_add=True) | ||||||
|  |     updated_at = models.DateTimeField(auto_now=True) | ||||||
|  |  | ||||||
|     class Status(models.TextChoices): |     class Status(models.TextChoices): | ||||||
|         UNPLAYED = ( |         UNPLAYED = ( | ||||||
| @ -68,7 +69,7 @@ def get_sentinel_platform(): | |||||||
|  |  | ||||||
| class Platform(models.Model): | class Platform(models.Model): | ||||||
|     name = models.CharField(max_length=255) |     name = models.CharField(max_length=255) | ||||||
|     group = models.CharField(max_length=255, null=True, blank=True, default=None) |     group = models.CharField(max_length=255, blank=True, default="") | ||||||
|     icon = models.SlugField(blank=True) |     icon = models.SlugField(blank=True) | ||||||
|     created_at = models.DateTimeField(auto_now_add=True) |     created_at = models.DateTimeField(auto_now_add=True) | ||||||
|  |  | ||||||
| @ -127,7 +128,7 @@ class Purchase(models.Model): | |||||||
|  |  | ||||||
|     objects = PurchaseQueryset().as_manager() |     objects = PurchaseQueryset().as_manager() | ||||||
|  |  | ||||||
|     games = models.ManyToManyField(Game, related_name="purchases", blank=True) |     games = models.ManyToManyField(Game, related_name="purchases") | ||||||
|  |  | ||||||
|     platform = models.ForeignKey( |     platform = models.ForeignKey( | ||||||
|         Platform, on_delete=models.CASCADE, default=None, null=True, blank=True |         Platform, on_delete=models.CASCADE, default=None, null=True, blank=True | ||||||
| @ -140,20 +141,19 @@ class Purchase(models.Model): | |||||||
|     price = models.FloatField(default=0) |     price = models.FloatField(default=0) | ||||||
|     price_currency = models.CharField(max_length=3, default="USD") |     price_currency = models.CharField(max_length=3, default="USD") | ||||||
|     converted_price = models.FloatField(null=True) |     converted_price = models.FloatField(null=True) | ||||||
|     converted_currency = models.CharField(max_length=3, null=True) |     converted_currency = models.CharField(max_length=3, blank=True, default="") | ||||||
|     price_per_game = models.FloatField(null=True) |     price_per_game = models.FloatField(null=True) | ||||||
|     num_purchases = models.IntegerField(default=0) |     num_purchases = models.IntegerField(default=0) | ||||||
|     ownership_type = models.CharField( |     ownership_type = models.CharField( | ||||||
|         max_length=2, choices=OWNERSHIP_TYPES, default=DIGITAL |         max_length=2, choices=OWNERSHIP_TYPES, default=DIGITAL | ||||||
|     ) |     ) | ||||||
|     type = models.CharField(max_length=255, choices=TYPES, default=GAME) |     type = models.CharField(max_length=255, choices=TYPES, default=GAME) | ||||||
|     name = models.CharField(max_length=255, default="", null=True, blank=True) |     name = models.CharField(max_length=255, blank=True, default="") | ||||||
|     related_purchase = models.ForeignKey( |     related_purchase = models.ForeignKey( | ||||||
|         "self", |         "self", | ||||||
|         on_delete=models.SET_NULL, |         on_delete=models.SET_NULL, | ||||||
|         default=None, |         default=None, | ||||||
|         null=True, |         null=True, | ||||||
|         blank=True, |  | ||||||
|         related_name="related_purchases", |         related_name="related_purchases", | ||||||
|     ) |     ) | ||||||
|     created_at = models.DateTimeField(auto_now_add=True) |     created_at = models.DateTimeField(auto_now_add=True) | ||||||
| @ -247,7 +247,6 @@ class Session(models.Model): | |||||||
|     game = models.ForeignKey( |     game = models.ForeignKey( | ||||||
|         Game, |         Game, | ||||||
|         on_delete=models.CASCADE, |         on_delete=models.CASCADE, | ||||||
|         blank=True, |  | ||||||
|         null=True, |         null=True, | ||||||
|         default=None, |         default=None, | ||||||
|         related_name="sessions", |         related_name="sessions", | ||||||
| @ -263,7 +262,7 @@ class Session(models.Model): | |||||||
|         blank=True, |         blank=True, | ||||||
|         default=None, |         default=None, | ||||||
|     ) |     ) | ||||||
|     note = models.TextField(blank=True, null=True) |     note = models.TextField(blank=True, default="") | ||||||
|     emulated = models.BooleanField(default=False) |     emulated = models.BooleanField(default=False) | ||||||
|  |  | ||||||
|     created_at = models.DateTimeField(auto_now_add=True) |     created_at = models.DateTimeField(auto_now_add=True) | ||||||
| @ -286,7 +285,7 @@ class Session(models.Model): | |||||||
|         calculated = timedelta(0) |         calculated = timedelta(0) | ||||||
|         if self.is_manual() and isinstance(self.duration_manual, timedelta): |         if self.is_manual() and isinstance(self.duration_manual, timedelta): | ||||||
|             manual = self.duration_manual |             manual = self.duration_manual | ||||||
|         if self.timestamp_end != None and self.timestamp_start != None: |         if self.timestamp_end is not None and self.timestamp_start is not None: | ||||||
|             calculated = self.timestamp_end - self.timestamp_start |             calculated = self.timestamp_end - self.timestamp_start | ||||||
|         return timedelta(seconds=(manual + calculated).total_seconds()) |         return timedelta(seconds=(manual + calculated).total_seconds()) | ||||||
|  |  | ||||||
| @ -302,7 +301,7 @@ class Session(models.Model): | |||||||
|         return Session.objects.all().total_duration_formatted() |         return Session.objects.all().total_duration_formatted() | ||||||
|  |  | ||||||
|     def save(self, *args, **kwargs) -> None: |     def save(self, *args, **kwargs) -> None: | ||||||
|         if self.timestamp_start != None and self.timestamp_end != None: |         if self.timestamp_start is not None and self.timestamp_end is not None: | ||||||
|             self.duration_calculated = self.timestamp_end - self.timestamp_start |             self.duration_calculated = self.timestamp_end - self.timestamp_start | ||||||
|         else: |         else: | ||||||
|             self.duration_calculated = timedelta(0) |             self.duration_calculated = timedelta(0) | ||||||
|  | |||||||
| @ -3,6 +3,9 @@ from django.db.models import ExpressionWrapper, F, FloatField, Q | |||||||
| from django.template.defaultfilters import floatformat | from django.template.defaultfilters import floatformat | ||||||
| from django.utils.timezone import now | from django.utils.timezone import now | ||||||
| from django_q.models import Task | from django_q.models import Task | ||||||
|  | import logging | ||||||
|  |  | ||||||
|  | logger = logging.getLogger("games") | ||||||
|  |  | ||||||
| from games.models import ExchangeRate, Purchase | from games.models import ExchangeRate, Purchase | ||||||
|  |  | ||||||
| @ -12,8 +15,8 @@ currency_to = currency_to.upper() | |||||||
|  |  | ||||||
|  |  | ||||||
| def save_converted_info(purchase, converted_price, converted_currency): | def save_converted_info(purchase, converted_price, converted_currency): | ||||||
|     print( |     logger.info( | ||||||
|         f"Changing converted price of {purchase} to {converted_price} {converted_currency} " |         f"Setting converted price of {purchase} to {converted_price} {converted_currency} (originally {purchase.price} {purchase.price_currency})" | ||||||
|     ) |     ) | ||||||
|     purchase.converted_price = converted_price |     purchase.converted_price = converted_price | ||||||
|     purchase.converted_currency = converted_currency |     purchase.converted_currency = converted_currency | ||||||
| @ -22,8 +25,10 @@ def save_converted_info(purchase, converted_price, converted_currency): | |||||||
|  |  | ||||||
| def convert_prices(): | def convert_prices(): | ||||||
|     purchases = Purchase.objects.filter( |     purchases = Purchase.objects.filter( | ||||||
|         converted_price__isnull=True, converted_currency__isnull=True |         converted_price__isnull=True, converted_currency="" | ||||||
|     ) |     ) | ||||||
|  |     if purchases.count() == 0: | ||||||
|  |         logger.info("[convert_prices]: No prices to convert.") | ||||||
|  |  | ||||||
|     for purchase in purchases: |     for purchase in purchases: | ||||||
|         if purchase.price_currency.upper() == currency_to or purchase.price == 0: |         if purchase.price_currency.upper() == currency_to or purchase.price == 0: | ||||||
| @ -31,13 +36,14 @@ def convert_prices(): | |||||||
|             continue |             continue | ||||||
|         year = purchase.date_purchased.year |         year = purchase.date_purchased.year | ||||||
|         currency_from = purchase.price_currency.upper() |         currency_from = purchase.price_currency.upper() | ||||||
|  |  | ||||||
|         exchange_rate = ExchangeRate.objects.filter( |         exchange_rate = ExchangeRate.objects.filter( | ||||||
|             currency_from=currency_from, currency_to=currency_to, year=year |             currency_from=currency_from, currency_to=currency_to, year=year | ||||||
|         ).first() |         ).first() | ||||||
|  |         logger.info(f"[convert_prices]: Looking for exchange rate in database: {currency_from}->{currency_to}") | ||||||
|         if not exchange_rate: |         if not exchange_rate: | ||||||
|             print( |             logger.info( | ||||||
|                 f"Getting exchange rate from {currency_from} to {currency_to} for {year}..." |                 f"[convert_prices]: Getting exchange rate from {currency_from} to {currency_to} for {year}..." | ||||||
|             ) |             ) | ||||||
|             try: |             try: | ||||||
|                 # this API endpoint only accepts lowercase currency string |                 # this API endpoint only accepts lowercase currency string | ||||||
| @ -50,7 +56,7 @@ def convert_prices(): | |||||||
|                 rate = currency_from_data.get(currency_to.lower()) |                 rate = currency_from_data.get(currency_to.lower()) | ||||||
|  |  | ||||||
|                 if rate: |                 if rate: | ||||||
|                     print(f"Got {rate}, saving...") |                     logger.info(f"[convert_prices]: Got {rate}, saving...") | ||||||
|                     exchange_rate = ExchangeRate.objects.create( |                     exchange_rate = ExchangeRate.objects.create( | ||||||
|                         currency_from=currency_from, |                         currency_from=currency_from, | ||||||
|                         currency_to=currency_to, |                         currency_to=currency_to, | ||||||
| @ -58,10 +64,10 @@ def convert_prices(): | |||||||
|                         rate=floatformat(rate, 2), |                         rate=floatformat(rate, 2), | ||||||
|                     ) |                     ) | ||||||
|                 else: |                 else: | ||||||
|                     print("Could not get an exchange rate.") |                     logger.info("[convert_prices]: Could not get an exchange rate.") | ||||||
|             except requests.RequestException as e: |             except requests.RequestException as e: | ||||||
|                 print( |                 logger.info( | ||||||
|                     f"Failed to fetch exchange rate for {currency_from}->{currency_to} in {year}: {e}" |                     f"[convert_prices]: Failed to fetch exchange rate for {currency_from}->{currency_to} in {year}: {e}" | ||||||
|                 ) |                 ) | ||||||
|         if exchange_rate: |         if exchange_rate: | ||||||
|             save_converted_info( |             save_converted_info( | ||||||
| @ -80,7 +86,7 @@ def calculate_price_per_game(): | |||||||
|     purchases = Purchase.objects.filter(converted_price__isnull=False).filter( |     purchases = Purchase.objects.filter(converted_price__isnull=False).filter( | ||||||
|         Q(updated_at__gte=last_run) | Q(price_per_game__isnull=True) |         Q(updated_at__gte=last_run) | Q(price_per_game__isnull=True) | ||||||
|     ) |     ) | ||||||
|     print(f"Updating {purchases.count()} purchases.") |     logger.info(f"[calculate_price_per_game]: Updating {purchases.count()} purchases.") | ||||||
|     purchases.update( |     purchases.update( | ||||||
|         price_per_game=ExpressionWrapper( |         price_per_game=ExpressionWrapper( | ||||||
|             F("converted_price") / F("num_purchases"), output_field=FloatField() |             F("converted_price") / F("num_purchases"), output_field=FloatField() | ||||||
|  | |||||||
| @ -58,6 +58,7 @@ | |||||||
|                     <c-gamestatus :status="game.status"> |                     <c-gamestatus :status="game.status"> | ||||||
|                         {{ game.get_status_display }} |                         {{ game.get_status_display }} | ||||||
|                     </c-gamestatus> |                     </c-gamestatus> | ||||||
|  |                     {% if game.mastered %}👑{% endif %} | ||||||
|                 </div> |                 </div> | ||||||
|                 <div class="flex gap-2 items-center"> |                 <div class="flex gap-2 items-center"> | ||||||
|                     <span class="uppercase font-bold text-slate-300">Platform</span> |                     <span class="uppercase font-bold text-slate-300">Platform</span> | ||||||
|  | |||||||
| @ -22,10 +22,10 @@ def model_counts(request: HttpRequest) -> dict[str, bool]: | |||||||
|         timestamp_start__day=this_day, |         timestamp_start__day=this_day, | ||||||
|         timestamp_start__month=this_month, |         timestamp_start__month=this_month, | ||||||
|         timestamp_start__year=this_year, |         timestamp_start__year=this_year, | ||||||
|     ).aggregate(time=Sum(F("duration_calculated")))["time"] |     ).aggregate(time=Sum(F("duration_calculated") + F("duration_manual")))["time"] | ||||||
|     last_7_played = Session.objects.filter( |     last_7_played = Session.objects.filter( | ||||||
|         timestamp_start__gte=(now - timedelta(days=7)) |         timestamp_start__gte=(now - timedelta(days=7)) | ||||||
|     ).aggregate(time=Sum(F("duration_calculated")))["time"] |     ).aggregate(time=Sum(F("duration_calculated") + F("duration_manual")))["time"] | ||||||
|  |  | ||||||
|     return { |     return { | ||||||
|         "game_available": Game.objects.exists(), |         "game_available": Game.objects.exists(), | ||||||
|  | |||||||
							
								
								
									
										30
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										30
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							| @ -1,4 +1,4 @@ | |||||||
| # This file is automatically @generated by Poetry 2.0.0 and should not be changed by hand. | # This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "asgiref" | name = "asgiref" | ||||||
| @ -228,14 +228,14 @@ files = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "django" | name = "django" | ||||||
| version = "5.1.5" | version = "5.1.7" | ||||||
| description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." | description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.10" | python-versions = ">=3.10" | ||||||
| groups = ["main", "dev"] | groups = ["main", "dev"] | ||||||
| files = [ | files = [ | ||||||
|     {file = "Django-5.1.5-py3-none-any.whl", hash = "sha256:c46eb936111fffe6ec4bc9930035524a8be98ec2f74d8a0ff351226a3e52f459"}, |     {file = "Django-5.1.7-py3-none-any.whl", hash = "sha256:1323617cb624add820cb9611cdcc788312d250824f92ca6048fda8625514af2b"}, | ||||||
|     {file = "Django-5.1.5.tar.gz", hash = "sha256:19bbca786df50b9eca23cee79d495facf55c8f5c54c529d9bf1fe7b5ea086af3"}, |     {file = "Django-5.1.7.tar.gz", hash = "sha256:30de4ee43a98e5d3da36a9002f287ff400b43ca51791920bfb35f6917bfe041c"}, | ||||||
| ] | ] | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| @ -249,14 +249,14 @@ bcrypt = ["bcrypt"] | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "django-cotton" | name = "django-cotton" | ||||||
| version = "1.3.0" | version = "1.6.0" | ||||||
| description = "Bringing component based design to Django templates." | description = "Bringing component based design to Django templates." | ||||||
| optional = false | optional = false | ||||||
| python-versions = "<4,>=3.8" | python-versions = "<4,>=3.8" | ||||||
| groups = ["main"] | groups = ["main"] | ||||||
| files = [ | files = [ | ||||||
|     {file = "django_cotton-1.3.0-py3-none-any.whl", hash = "sha256:a23f29b759c43423e2f901352c0810388839cc412f6985614153c6ccfcfc2595"}, |     {file = "django_cotton-1.6.0-py3-none-any.whl", hash = "sha256:46452e5fc9ddfff43ac3b10925ba63151e2e9143ffa665a9519178122204b456"}, | ||||||
|     {file = "django_cotton-1.3.0.tar.gz", hash = "sha256:8f4a15dd55c8ee9182cf7234c228ea45d9fcdec1de125221bce8d05af035730a"}, |     {file = "django_cotton-1.6.0.tar.gz", hash = "sha256:1feb2ab486491f304e701fda82f37e608f0b9874473b3ec92922f3891d1a6cd7"}, | ||||||
| ] | ] | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| @ -450,7 +450,7 @@ files = [ | |||||||
| [package.extras] | [package.extras] | ||||||
| docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] | docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] | ||||||
| testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] | testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] | ||||||
| typing = ["typing-extensions (>=4.12.2)"] | typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "graphene" | name = "graphene" | ||||||
| @ -528,14 +528,14 @@ graphql-core = ">=3.2,<3.3" | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "gunicorn" | name = "gunicorn" | ||||||
| version = "22.0.0" | version = "23.0.0" | ||||||
| description = "WSGI HTTP Server for UNIX" | description = "WSGI HTTP Server for UNIX" | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.7" | python-versions = ">=3.7" | ||||||
| groups = ["main"] | groups = ["main"] | ||||||
| files = [ | files = [ | ||||||
|     {file = "gunicorn-22.0.0-py3-none-any.whl", hash = "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9"}, |     {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, | ||||||
|     {file = "gunicorn-22.0.0.tar.gz", hash = "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63"}, |     {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, | ||||||
| ] | ] | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| @ -1167,7 +1167,7 @@ files = [ | |||||||
| ] | ] | ||||||
|  |  | ||||||
| [package.extras] | [package.extras] | ||||||
| brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] | brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] | ||||||
| h2 = ["h2 (>=4,<5)"] | h2 = ["h2 (>=4,<5)"] | ||||||
| socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] | ||||||
| zstd = ["zstandard (>=0.18.0)"] | zstd = ["zstandard (>=0.18.0)"] | ||||||
| @ -1189,7 +1189,7 @@ click = ">=7.0" | |||||||
| h11 = ">=0.8" | h11 = ">=0.8" | ||||||
|  |  | ||||||
| [package.extras] | [package.extras] | ||||||
| standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] | standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "virtualenv" | name = "virtualenv" | ||||||
| @ -1210,9 +1210,9 @@ platformdirs = ">=3.9.1,<5" | |||||||
|  |  | ||||||
| [package.extras] | [package.extras] | ||||||
| docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] | docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] | ||||||
| test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] | test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] | ||||||
|  |  | ||||||
| [metadata] | [metadata] | ||||||
| lock-version = "2.1" | lock-version = "2.1" | ||||||
| python-versions = "^3.11" | python-versions = "^3.11" | ||||||
| content-hash = "b5bb46a6591964aec145637cd9a412a681f2cc5e7e4fdd6fd9ecb0fe8724b8e3" | content-hash = "3a1c1cd04ceca7a53961a487d4e2659c53384d59a5d524e3548b0f0b3c4bbc57" | ||||||
|  | |||||||
| @ -22,7 +22,7 @@ django-debug-toolbar = "^4.4.2" | |||||||
| [tool.poetry.dependencies] | [tool.poetry.dependencies] | ||||||
| python = "^3.11" | python = "^3.11" | ||||||
| django = "^5.0.6" | django = "^5.0.6" | ||||||
| gunicorn = "^22.0.0" | gunicorn = "^23.0.0" | ||||||
| uvicorn = "^0.30.1" | uvicorn = "^0.30.1" | ||||||
| graphene-django = "^3.2.0" | graphene-django = "^3.2.0" | ||||||
| django-htmx = "^1.18.0" | django-htmx = "^1.18.0" | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user